Detecção automática de rosto usando a API do Picasa para extrair imagens individuais
-
28-09-2019 - |
Pergunta
(Uma pergunta semelhante foi feita ao superusuário para obter respostas relacionadas a aplicativos.A questão é postada aqui para reunir soluções programáveis para a mesma)
No meu local de trabalho, as fotografias tamanho passaporte são digitalizadas juntas, depois cortadas em imagens individuais e salvas com números de arquivo exclusivos.Atualmente usamos o Paint.net para selecionar, cortar e salvar manualmente as imagens.
Exemplo de captura de tela do Picasa de documento digitalizado:(de:pesquisa de imagens do Google em várias fontes, uso justo)
Por exemplo.No Picasa 3.8, ao clicar em Visualizar > Pessoas, todos os rostos são mostrados e sou solicitado a nomeá-los. Posso salvar essas imagens individuais automaticamente com os nomes como imagens diferentes?
Atualizada
Tudo o que quero fazer é converter a imagem acima em imagens individuais.
Na imagem acima, mostrei como o Picasa 3.8 detecta as imagens e me pede para nomeá-las.Não preciso de reconhecimento facial, simplesmente preciso de detecção facial.O Picasa detecta as imagens individuais e as mostra no RHS.Essas imagens individuais são o que eu preciso.O Picasa cria um arquivo .ini que salva os valores hexadecimais que contêm as coordenadas das faces individuais.
Esses rostos individuais são o que me interessa. Se eu puder ter as coordenadas, posso cortar as imagens necessárias da imagem.
AMOSTRA.jpg
conteúdo inicial
[SAMPLE.jpg]
faces=rect64(c18f4c8ef407851e),d4ff0a020be5c3c0;rect64(534a06d429ae627),dff6163dfd9d4e41;rect64(b9c100fae46b3046),e1059dcf6672a2b3;rect64(7b5105daac3a3cf4),4fc7332c107ffafc;rect64(42a036a27062a6c),ef86c3326c143248;rect64(31f4efe3bd68fd8),90158b3d3b65dc9b;rect64(327904e0614d390d),43cbda6e92fcb63e;rect64(4215507584ae9b8c),15b6a967e857f334;rect64(895d4efeb8b68425),5c4ff70ac70b27d3
backuphash=3660
*O arquivo ini parece estar salvando as coordenadas das tags faciais como rect64(534a06d429ae627),dff6163dfd9d4e41
para cada etiqueta.Citando de Site de ajuda do Picasa do utilizador Technonath diz
@oedious escreveu:- Isso vai ser um pouco técnico, então espere.* O número contido em rect64() é um número hexadecimal de 64 bits.* Divida isso em quatro números de 16 bits.* Divida cada um pelo número máximo de 16 bits não assinados (65535) e você terá quatro números entre 0 e 1.* Os quatro números restantes dão-lhe coordenadas relativas para o rosto retângulo:(esquerda, superior, direita, inferior).* Se você quiser acabar com coordenadas absolutas, vários à esquerda e À direita pela largura da imagem e pela parte superior e inferior pela altura da imagem.
A citação acima fala sobre o número entre rect64() e o número fora dos parênteses após a vírgula?
Eu fiz uma pergunta relacionada.As respostas também podem ajudá-lo.Obtenha quatro números de 16 bits a partir de um valor hexadecimal de 64 bits
Observação:O Os detalhes do INI são os mesmos que o Picasa gerado para a imagem em particular.
Além disso, a pergunta foi atualizada várias vezes e pode não ser clara o suficiente.
Existem algumas respostas no Site de ajuda do Picasa, onde fiz a mesma pergunta Uma das respostas desse thread para obter coordenadas com base nos valores hexadecimais do arquivo ini.O código a seguir está em C# de esac do site de ajuda.Posso fazer o mesmo em PHP?
public static RectangleF GetRectangle(string hashstr)
{
UInt64 hash = UInt64.Parse(hashstr, System.Globalization.NumberStyles.HexNumber);
byte[] bytes = BitConverter.GetBytes(hash);
UInt16 l16 = BitConverter.ToUInt16(bytes, 6);
UInt16 t16 = BitConverter.ToUInt16(bytes, 4);
UInt16 r16 = BitConverter.ToUInt16(bytes, 2);
UInt16 b16 = BitConverter.ToUInt16(bytes, 0);
float left = l16 / 65535.0F;
float top = t16 / 65535.0F;
float right = r16 / 65535.0F;
float bottom = b16 / 65535.0F;
return new RectangleF(left, top, right - left, bottom - top);
}
Código PHP tentando converter 64 bits em números entre 1 e 0
<?php
$dim = getimagesize("img.jpg");
$hex64=array();
$b0="c18f4c8ef407851e";
$hex64[]=substr($b0,0,4);
$hex64[]=substr($b0,4,4);
$hex64[]=substr($b0,8,4);
$hex64[]=substr($b0,12,4);
$width=$dim[0];
$height=$dim[1];
foreach($hex64 as $hex16){
$dec=hexdec($hex16);
$divide=65536;
$mod=$dec%$divide;
$result=$dec/$divide;
$cordinate1=$result*$width;
$cordinate2=$result*$height;
echo "Remainder 1 : ".$mod." ; Result 1 : ".$result."<br/>CO-ORDINATES : <B>".$cordinate1." ".$cordinate2."</B><br/>";
}
?>
A saída
Restante 1:49551;Resultado 1:0,75608825683594 COORDENADAS:371.99542236328 396.94633483887 Restante 1:19598;Resultado 1:0,29904174804688 COORDENADAS:147.12854003906 156.99691772461 Restante 1:62471;Resultado 1:0.95323181152344 COORDENADAS:468.99005126953 500.4467010498 Restante 1:34078;Resultado 1:0,51998901367188 COORDENADAS:255.83459472656 272.99423217773
Então eu também tenho as coordenadas e @Nirmal tem mostrado como cortá-los.Agora, as próximas etapas seriam analisar o picasa.ini em busca de códigos hexadecimais e nomes de arquivos e integrar o código.No momento, o Picasa não fornece códigos hexadecimais por meio de uma API(ou Eles?).Se fosse esse o caso, as coisas teriam sido melhores.
Portanto, estamos nos aproximando de uma solução.Obrigado a todos, gostaria de poder conceder a recompensa a todos (não posso, mas não tema e fique atento para um aumento em sua reputação!)
Solução
Para responder à pergunta do picasa, veja esta resposta nos fóruns do picasa:
http://www.google.com/support/forum/p/Picasa/thread?tid=36ae553a7b49088e&hl=en
@oedious escreveu:- Isso vai ser um pouco técnico, então espere.* O número contido em rect64() é um número hexadecimal de 64 bits.* Divida isso em quatro números de 16 bits.* Divida cada um pelo número máximo de 16 bits não assinados (65535) e você terá quatro números entre 0 e 1.* Os quatro números restantes dão-lhe coordenadas relativas para o rosto retângulo:(esquerda, superior, direita, inferior).* Se você quiser acabar com coordenadas absolutas, vários à esquerda e À direita pela largura da imagem e pela parte superior e inferior pela altura da imagem.
Outras dicas
Olhe para OpenCV - um dos exemplos que acompanha a distribuição é para detecção de rosto.
Sua solução para o problema é um exagero.Ignore os rostos.O que você tem é um fundo branco sólido e um monte de imagens retangulares nele.Tudo que você precisa fazer é encontrar o retângulo que envolve cada imagem e recortar.
Comece executando um filtro sobre a imagem original que marca todos os pixels que não são de fundo.Isso exigirá alguns ajustes porque às vezes o fundo terá um toque de tonalidade (sujeira) ou a foto terá alguns pixels que se parecem com o fundo (dentes bem brancos).
Agora você procura grandes áreas sem cor de fundo.Corte-os em retângulos.
Já que é você quem está fazendo a digitalização, por que não deixar o fundo verde?Verde pode ser uma cor mais fácil de filtrar, especialmente porque as fotos do passaporte são tiradas em fundo branco.
Você pode simplificar ainda mais o problema :-) se as imagens digitalizadas estiverem sempre em uma grade 5x4...então você pode facilmente basta abrir a imagem em praticamente qualquer linguagem de programação que ofereça manipulação de bitmap e salvar cada quadrado.Aqui está um exemplo de como fazer isso com C#:
private Image Crop(Image pics, Rectangle area)
{
var bitmap = new Bitmap(pics);
return (Image)bitmap.Clone(area, bitmap.PixelFormat);
}
Tudo o que você precisa fazer é calcular cada retângulo e depois chamar esse método que retorna apenas a área da imagem definida pelo retângulo.Algo como (possivelmente pseudocódigo, não compilei o código abaixo):
// assuming that each sub image in the larger is 45x65
int cellwidth=45, cellheight=65;
for(int row=0;row<5;row++)
{
for(int col=0;col<4;col++)
{
var rect = new Rectangle(
row * cellwidth,
col * cellheight,
cellwidth,
cellheight);
var picture = Crop(bigPicture, rect);
// then save the sub image with whatever naming convention you need
}
}
Para a parte de corte, estou digitando o código sem testar, mas deve funcionar:
<?php
//source image
$srcImg = "full/path/of/source/image.jpg";
//output image
$outImg = "full/path/to/result/image.jpg";
//coordinates obtained from your calculation
$p1 = array('X'=>371, 'Y'=>156);
$p2 = array('X'=>468, 'Y'=>156);
$p3 = array('X'=>468, 'Y'=>272);
$p4 = array('X'=>371, 'Y'=>272);
//let's calculate the parametres
$srcX = $p1['X'];
$srcY = $p1['Y'];
$width = $p2['X'] - $p1['X'];
$height = $p4['Y'] - $p1['Y'];
//image processing
$srcImg = imagecreatefromjpeg($srcImg);
$dstImg = imagecreatetruecolor($width, $height);
imagecopy($dstImg, $srcImg, 0, 0, $srcX, $srcY, $width, $height);
imagejpeg($dstImg, $outImg, 100); // 100 for highest quality, 0 for lowest quality
imagedestroy($dstImg);
?>
O código acima assume que sua imagem de origem está no formato JPEG e as coordenadas formam um retângulo ou quadrado perfeito.
Espero que ajude.
Isso deve levá-lo até a linha de chegada.Aqui está um código para analisar o INI.
<?php
$vals = parseIni('picasa.ini');
foreach($vals as $filename => $values) {
$rects = getRects($values['faces']);
foreach($rects as $rect) {
printImageInfo($filename, $rect);
}
}
/**
* PHP's own parse_ini_file doesn't like the Picasa format.
*/
function parseIni($file)
{
$index = 0;
$vals = array();
$f = fopen($file, 'r');
while(!feof($f)) {
$line = trim(fgets($f));
if (preg_match('/^\[(.*?)\]$/', $line, $matches)) {
$index = $matches[1];
continue;
}
$parts = explode('=', $line, 2);
if (count($parts) < 2) continue;
$vals[$index][$parts[0]] = $parts[1];
}
fclose($f);
return $vals;
}
function getRects($values)
{
$values = explode(';', $values);
$rects = array();
foreach($values as $rect) {
if (preg_match('/^rect64\(([^)]+)\)/', $rect, $matches)) {
$rects[] = $matches[1];
}
}
return $rects;
}
function printImageInfo($filename, $rect)
{
$dim = getimagesize($filename);
$hex64=array();
$hex64[]=substr($rect,0,4);
$hex64[]=substr($rect,4,4);
$hex64[]=substr($rect,8,4);
$hex64[]=substr($rect,12,4);
$width=$dim[0];
$height=$dim[1];
foreach($hex64 as $hex16){
$dec=hexdec($hex16);
$divide=65536;
$mod=$dec%$divide;
$result=$dec/$divide;
$cordinate1=$result*$width;
$cordinate2=$result*$height;
echo "Remainder 1 : ".$mod." ; Result 1 : ".$result."<br/>CO-ORDINATES : <B>".$cordinate1." ".$cordinate2."</B><br/>";
}
}
Desenvolvi um pequeno aplicativo em .NET que faz exatamente o que você falou, produz os arquivos para os rostos.Confira aqui: http://ceottaki.com/devprojects/getpicasafaces
O código-fonte também está disponível.
Embora eu não tenha implementado a obtenção do nome dos contatos a partir do código hexadecimal, é possível usar a API de Contatos do Google: http://code.google.com/apis/contacts/
Com essa API é possível obter contatos por ID, e se seus contatos estiverem sincronizados entre o Picasa e os Contatos do Google, o ID hexadecimal é o mesmo.
A última parte de um link de contato completo é o hexadecimal usado pelo Picasa.
Eu espero que isso ajude.
Obrigada Felipe.