Joseph
Joseph

Reputation: 383

Compare 2 images in PHP

Comparing two images to see if they are both the same files is easy: throw the files MD5. But is it possible or even plausible to determine if two images are same by using PHP GD to get the difference of the two images? If we were to get the difference of the two, and it was all white (I'd assume white or even black), then we would now know whether they were both the same photo?

Side note: I'd like to know if it's possible to get two images of equal size to create an onion skin effect: 50% transparency on one and 50% on the other.

Upvotes: 35

Views: 76421

Answers (5)

Vadym Matsotskyi
Vadym Matsotskyi

Reputation: 71

If you are using php 8.1 or higher, you can use https://github.com/sapientpro/image-comparator library for that:

$comparator = new SapientPro\ImageComparator\ImageComparator();

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

echo $similarity; // 82.3

Upvotes: 0

Leopoldo Sanczyk
Leopoldo Sanczyk

Reputation: 1609

I coded a kind of Mean Square Error comparing method, using GD Graphics Library already included in PHP.

I was concerned about time, so I included some options such as:

But seeing the results, if you are doing bulk operations, perhaps the best is to hardcode the options to avoid the overhead of the conditional lines.

This is the code, with some test and a benchmark:

<?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.
?>

Upvotes: 4

kander
kander

Reputation: 4286

Most of the other answers refer to using various hashing functions. The question explicitly is asking about comparing the contents of the images, not about comparing the files.

This means you end up having to actually understand the contents of the image. In PHP there are two extensions often used for this, ImageMagick and GD.

ImageMagick offers various tools you can use for this, through the PHP ImageMagick extension.

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

Biggest problem is that the documentation for that library is pretty much non-existing, so there will be a lot of trial-and-error involved. The PHP extension is a pretty thin wrapper around the ImageMagick library, so details of how the compareimages() function behaves can be found in the ImageMagick documentation.

Upvotes: 24

mrd081
mrd081

Reputation: 279

If you are comparing just two files then hashing data and then comparing is perfect solution. If you are comparing large number of files, then better sort them first based on size and then compare only with same size.

Upvotes: 0

Hamada Mido
Hamada Mido

Reputation: 393

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

}

Upvotes: 19

Related Questions