BackendGuy
Face detection in PHP

[Machine learning] Face detection in PHP

Face detection in PHP

Please download the Detection.dat file here before you proceed. Please note that this file is in text format, kindly copy all the code and save as detection.datdat, if not the code won’t run.

Face detection in PHP code is completed written in PHP it cannot attain the raw speed that C,C++ provides, but for many trivial application a PHP version can do.

The sample code to show the detected face is show below. Note that in images having multiple faces only the first one is detected. Although this might seem like a limitation, this can be useful in web applications that need to create a user profile picture from a raw image. So that if a user uploads a full length picture of him/her, the given code can detect the face and automatically create a profile picture for the user.

Firstly, the facedetector.php file should be created as a parent class from which others can inherit from

Facedetector.php
<?php
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.



class Face_Detector {

protected $detection_data;
protected $canvas;
protected $face;
private $reduced_canvas;

public function __construct($detection_file = 'detection.dat') {
if (is_file($detection_file)) {
$this->detection_data = unserialize(file_get_contents($detection_file));
} else {
throw new Exception("Couldn't load detection data");
}
}

public function face_detect($file) {
if (!is_file($file)) {
throw new Exception("Can not load $file");
}

$this->canvas = imagecreatefromjpeg($file);
$im_width = imagesx($this->canvas);
$im_height = imagesy($this->canvas);

//Resample before detection?
$ratio = 0;
$diff_width = 320 - $im_width;
$diff_height = 240 - $im_height;
if ($diff_width > $diff_height) {
$ratio = $im_width / 320;
} else {
$ratio = $im_height / 240;
}

if ($ratio != 0) {
$this->reduced_canvas = imagecreatetruecolor($im_width / $ratio, $im_height / $ratio);
imagecopyresampled($this->reduced_canvas, $this->canvas, 0, 0, 0, 0, $im_width / $ratio, $im_height / $ratio, $im_width, $im_height);

$stats = $this->get_img_stats($this->reduced_canvas);
$this->face = $this->do_detect_greedy_big_to_small($stats['ii'], $stats['ii2'], $stats['width'], $stats['height']);
$this->face['x'] *= $ratio;
$this->face['y'] *= $ratio;
$this->face['w'] *= $ratio;
} else {
$stats = $this->get_img_stats($this->canvas);
$this->face = $this->do_detect_greedy_big_to_small($stats['ii'], $stats['ii2'], $stats['width'], $stats['height']);
}
return ($this->face['w'] > 0);
}


public function toJpeg() {
$color = imagecolorallocate($this->canvas, 255, 0, 0); //red
imagerectangle($this->canvas, $this->face['x'], $this->face['y'], $this->face['x']+$this->face['w'], $this->face['y']+ $this->face['w'], $color);
header('Content-type: image/jpeg');
imagejpeg($this->canvas);
}

/* Added by codediesel */
public function cropFace() {
$canvas = imagecreatetruecolor($this->face['w'], $this->face['w']);
imagecopy($canvas, $this->canvas, 0, 0, $this->face['x'], $this->face['x'], $this->face['w'], $this->face['w']);
header('Content-type: image/jpeg');
imagejpeg($canvas);
}

public function toJson() {
return "{'x':" . $this->face['x'] . ", 'y':" . $this->face['y'] . ", 'w':" . $this->face['w'] . "}";
}

public function getFace() {
return $this->face;
}

protected function get_img_stats($canvas){
$image_width = imagesx($canvas);
$image_height = imagesy($canvas); 
$iis = $this->compute_ii($canvas, $image_width, $image_height);
return array(
'width' => $image_width,
'height' => $image_height,
'ii' => $iis['ii'],
'ii2' => $iis['ii2']
); 
}

protected function compute_ii($canvas, $image_width, $image_height ){
$ii_w = $image_width+1;
$ii_h = $image_height+1;
$ii = array();
$ii2 = array(); 

for($i=0; $i<$ii_w; $i++ ){
$ii[$i] = 0;
$ii2[$i] = 0;
} 

for($i=1; $i<$ii_w; $i++ ){ 
$ii[$i*$ii_w] = 0; 
$ii2[$i*$ii_w] = 0; 
$rowsum = 0;
$rowsum2 = 0;
for($j=1; $j<$ii_h; $j++ ){
$rgb = ImageColorAt($canvas, $j, $i);
$red = ($rgb >> 16) & 0xFF;
$green = ($rgb >> 8) & 0xFF;
$blue = $rgb & 0xFF;
$grey = ( 0.2989*$red + 0.587*$green + 0.114*$blue )>>0; // this is what matlab uses
$rowsum += $grey;
$rowsum2 += $grey*$grey;

$ii_above = ($i-1)*$ii_w + $j;
$ii_this = $i*$ii_w + $j;

$ii[$ii_this] = $ii[$ii_above] + $rowsum;
$ii2[$ii_this] = $ii2[$ii_above] + $rowsum2;
}
}
return array('ii'=>$ii, 'ii2' => $ii2);
}

protected function do_detect_greedy_big_to_small( $ii, $ii2, $width, $height ){
$s_w = $width/20.0;
$s_h = $height/20.0;
$start_scale = $s_h < $s_w ? $s_h : $s_w;
$scale_update = 1 / 1.2;
for($scale = $start_scale; $scale > 1; $scale *= $scale_update ){
$w = (20*$scale) >> 0;
$endx = $width - $w - 1;
$endy = $height - $w - 1;
$step = max( $scale, 2 ) >> 0;
$inv_area = 1 / ($w*$w);
for($y = 0; $y < $endy ; $y += $step ){
for($x = 0; $x < $endx ; $x += $step ){
$passed = $this->detect_on_sub_image( $x, $y, $scale, $ii, $ii2, $w, $width+1, $inv_area);
if( $passed ) {
return array('x'=>$x, 'y'=>$y, 'w'=>$w);
}
} // end x
} // end y
} // end scale
return null;
}

protected function detect_on_sub_image( $x, $y, $scale, $ii, $ii2, $w, $iiw, $inv_area){
$mean = ( $ii[($y+$w)*$iiw + $x + $w] + $ii[$y*$iiw+$x] - $ii[($y+$w)*$iiw+$x] - $ii[$y*$iiw+$x+$w] )*$inv_area;
$vnorm = ( $ii2[($y+$w)*$iiw + $x + $w] + $ii2[$y*$iiw+$x] - $ii2[($y+$w)*$iiw+$x] - $ii2[$y*$iiw+$x+$w] )*$inv_area - ($mean*$mean); 
$vnorm = $vnorm > 1 ? sqrt($vnorm) : 1;

$passed = true;
for($i_stage = 0; $i_stage < count($this->detection_data); $i_stage++ ){
$stage = $this->detection_data[$i_stage]; 
$trees = $stage[0];

$stage_thresh = $stage[1];
$stage_sum = 0;

for($i_tree = 0; $i_tree < count($trees); $i_tree++ ){
$tree = $trees[$i_tree];
$current_node = $tree[0]; 
$tree_sum = 0;
while( $current_node != null ){
$vals = $current_node[0];
$node_thresh = $vals[0];
$leftval = $vals[1];
$rightval = $vals[2];
$leftidx = $vals[3];
$rightidx = $vals[4];
$rects = $current_node[1];

$rect_sum = 0;
for( $i_rect = 0; $i_rect < count($rects); $i_rect++ ){
$s = $scale;
$rect = $rects[$i_rect];
$rx = ($rect[0]*$s+$x)>>0;
$ry = ($rect[1]*$s+$y)>>0;
$rw = ($rect[2]*$s)>>0; 
$rh = ($rect[3]*$s)>>0;
$wt = $rect[4];

$r_sum = ( $ii[($ry+$rh)*$iiw + $rx + $rw] + $ii[$ry*$iiw+$rx] - $ii[($ry+$rh)*$iiw+$rx] - $ii[$ry*$iiw+$rx+$rw] )*$wt;
$rect_sum += $r_sum;
} 

$rect_sum *= $inv_area;

$current_node = null;
if( $rect_sum >= $node_thresh*$vnorm ){
if( $rightidx == -1 ) 
$tree_sum = $rightval;
else
$current_node = $tree[$rightidx];
} else {
if( $leftidx == -1 )
$tree_sum = $leftval;
else
$current_node = $tree[$leftidx];
}
} 
$stage_sum += $tree_sum;
} 
if( $stage_sum < $stage_thresh ){
return false;
}
} 
return true;
}
}

Then afterwards create another file which you can call example.php
<?php
 
include "FaceDetector.php";
 
$face_detect = new Face_Detector('detection.dat');
$face_detect->face_detect('sample-image3.jpg');
$face_detect->toJpeg();
 
?>


Flickr :By modelxing

The above code displays a border around the detected face, instead we can directly crop the resulting face area and output the resulting image.

<?php
 
include "FaceDetector.php";
 
$face_detect = new Face_Detector('detection.dat');
$face_detect->face_detect('sample-image3.jpg');
$face_detect->cropFace();
 
?>

Face detection in PHP (Face coordinates)

If you need the coordinates of the detected face for further processing, you can use the following.

// Return coordinates in JSON
// {'x':56.375, 'y':45.1, 'w':227.55}
$face_detect->toJson();
 
// OR
 
// Return coordinates as an Array
$face_detect->getFace();

Extending the class

You can extend the Face_Detector class using your own methods. For example I’ve added three methods – Rotate(), resizeFace() and toGrayScale() to the class, which lets you modify the final face area further.

<?php
 
include "FaceDetector.php";
 
/* We now extend the above class so we can add our own methods */
class FaceModify extends Face_Detector {
 
  public function Rotate() {
    $canvas = imagecreatetruecolor($this->face['w'], $this->face['w']);
    imagecopy($canvas, $this->canvas, 0, 0, $this->face['x'], 
              $this->face['x'], $this->face['w'], $this->face['w']);
    $canvas = imagerotate($canvas, 180, 0);
    $this->_outImage($canvas);
  }
 
  public function toGrayScale() {
    $canvas = imagecreatetruecolor($this->face['w'], $this->face['w']);
    imagecopy($canvas, $this->canvas, 0, 0, $this->face['x'], 
              $this->face['x'], $this->face['w'], $this->face['w']);
    imagefilter ($canvas, IMG_FILTER_GRAYSCALE);
    $this->_outImage($canvas);
  }
 
  public function resizeFace($width, $height) {
    $canvas = imagecreatetruecolor($width, $width);
    imagecopyresized($canvas, $this->canvas, 0, 0, $this->face['x'], 
                     $this->face['y'], $width, $height, 
                     $this->face['w'], $this->face['w']);
    $this->_outImage($canvas);
  }
 
  private function _outImage($canvas) {
    header('Content-type: image/jpeg');
    imagejpeg($canvas);
  }
}
 
 
/* Using the extended class */
$face_detect = new FaceModify('detection.dat');
$face_detect->face_detect('sample-image3.jpg');
$face_detect->resizeFace(100,100);
 
?>

NB: If for some reason the code returns a error, turn off error reporting in php.ini

BackendGuy

5 comments

  • it gave me this error “Fatal error: Uncaught Exception: Couldn’t load detection data in D:\AppServ\www\Facedetector.php:26 Stack trace: #0 D:\AppServ\www\example.php(39): Face_Detector->__construct(‘detection.dat’) #1 {main} thrown in D:\AppServ\www\Facedetector.php on line 26

    • That was because the detection.DAT file was not available, kindly click the link before the post, and copy all the code , and save in .dat format

    • Thanks for the call out, the post has been updated, kindly copy and save the file as .DAT , because it is in a txt format

Your Header Sidebar area is currently empty. Hurry up and add some widgets.