使用Picasa API进行自动人脸检测以提取个体图像

10

(类似的问题已经在superuser上提出,涉及应用程序的答案。这里发布此问题是为了收集相同的可编程解决方案)

在我的工作场所,护照大小的照片被一起扫描,然后被切成单独的图片并保存为带有唯一文件号的文件。目前我们使用Paint.net手动选择、剪切和保存图片。

样本扫描文档 Picasa 截图: (来源:谷歌图像搜索多个来源,合理使用)

picasa screenshot

例如,在Picasa 3.8中,单击"查看"->"人物",所有的面孔都会显示出来,然后我需要为它们命名,我能否自动保存这些带有不同名称的个人图片?更新:我想做的就是将上面的图片转换为单独的图片。在上面的图像中,我展示了Picasa 3.8如何检测图像并提示我对其进行命名。我不需要人脸识别,只需要人脸检测。Picasa检测到单独的图像并在右侧显示它们。这些单独的图像是我需要的。Picasa创建了一个.ini文件,其中包含包含单独面孔坐标的十六进制值。我感兴趣的是这些单独的面孔。如果我有坐标,我可以从图片中裁剪所需的图像。

SAMPLE.jpg

sample.jpg

ini内容

 [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

该ini文件似乎将每个标签的坐标保存为rect64(534a06d429ae627),dff6163dfd9d4e41。引用Picasa帮助站点用户Technonath的话:

@oedious写道:- 这将有些技术含量,所以请耐心等待。 * rect64()中的数字是一个64位十六进制数。 * 将其分成四个16位数字。 * 每个数字除以最大无符号16位数字(65535),您将得到介于0和1之间的四个数字。 * 剩下的四个数字为面部矩形提供相对坐标:(左,上,右,下)。 * 如果要得到绝对坐标,请将左侧和右侧乘以图像宽度,将顶部和底部乘以图像高度。

上述引用谈论了rect64()中的数字,那么逗号后括号外的数字呢?

我问了一个相关的问题。回答可能也对您有所帮助。 从64位十六进制值获取四个16位数字

注意:ini细节与picasa为特定图像生成的细节相同。 此外,该问题已经更新多次,可能不够清晰。
Picasa帮助网站上有一些回复,我问了同样的问题 该线程中的一个答案是根据ini文件中的十六进制值获取坐标。以下代码来自esac的帮助网站中的C#。我能用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);
} 

PHP代码试图将64位转换为介于1和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/>";
}
?>

输出结果

余数1:49551;结果1:0.75608825683594坐标:371.99542236328 396.94633483887余数1:19598;结果1:0.29904174804688坐标:147.12854003906 156.99691772461余数1:62471;结果1:0.95323181152344坐标:468.99005126953 500.4467010498余数1:34078;结果1:0.51998901367188坐标:255.83459472656 272.99423217773

所以我也有坐标,@Nirmal已经展示了如何裁剪它们。现在下一步是解析picasa.ini文件中的十六进制代码和文件名,并集成代码。 Picasa目前不通过api提供十六进制代码(或他们会吗?)。如果是这样,情况将会更好。

所以我们接近解决方案。谢谢大家,我希望能把奖励颁发给每个人(我不能,但不要担心,注意你的声望的增长!)


你能以(x,y)格式给出所有四个角落的最终坐标吗? - Nirmal
@Nirmal(371,156),(468,156),(468,272),(371,272) - abel
7个回答

5

看看OpenCV - 其中一个示例是用于人脸检测的。


1
我已经寻找这样的库很长时间了,看起来这个会有所帮助。谢谢分享。 - Nirmal

5
您解决问题的方法有点过头了。不必在意脸部照片细节。您只需要一个纯白背景,上面放置一堆矩形图像,找出包含每个图像的矩形并进行裁剪即可。
首先,在原始图像上运行一个过滤器,标记所有非背景像素。这需要一些调整,因为有时背景可能会有一点色调(污垢),或者照片上可能有一些看起来像背景的像素(非常白的牙齿)。
现在,您要寻找没有背景颜色的大面积区域,并将其裁剪成矩形。
既然您正在扫描,为什么不将背景设置为绿色呢? 绿色可能更容易过滤,特别是因为护照照片是在白色背景下拍摄的。

让我从不同的角度看待问题,这是个好主意。制作绿色背景将会非常简单。我正在开发一个 PHP Web 应用程序,它将在本地服务器上运行,它将接收扫描的图像上传,然后将单独的图片保存在服务器上,并将它们作为 zip 文件提供下载。是否有 PHP 库(如 GD?)可以允许我检测颜色并选择矩形? - abel
我不知道是否有库。然而,必须有库来加载图像并处理其中的颜色。首先过滤背景,例如绿色> 90%,红色和蓝色<10%。然后寻找一个连续的绿色区域,其中有矩形孔。 (下载GIMP并使用魔术棒工具玩一下,就会知道我的意思了。)搜索孔的边缘并将其转换为矩形。第二部分有点困难,但有技巧可用,如霍夫变换(http://en.wikipedia.org/wiki/Hough_transform)。如果您知道矩形与页面边缘成直角,则会更有帮助。 - Eyal
我已经更新了问题,用一个PHP脚本来获取图像的坐标。我可以使用这些坐标来裁剪图像吗? - abel

3
要回答有关Picasa的问题,请参考Picasa论坛上的这个帖子:
http://www.google.com/support/forum/p/Picasa/thread?tid=36ae553a7b49088e&hl=en

@oedious写道:- 这将是有点技术性的,所以请耐心等待。 * 矩形64()中的数字是一个64位十六进制数。 * 将其分成四个16位数字。 * 将每个数字除以最大无符号16位数字(65535),您将得到介于0和1之间的四个数字。 * 剩下的四个数字为您提供了人脸矩形的相对坐标:(左,上,右,下)。 * 如果要得到绝对坐标,请将左和右乘以图像宽度,将上和下乘以图像高度。


这是一个宝贵的链接。谢谢。如果您已经理解了我的问题,我正在尝试将扫描的图像分解成单独的图像。如果我有坐标,我可以编写一个为Paint.net设计的插件来实现此目的(我没有任何桌面编程经验,所以这可能需要几个月时间,完成后会发布链接 :))。 - abel
如何从64位数字中获取16位数字? - abel

2

如果扫描的图像始终是5x4网格,则您可以进一步简化问题 :-) ... 然后您可以在几乎任何提供位图操作的编程语言中轻松地打开图像并保存每个正方形。以下是使用C#完成此操作的示例:

private Image Crop(Image pics, Rectangle area)
{
   var bitmap = new Bitmap(pics);
   return (Image)bitmap.Clone(area, bitmap.PixelFormat);
}

您只需要计算每个矩形,然后调用此方法,该方法仅返回由矩形定义的图像区域的面积。类似于以下内容(可能是伪代码,尚未编译以下代码):

// 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
  }
}

但护照照片大小差异很大。有些是5厘米乘4厘米,有些是4乘3厘米,4x4等等,再加上将它们放置在扫描仪中通常会产生杂乱的排列方式。如果我能检测到图像的标记部分,然后将它们裁剪出来...我可以使用一些PHP。 - abel
我已经更新了原始问题,如果我能够获得坐标,您的解决方案似乎是可行的。但是这些坐标是十六进制的,而我对十六进制一无所知。 - abel

1
关于裁剪部分,我正在输入代码但尚未测试,但应该可以工作:
<?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);
?>

以上的代码假设你的源图像是JPEG格式,而且坐标构成一个完美的矩形或正方形。
希望能有所帮助。

谢谢你,尼尔马尔。我正在探索用于PHP的图像处理库。点赞! - abel

1

这应该能帮助你完成任务。这里有一些解析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/>";
    }
}

1

我在.NET中开发了一个小应用程序,正如你所说的那样,它可以生成面部文件。在这里查看:http://ceottaki.com/devprojects/getpicasafaces

源代码也可用。

虽然我还没有实现从十六进制代码获取联系人姓名,但使用Google Contacts API是可能的:http://code.google.com/apis/contacts/

通过该API,可以按ID获取联系人,如果您的联系人在Picasa和Google Contacts之间同步,则十六进制ID相同。

完整联系人链接的最后一部分是Picasa使用的十六进制。

希望这有所帮助。

祝好, Felipe。


1
干得好!没有想到联系人集成;这是一个额外的优点。但是依赖 Picasa 获取矩形十六进制值是很痛苦的。希望有一个 API 可以解决这个问题! - abel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接