Estou trabalhando em um projeto que inclui reconhecimento facial, está sendo desenvolvido em PHP e JavaScript, mais tarde vou postar como detectar rostos em jQuery, mas para meu projeto precisei fazer em PHP, busquei diversas saídas, a que vou postar foi a mais eficiente para o projeto.
Classe Face_Detector (FaceDetector.php)
<?php 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 - debug by jonatahs guerra */ public function cropFace() { $canvas = imagecreatetruecolor($this->face['w'], $this->face['w']); imagecopy($canvas, $this->canvas, 0, 0, $this->face['x'], $this->face['y'], $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; } } ?>
Exemplo
O código de exemplo mostra como identificar um rosto. Mesmo que a imagem tenhas mais de um rosto, apenas o primeiro é detectado. Embora possa parecer uma limitação, isso pode ser útil em aplicações web que necessitam para criar uma imagem no perfil do usuário a partir de uma imagem crua. De modo que se um usuário carrega uma imagem de corpo inteiro, o código pode detectar o rosto e criar automaticamente uma imagem no perfil para o usuário. (Isso é muito important pro projeto que estou desenvolvendo)
Download detection.dat
<?php include "FaceDetector.php"; $detect = new Face_Detector('detection.dat'); $detect -> face_detect("anadearmas.jpg '); $detect -> toJpeg(); ?>

Reconhecendo Rosto
O código acima mostra uma borda ao redor do rosto detectado, em vez disso, podemos cortar diretamente a área do rosto.
<?php include "FaceDetector.php"; $detect = new Face_Detector('detection.dat'); $detect -> face_detect("anadearmas.jpg '); $detect -> cropFace(); ?>

Retornando somente rosto
Coordenadas do rosto
Caso precise das coordenadas exatas do rosto para usar de alguma maneira, é possível com o código abaixo.
<?php
// Retorna coorndenadas em JSON
// {'x':56.375, 'y':45.1, 'w':227.55}
$detect->toJson();
// OU
// Retorna coordenadas em um Array
$detect->getFace();
?>
Extensão da Classe
Você pode estender a classe Face_Detector usando seus próprios métodos. Por exemplo esse três métodos – rodar(), tamanho() e escalazina() para a classe, o que permite que você modifique ainda mais o resultado.
<?php include "FaceDetector.php"; class FaceModify extends Face_Detector { public function rodar() { $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 escalacinza() { $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 tamanho($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); } } /* Usando */ $detect = new FaceModify('detection.dat'); $detect->face_detect('anadearmas.jpg'); $detect->resizeFace(100,100); ?>
Esse classe foi muito útil para mim, talvez seja para outros! =)
aqui está aparecendo esse alerta e não mostra a imagem.
Notice: imagecolorat(): 1,240 is out of bounds in C:\wamp\www\Nova pasta\FaceDetector.php on line 106
O que exatamente você está tentando fazer?
Cria um pasta, ‘face’ crie um arquivo index.php e add isso.
Pra isso o arquivo .dat e o .php precisa estar na mesma pasta da index.
include “FaceDetector.php”;
$detect = new Face_Detector(‘detection.dat’);
$detect -> face_detect(“anadearmas.jpg ‘);
$detect -> toJpeg();
É exatamente isso que preciso com um porém; preciso de um código em javascript assim, para rodar dentro do photoshop.
Você tem uma idéia de como posso fazer?
Não sou fera em Design(PhotoShop) mas existe como fazer isso em jQuery facilmente. Segue o link do plugin http://facedetection.jaysalvat.com/
Notice: imagecolorat(): 204,240 is out of bounds in /opt/lampp/htdocs/face_detection/FaceDetector.php on line 115
Fiz tudo de acordo com seu post.
Teria algum repositório Git?
Preciso conferir a versão do PHP que está usando e as configurações do servidor.
Segue link do repositório Git: https://github.com/mauricesvay/php-facedetection
Olá Jonathas, muito bom seu post, funcionou de boa. Algumas dúvidas acabaram surgindo, como faço para capturar todas as faces? há possibilidade, como no android, de capturar dados da face, como sorriso, o tanto que olhos estão abertos, etc? pois estou empenhado em um estudo de caso no mestrado com detecção de face, no android já consigo tranquilo fazer isto, e preciso fazer algo desse tipo em php para anailsar o desempenho do smartphone. Abs, Josival
Olá Josival, é possível sim, mas não só com o script que postei.
Eu precisei fazer exatamente o que está comentando, eu usei uma API, segue o link: Face Plus Plus
Espero ter ajudado!
Boa tarde, parabéns pelo Post.
Existe a possibilidade de adaptar para reconhecer um código de barras, de uma imagem digitalizada?
Estou necessitando pegar estes dados de Nota Fiscal Eletronica. Obrigado! Richard
Olá Richard,
Existem classes melhores para reconhecimento de código de barras, não sugiro usar o reconhecimento facial para isso.
Jonathas, muito bom trabalho, estou querendo usar isso no meu projeto de automação residencial.
com este exemplo e possível fazer a medição de certos pontos, como tamanho dos olhos, boca, nariz, bochechas, quero fazer uma comparação para saber se a pessoa pode ou não ter acesso ao meu quarto por exemplo..
Olá Juliano, Eu precisei fazer exatamente o que está comentando, eu usei uma API, segue o nome: Face Plus Plus
Espero ter ajudado!
Boa tarde Jonathas Guerra.
Muito bom seu artigo. Poderia me ajudar com um exemplo do Face Plus Plus? Também pretendo desenvolver uma aplicação para salvar as coordenadas num banco de dados e a partir de nova foto identificar a pessoa.
Muito obrigado.
Olá João, obrigado.
Aqui tem a API: https://github.com/FacePlusPlus
Boa tarde Jonathas.
Obrigado pela resposta. A propósito, no exemplo que você mostra o código para as coordenadas, tenho de acrescer ao código que detecta o rosto não é?
Assim ou estou errando? Segue o código
face_detect(‘sarah.jpg’);
$detect->toJson();
$detect->getFace();
print_r ($detect);
?>
A propósito, demora uns 10, 15 segundos num computador com processador Intel I5. Demora mesmo?
Obrigado
Boa tarde Jonathas, muito bom seu post!
Parabéns mesmo.
Queria saber se você possui conhecimento da existência de alguma API, que seja JS mesmo ou em alguma linguagem WEB que realize identificação de face através de uma webcam.
Se puder ajudar, agradeço imensamente!
Att.
Aqui tem a API: https://github.com/FacePlusPlus
Boa tarde Jonathas.
Obrigado pela resposta. A propósito, no exemplo que você mostra o código para as coordenadas, tenho de acrescer ao código que detecta o rosto não é?
Assim ou estou errando? Segue o código
face_detect(‘sarah.jpg’);
$detect->toJson();
$detect->getFace();
print_r ($detect);
?>
A propósito, demora uns 10, 15 segundos num computador com processador Intel I5. Demora mesmo?
Obrigado
Normalmente não demora nem 0,1seg. pode ser algo do seu computador.
Boa tarde, Jonathas
Bem interessante excelente post, e parabéns pelo trabalho, apesar de ainda não ser o que estava procurando.
Meu nome é Marcio, sou voluntário num projeto social chamado Projeto RUAS (Rondas Urbanas de Amigos Solidários) onde trabalhamos com a população em situação de rua no Rio de Janeiro e Maceio.
Temos um sistema de cadastro de atendidos e estava procurando uma maneira mais rápida pra cadastrar/identificar os atendidos, e o reconhecimento facial foi uma ideia que tive hoje. A ideia é ao começar o atendimento, o voluntário da nossa equipe abriria na tela de Atendidos e em primeiro lugar apontaria a camera do celular para a pessoa que automaticamente faria a busca no banco de dados procurando rostos semelhantes. Caso não encontre ninguém, ainda teríamos a opção de uma busca nominal, e se realmente ele não estivesse no nosso banco, abriria um cadastro novo pro atendido.
Se o rosto fosse identificado por semelhança no nosso banco, já abriria o cadastro dele pra iniciar o atendimento.
Te adicionei no Face pra poder te mostrar o nosso Projeto RUAS, e se você se interessar em colaborar como voluntário para nos ajudar a desenvolver essa ideia, seria SHOW!
Abraços
Bom dia, parab´nes pelo post.
Gostaria de implementar esse com consulta na base de dados, ou seja, capturar a imagem e pesquisar na base.
Sabe de algo?
Abraço e parab´nes
Só tornar a foto dinâmica vindo do banco de dados.
Boa Tarde Companheiro.
Sou iniciante.
Parse error: syntax error, unexpected T_STRING in /home/gdpse234/public_html/newsportal/biometria/FaceDetector.php on line 22
LINHA 22 do FaceDetector.php
descrição linha : namespace svay;
Preciso entender melhor o que ta fazendo. Tem mais detalhes?
[…] Além dessa API do Google, existem centenas de API e não necessariamente precise de uma API, já mostrei aqui no Blog que é possível fazer um reconhecimento facial de maneira simples. […]
Há um erro no código:
$detect->face_detect(“anadearmas.jpg’);
está abrindo aspas duplas e fechando aspas simples.
Boa observação! é de propósito para gerar erro e fazer o aluno perceber onde está e corrigir! Você passou no teste! Parabéns!
Boa noite
Primeiramente parabéns pelo seu trabalho, excepcional a iniciativa e a explanação, e veio de encontro a um projeto/necessidade que tenho atualmente.
É possível usar esse software para realizar buscas de semelhança facial em um banco de dados já existente?
Se sim pode me orientar qual o caminho a ser trilhado?