在PHP中比较两个图像

35
比较两张图片是否为同一文件很容易:使用文件的MD5值即可。但是,通过使用PHP GD获取两张图片的差异来确定它们是否相同,这种方法是否可行呢?如果我们获取了两张图片的差异,并且它全部是白色(我会假设是白色或者黑色),那么我们现在就知道它们是否是同一张照片了吗?
另外,我想知道是否有可能获得两张大小相等的图片,以创建洋葱皮效果:其中一张透明度为50%,另一张也是50%。

你还需要透明效果吗?我已经回答了标题问题,但也可以编写侧边注释。 - Leopoldo Sanczyk
9个回答

22

大多数其他答案是在提及使用各种哈希函数。而这个问题明确地询问的是比较图像内容,而不是比较文件。

这意味着您需要真正理解图像的内容。在 PHP 中,通常使用两个扩展来处理此问题:ImageMagick 和 GD。

ImageMagick 提供了各种工具,可以通过 PHP ImageMagick 扩展进行使用。

http://www.php.net/manual/en/function.imagick-compareimages.php

最大的问题是该库的文档几乎不存在,因此需要进行大量的尝试和错误检查。PHP 扩展是 ImageMagick 库的一个很薄的封装器,因此 compareimages() 函数的详细信息可以在 ImageMagick 文档中找到。


如果我们不依赖于一个库,那怎么样? - gumuruh
1
不确定我理解你的问题,gumuruh?如果你不想依赖库,你就得自己编写算法来解析和处理图像。ImageMagick是开源的,所以如果你有很多时间(并且愿意牺牲效率),我猜你可以编写一个PHP本地实现。 - kander

18
$md5image1 = md5(file_get_contents($image1));
$md5image2 = md5(file_get_contents($image2));
if ($md5image1 == $md5image2) {

}

11
为什么不直接使用md5_file()函数? - Jason
14
确实是文件比较,而不是问题所涉及的图像比较。 - Michael M
@Jason 为什么不直接使用 ($image1 == $image2) 呢? - Peter
1
我不知道为什么,但是当我比较两个相同的图像时,md5失败了,而md5_file却可以正常工作。 - Pablo
4
如果您要比较由不同软件(甚至是不同的libpng版本)生成的图像,则同一张图片在磁盘上可能具有不同的二进制表示,因此MD5比较会失败。 - AnotherHowie
显示剩余3条评论

11

Libpuzzle 是一个可以比较图像的 PHP 扩展。


4

这个类似的问题在 Stackoverflow 的讨论中被提出,我曾经开发了一个用于自己使用的工具。现在在这里分享出来以帮助其他人。

它需要两个(或多个)图像,并提供了一些选项来检查它们之间的差异。可以选择使用的分辨率和严格程度等选项。

我还写了一篇更全面的博客文章介绍了它的使用方法。


那确实是一个非常棒的脚本。 - Kalle H. Väravas

4
我编写了一种均方误差比较方法,使用已经包含在PHP中的GD图形库。我很关心时间,因此我包括了一些选项,例如: 但是观察结果,如果您正在执行批量操作,则最好硬编码选项以避免条件语句的开销
这是代码和一些测试基准测试
<?php
define('FILENAME1','./image1.png'); 
define('FILENAME2','./image2.png');    
define('OUTEXT','png'); //Output extension
    
/**
 * Calculate an arbitrary metric of difference between images
 * 
 * @param resource(gd) $img1 First image
 * @param resource(gd) $img2 Second image
 * @param int $resX Scaled width resolution
 * @param int $resY Scaled height resolution
 * @param int $pow Mean square error if 2, but could be another power
 * @param string $channel RGB channel or 'all' for perceived luminance
 *
 * @return float the calculated metric
 */
function diff($img1,$img2,$resX=false,$resY=false,$pow='2',$channel='all'){ 
    //Scaling to image 1 size for default
    if(!$resX||!$resY){     
        $resX=imagesx($img1); //width
        $resY=imagesy($img2); //height
    }
    //Use imagescale() function to scale the images 
    $thumb1=imagescale($img1,$resX,$resY);
    $thumb2=imagescale($img2,$resX,$resY);
    //Sum of errors
    $sum=0;
    for($x=0;$x<$resX;++$x){
        for($y=0;$y<$resY;++$y){
            //Read pixel bytes
            $bytes1=imagecolorat($thumb1,$x,$y);
            $bytes2=imagecolorat($thumb2,$x,$y);
            //Split the channel values from bytes
            $colors1=imagecolorsforindex($thumb1,$bytes1);
            $colors2=imagecolorsforindex($thumb2,$bytes2);
            //Choose image channel
            if($channel=='all'){
                //Perceived luminance
                $value1=sqrt(0.2126*$colors1['red']**2+0.7152*$colors1['green']**2+ 0.0722*$colors1['blue']**2);
                $value2=sqrt(0.2126*$colors2['red']**2+0.7152*$colors2['green']**2+ 0.0722*$colors2['blue']**2);
            }else{
                //RGB channel
                $value1=$colors1[$channel];
                $value2=$colors2[$channel];
            }
            $sum+=abs($value1-$value2)**$pow;           
        }
    }
    //Return mean of the error sum
    return $sum/($resX*$resY);
}

//Show image in HTML
function imgdraw($imgobj){      
    //Choose function
    $image="image".OUTEXT;
    //Capture the image data stream     
    ob_start();     
    $image($imgobj);        
    $data = ob_get_clean();     
    //Create and HTML img
    echo '<img src="data:image/png;base64,'.base64_encode($data).'" />';
}

//Load an image
function loadimg($filename){
    //Get filename extension
    $ext=substr($filename,strrpos($filename,'.')+1);
    //Create image object
    $imagecreate="imagecreatefrom$ext";
    return $imagecreate($filename);
}

//Test 
$img1=loadimg(FILENAME1);
$img2=loadimg(FILENAME2);
if( !$img1 || !$img2){
    //Problem reading the files
    die("Error loading image files");
}else{
    imgdraw($img1);
    imgdraw($img2);
}

//Times for 133x144 pixels png images
echo "<p>original size MSE perceived luminance: ".diff($img1,$img2)."</p>";
//time: 0.2281 seconds.
echo "<p>original size MSE green channel: ".diff($img1,$img2,false,false,2,'green')."</p>";
//time: 0.1364 seconds.
echo "<p>original size linear perceived luminance: ".diff($img1,$img2,false,false,1)."</p>";
//time: 0.1920 seconds.
echo "<p>original size linear green channel: ".diff($img1,$img2,false,false,1,'green')."</p>";
//time: 0.1351 seconds.
echo "<p>64x64 MSE perceived luminance: ".diff($img1,$img2,64,64)."</p>";
//time: 0.0431 seconds.
echo "<p>64x64 MSE green channel: ".diff($img1,$img2,64,64,2,'green')."</p>";
//time: 0.0293 seconds.
echo "<p>64x64 linear perceived luminance: ".diff($img1,$img2,64,64,1)."</p>";
//time: 0.0423 seconds.
echo "<p>64x64 linear green channel: ".diff($img1,$img2,64,64,1,'green')."</p>";
//time: 0.0293 seconds.
echo "<p>16x16 MSE perceived luminance: ".diff($img1,$img2,16,16)."</p>";
//time: 0.0028 seconds.
echo "<p>16x16 MSE green channel: ".diff($img1,$img2,16,16,2,'green')."</p>";
//time: 0.0027 seconds.
echo "<p>16x16 linear perceived luminance: ".diff($img1,$img2,16,16,1)."</p>";
//time: 0.0027 seconds.
echo "<p>16x16 linear green channel: ".diff($img1,$img2,16,16,1,'green')."</p>";
//time: 0.0018 seconds.
?>

3

1
这是一个链接到Wayback机器上的网页,它展示了一个名为“Image Compare”的程序。该程序可以帮助用户比较两张图片之间的差异。它使用PHP语言编写,并包含了一些基本的图像处理功能。通过这个程序,用户可以上传并比较两张不同的图片。 - mfink

0

如果你只是比较两个文件,那么哈希数据然后进行比较是完美的解决方案。如果你要比较大量的文件,最好先按大小排序,然后只与相同大小的文件进行比较。


0

0
如果您正在使用 PHP 8.1 或更高版本,您可以使用 https://github.com/sapientpro/image-comparator 库来实现此功能:
$comparator = new SapientPro\ImageComparator\ImageComparator();

$similarity = $imageComparator->compare('your-images/your-image1.jpg', 'your-images/your-image12.jpg');

echo $similarity; // 82.3

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