Detector de Rostros en PHP

Un código interesante para reconocimiento facial en fotografias, la verdad todo una maravilla,  yo lo he probado como puedes ver en la imagen de abajo y funciona a las mil maravillas, ademas de 1 clase necesitas un archivo .dat, necesitas las extensión GD2 activada en PHP.

test.php

<?php
include 'Face_Detector.php';
$detector = new Face_Detector('detection.dat');
$detector->face_detect('m(01-32)_gr.jpg');
$detector->toJpeg();
?>

<?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.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// @Author Karthik Tharavaad
//         karthik_tharavaad@yahoo.com
// @Contributor Maurice Svay
//              maurice@svay.Com
 
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");
        }
        //$this->detection_data = json_decode(file_get_contents('data.js'));
    }
 
    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);
    }
 
    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;
    }
}

Tags: ,

3 Responses to “Detector de Rostros en PHP”


  1. TRX
    on Jun 26th, 2009
    @ 11:50

    Hola!
    sería muy interesante que alguien explicara un poco el código por encima. Sobre todo a mi me interesaría saber qué hay en el archivo .dat que hace que sea capaz de detectar caras. Estoy seguro que simplemente cambiando el .dat se podría hacer que se detectaran otro tipo de objetos, de ahí mi interés en entender cómo funciona!
    Un saludo!


  2. Jota
    on Jun 26th, 2009
    @ 16:11

    Aqui hay info adicional…

    http://svay.com/blog/index/post/2009/06/19/Face-detection-in-pure-PHP-(without-OpenCV)


  3. Alex Sandoval
    on May 31st, 2010
    @ 0:34

    @TRX: En resumen. El código visible es una implementación y ejecución de una red neuronal artificial (RNA). El archivo .dat contiene un array de resultados de un entrenamiento. Cada valor contenido en ese archivo es el resultado de una o varias ecuaciones encargadas de ajustar (aprender) la sinapsis que conectan a cada una de las neuronas con la finalidad de detectar rostros. El entrenamiento de la red neuronal, genera esté archivo de datos, y para entrenarla el proceso es más o menos así: 1. debes tener 2 muestras, miles de fotografías con rostros y miles de fotografías sin rostros. Una red neuronal artificial, como la red neuronal humana, aprende por experiencia y entrenamiento. El proceso de entrenamiento consiste en diferenciar el objetivo con lo “otro”, por eso es necesario fotos con caras y sin caras. 2. después de tener las fotografías, necesitas normalizarlas, es decir, ajustas los tamaños a un ratio estandar para todas. 3. crear variaciones de las fotografias, ajustando los colores, iluminación, etc. 4. Conseguir un programa de entrenamiento de redes neuronales artificiales que te permita diseñar y entrenar. 5. Iniciar el entrenamiento, esté puede tardar mucho tiempo, dependiendo de la potencia de procesamiento que tengas, pero puede ser incluso días all-time.

    Esta técnica la puedes emplear para cualquier cosa, en realidad se puede aplicar a todo lo que pueda ser expresado matemáticamente (todo!). Por ejemplo, detectar captcha, detectar la voz humana de una persona, etc,etc,etc. El uso de RNAs es muy comun en sub aplicaciones de inteligencia artificial. Y para implementarlas no es necesario aprender ecuaciones “complejas”. Para empezar es necesario leer un poco sobre RNAs, seleccionar la red neuronal conseguir los programas necesarios y listo!

    Espero se entienda.

    Saludos,

Dejar una Respuesta

Spam Protection by WP-SpamFree

© 2009 Jotadeveloper Blog. Nuestros contenidos están bajo licencia Creative Commons mientras no se indique lo contrario,
y pueden reproducirse libremente sin más que mencionar la fuente ("JotaDeveloper") y la URL concreta del artículo original. .

This blog is powered by Wordpress and JotaDeveloperTheme.