image diff

November 15th, 2008. Tagged: images, php

Was having fun today with idiff.php - a PHP shell script to tell you if two images are visually different by comparing them pixel by pixel. If there's a difference, the script creates a third image - black background with the different pixels in green. Only after writing the script I found that there's an imagemagick command compare that does the same :)

the script

The idea is that two GD images are created from the input files, also a third GD image with black background to store the diff. Loop through all the pixels and compare them one by one. If one is different, write a green pixel at the same location of the diff image.

<?php
/**
 * Shell script to tell if two images are identical.
 * If not, a third image is written - black background with the different pixels painted green
 * Code partially inspired by and borrowed from http://pear.php.net/Image_Text test cases
 */
 
// check if there's enough input
if (empty($argv[1]) || empty($argv[2])) {
    echo 'gimme at least two image filenames, please.', "\n";
    echo 'e.g. "php idiff.php img1.png img2.png"';
    echo "\n", 'third filename is the image diff, optional, default is "diffy.png"';
    exit(1);
}
 
// create images
$i1 = @imagecreatefromstring(file_get_contents($argv[1]));
$i2 = @imagecreatefromstring(file_get_contents($argv[2]));
 
// check if we were given garbage
if (!$i1) {
    echo $argv[1] . ' is not a valid image';
    exit(1);
}
if (!$i2) {
    echo $argv[2] . ' is not a valid image';
    exit(1);
}
 
// dimensions of the first image
$sx1 = imagesx($i1);
$sy1 = imagesy($i1);
 
// compare dimensions
if ($sx1 !== imagesx($i2) || $sy1 !== imagesy($i2)) {
    echo "The images are not even the same size";
    exit(1);
}
 
// create a diff image
$diffi = imagecreatetruecolor($sx1, $sy1);
$green = imagecolorallocate($diffi, 0, 255, 0);
imagefill($diffi, 0, 0, imagecolorallocate($diffi, 0, 0, 0));
 
// increment this counter when encountering a pixel diff
$different_pixels = 0;
 
// loop x and y
for ($x = 0; $x < $sx1; $x++) {
    for ($y = 0; $y < $sy1; $y++) {
 
        $rgb1 = imagecolorat($i1, $x, $y);
        $pix1 = imagecolorsforindex($i1, $rgb1);
 
        $rgb2 = imagecolorat($i2, $x, $y);
        $pix2 = imagecolorsforindex($i2, $rgb2);
 
        if ($pix1 !== $pix2) { // different pixel
            // increment and paint in the diff image
            $different_pixels++;
            imagesetpixel($diffi, $x, $y, $green);
        }
 
    }
}
 
 
if (!$different_pixels) {
    echo "Image is the same";
    exit(0);
} else {
    if (empty($argv[3])) {
        $argv[3] = 'diffy.png'; // default result filename
    }
    imagepng($diffi, $argv[3]);
    $total = $sx1 * $sy1;
    echo "$different_pixels/$total different pixels, or ", number_format(100 * $different_pixels / $total, 2), '%';
    exit(1);
}
?>

usage

Type in the command line something like:

php idiff.php img1.png img2.png result.png

This command will compare img1.png with img2.png. If they are not the same dimensions, will exit with error code. If their pixels exactly the same will exit with success code 0 and print a success message. If the images are not the same, will write the file result.png.

If you omit result.png, the generated file will be called diffy.png

example

img1.png:

img2.png

running the command:

>php idiff.php img1.png img2.png
70/24600 different pixels, or 0.28%

the result diffy.png:

imagemagick

yep, so after I did this I found that imagemagick has a command called compare that does the same, only with more features, like the -fuzz option for example that allows you to specify how different should the pixels be, in order to consider them important.

the command:

>compare img1.png img2.png diffy-im.png

the diffy-im.png result

As you can see the different pixels are red and the actual image is a very pale background. Nice.

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov