Face detection answers two questions: how many face-like regions are present, and where are their bounding boxes? It is face detection, not face recognition. A detector does not identify a person, compare identities, or establish that a region contains a real human rather than a face-like pattern.
The native PHP-Facedetect extension exposes OpenCV's cascade classifier through face_detect() and face_count(). This 2008 workflow is preserved as a practical account of using those results, choosing a cascade, and understanding why different trained classifiers produce different boxes on the same image.
Bounding Boxes from PHP
face_detect() accepts an image pathname and an optional cascade XML pathname. On success it returns one array per detection:
[
['x' => 142, 'y' => 331, 'w' => 118, 'h' => 118],
] The x and y coordinates locate the top-left corner of the rectangle. Width and height are measured in pixels in the original image. An empty array means that processing succeeded but no candidate passed the classifier. false means the image or cascade could not be processed.
The following CLI example accepts only a local JPEG below /srv/facedetect/input, runs the classifier, clamps every returned rectangle to the decoded image, and writes an annotated copy with PHP's GD extension:
<?php
$inputRoot = realpath('/srv/facedetect/input');
$outputRoot = realpath('/srv/facedetect/output');
$input = realpath($argv[1] ?? '');
$cascade = realpath($argv[2] ?? '');
if ($inputRoot === false || $outputRoot === false) {
throw new RuntimeException('The input and output directories must exist.');
}
if ($input === false || $cascade === false) {
throw new InvalidArgumentException('Pass an existing image and cascade.');
}
$inputPrefix = $inputRoot . DIRECTORY_SEPARATOR;
if (!str_starts_with($input, $inputPrefix)) {
throw new InvalidArgumentException('The image is outside the input directory.');
}
$imageInfo = getimagesize($input);
if ($imageInfo === false || $imageInfo[2] !== IMAGETYPE_JPEG) {
throw new InvalidArgumentException('The input must be a decodable JPEG.');
}
$faces = face_detect($input, $cascade);
if ($faces === false) {
throw new RuntimeException('OpenCV could not process the image or cascade.');
}
$image = imagecreatefromjpeg($input);
if ($image === false) {
throw new RuntimeException('GD could not decode the JPEG.');
}
$red = imagecolorallocate($image, 220, 30, 30);
$imageWidth = imagesx($image);
$imageHeight = imagesy($image);
foreach ($faces as $face) {
if (!isset($face['x'], $face['y'], $face['w'], $face['h'])) {
continue;
}
$width = (int) $face['w'];
$height = (int) $face['h'];
if ($width <= 0 || $height <= 0) {
continue;
}
$left = max(0, (int) $face['x']);
$top = max(0, (int) $face['y']);
$right = min($imageWidth - 1, (int) $face['x'] + $width - 1);
$bottom = min($imageHeight - 1, (int) $face['y'] + $height - 1);
if ($left <= $right && $top <= $bottom) {
imagerectangle($image, $left, $top, $right, $bottom, $red);
}
}
$output = $outputRoot . DIRECTORY_SEPARATOR
. pathinfo($input, PATHINFO_FILENAME) . '-detected.jpg';
if (!imagejpeg($image, $output, 90)) {
throw new RuntimeException('Unable to write the annotated JPEG.');
}
imagedestroy($image);
printf("Detected %d face candidates; wrote %s\n", count($faces), $output); Do not pass a request parameter directly to either filesystem function. Uploads need byte, pixel, format, and decode limits before native image processing. In a web application, queue this work outside the request process and publish only a derived result with server-owned filenames.
What the Cascade Controls
A Haar cascade is a trained detector for one object class and view. OpenCV scans an image pyramid, rejects most windows with inexpensive rectangular features, and retains windows that pass every cascade stage. The extension converts the image to grayscale, equalizes its histogram, and calls detectMultiScale() with a scale factor of 1.1, minNeighbors set to 2, and a minimum window of 30 by 30 pixels.
The scale factor determines how finely the image pyramid is sampled. A value closer to one examines more scales and costs more CPU. minNeighbors controls how much overlapping evidence is required around a candidate: raising it often suppresses false positives but can also remove weak true detections. PHP-Facedetect fixes these values in its native implementation, so changing them requires changing the extension or moving detection to a service with a wider API.
Historical Cascade Comparison
The following 2008 results run different OpenCV cascade files over the same contact sheet. The red circles were the original visualization of rectangular detections; their centers and radii came from the returned x, y, w, and h values.
haarcascade_frontalface_alt2.xml
haarcascade_frontalface_alt_tree.xml
haarcascade_frontalface_alt.xml
haarcascade_frontalface_default.xml
haarcascade_profileface.xml
haarcascade_lowerbody.xml
haarcascade_upperbody.xml
Combining counts from several cascades does not automatically improve accuracy. Their boxes can overlap, represent different object classes, or detect the same face more than once. A multi-model system needs class-specific evaluation, confidence calibration, and a defined box-merging rule such as non-maximum suppression.
Evaluate the Detector
The contact sheet demonstrates why one successful image is not a benchmark. Build a labeled test set representing the actual camera, image size, lighting, pose, occlusion, and population. Match predicted and reference boxes with a stated intersection-over-union threshold, then report precision and recall. Count false positives and false negatives separately; one number cannot describe both errors.
Haar cascades remain compact and inexpensive on a CPU, but they are sensitive to pose and imaging conditions. For unconstrained photographs or higher accuracy requirements, a maintained DNN detector with documented training and evaluation data is usually a better choice. Measure end-to-end latency and accuracy on the target hardware rather than selecting a model by age or popularity.
Face processing also raises privacy, consent, retention, and access-control obligations even when no identity is inferred. Store the minimum data required, define deletion periods, protect originals and derived boxes, and do not silently repurpose detections for recognition or sensitive-trait inference.
References
- [OpenCV]OpenCV project. Cascade Classifier tutorial.
- [ViolaJones2001]Paul Viola and Michael Jones, Rapid Object Detection using a Boosted Cascade of Simple Features, CVPR 2001.
- [ViolaJones2004]Paul Viola and Michael J. Jones, “Robust Real-Time Face Detection,” International Journal of Computer Vision, 57(2), 2004, pp. 137–154.