raw Software

PHP-Facedetect is a small native PHP extension that exposes OpenCV's cascade classifier through two functions. It was created when invoking computer-vision code directly from PHP usually meant writing an extension or maintaining a separate service. The wrapper keeps that boundary deliberately narrow: give it an image and a trained cascade, then receive either the number of detections or their bounding rectangles.

The project performs face detection, not face recognition. It locates regions that resemble the objects represented by a cascade; it does not identify a person, compare identities, estimate attributes, or determine whether a detected face belongs to a real person.

Project Status

The source remains available under the BSD 3-Clause license in the PHP-Facedetect repository. The current source reports version 0.1.0, detects OpenCV 3 and OpenCV 4 through pkg-config, contains compatibility work for PHP 7, PHP 8, and PHP 8.1, and last received a repository update in 2022.

The old instruction pecl install facedetect is no longer valid: there is no current Facedetect package in the PECL registry, and PECL itself is deprecated in favor of PIE. This repository does not currently contain PIE package metadata or an automated compatibility matrix. Treat it as a historical native extension that may require maintenance for a current PHP, compiler, and OpenCV combination. Build and test it in the same container or operating system image used in production.

As a point-in-time verification for this migration, the current master branch compiled, linked, and loaded successfully with PHP 8.4.1 and OpenCV 4.12.0 on arm64 macOS. Reflection reported the documented PHP 8 return types. An end-to-end smoke test also decoded a JPEG with OpenCV's official frontal-face cascade and returned a bounding box. This result validates that exact environment; it is not a compatibility promise for other versions or platforms.

API

Both functions take a readable image pathname and an optional cascade pathname:

face_detect(string $imagePath, ?string $cascadePath = null): array|false
face_count(string $imagePath, ?string $cascadePath = null): int|false

face_detect() returns one associative array per detected region. Coordinates are pixel values measured from the image's top-left corner:

<?php

$cascade = '/opt/opencv-data/haarcascades/haarcascade_frontalface_default.xml';
$image = '/srv/facedetect/input/group.jpg';

$faces = face_detect($image, $cascade);

if ($faces === false) {
    throw new RuntimeException('The image or cascade could not be processed.');
}

foreach ($faces as $face) {
    printf(
        "x=%d y=%d width=%d height=%d\n",
        $face['x'],
        $face['y'],
        $face['w'],
        $face['h'],
    );
}

A successful call with no detections returns an empty array. A successful face_count() call returns zero in that case:

<?php

$count = face_count($image, $cascade);

if ($count === false) {
    throw new RuntimeException('The image or cascade could not be processed.');
}

printf("Detected faces: %d\n", $count);

The count function runs the same detection pipeline; it is not cheaper in computer-vision work. It only avoids constructing and returning the rectangle arrays.

How Detection Works

The implementation reads the image with OpenCV, converts it from BGR to grayscale, equalizes the grayscale histogram, and calls CascadeClassifier::detectMultiScale(). Its fixed parameters are a scale factor of 1.1, minNeighbors = 2, and a minimum detection size of 30 by 30 pixels.

The cascade XML file is therefore part of the model, not a generic configuration detail. A frontal-face cascade, a profile-face cascade, and a body cascade solve different detection tasks and produce different errors. OpenCV ships pretrained Haar cascades in its source data directory, while operating-system packages install them in distribution-specific locations.

A historical comparison of frontal, profile, and body cascades on the same image illustrates how model choice changes the selected region and can create false positives.

A cascade can also be loaded once through the extension setting:

; php.ini or a dedicated conf.d/facedetect.ini file
extension=facedetect.so
facedetect.cascade=/opt/opencv-data/haarcascades/haarcascade_frontalface_default.xml

After that, calls may omit the second argument:

<?php

$faces = face_detect('/srv/facedetect/input/group.jpg');

If neither the call nor facedetect.cascade supplies a successfully loaded classifier, the function emits a warning and returns false. Passing a cascade path loads that classifier into the extension's shared classifier object and affects later calls in the same PHP process. Prefer one fixed startup configuration in a long-running PHP-FPM worker rather than switching models per request.

Build from Source

A source build requires a C++ compiler, PHP development headers that match the target PHP runtime, phpize, php-config, pkg-config, and OpenCV development files. The build script accepts either an opencv.pc or opencv4.pc package with version 3.0.0 or newer.

git clone https://github.com/infusion/PHP-Facedetect.git
cd PHP-Facedetect

phpize
./configure --with-facedetect --with-php-config="$(command -v php-config)"
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)"
make test
sudo make install

The repository currently has no bundled test suite, so make test may report that no tests exist. It is still worth running because downstream packaging may add tests. Do not run sudo for cloning, configuring, or compiling; installation is the only step that may need elevated privileges.

Enable the module for the correct Server API:

printf '%s\n' 'extension=facedetect.so' | \
  sudo tee "$(php --ini | sed -n 's|Scan for additional .ini files in: ||p')/facedetect.ini"

php --ri facedetect

The command above assumes that the CLI scans a writable configuration directory and that it matches the target runtime. PHP CLI and PHP-FPM often load different configuration trees. Verify both with php --ini and the FPM package's own configuration before restarting the service. A package-managed conf.d file is preferable to editing the main php.ini.

Verify the Build

Before processing real images, verify module loading, both public functions, and OpenCV linkage:

php --ri facedetect
php -r 'var_dump(extension_loaded("facedetect"));'
php -r 'var_dump(function_exists("face_detect"), function_exists("face_count"));'

php -r '
$faces = face_detect($argv[1], $argv[2]);
if ($faces === false) {
    exit(1);
}
echo json_encode($faces, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), PHP_EOL;
' /srv/facedetect/test.jpg /opt/opencv-data/haarcascades/haarcascade_frontalface_default.xml

Keep the image and expected cascade in a deployment smoke test. Loading the module proves ABI compatibility only far enough for startup; decoding an image and executing the classifier exercises the OpenCV path that the application actually needs.

Operational and Security Limits

For new systems, a separate computer-vision service or a maintained binding is usually easier to upgrade and isolate than a custom module loaded into every PHP worker. The extension remains useful where its tiny API, low call overhead, and historical compatibility are specifically valuable.

References