JakubKubera
JakubKubera

Reputation: 436

imagick / php - check the differences between the pictures and the determination of changes

I have two images (first and second link).

First image

Second image

Third image

Third image was generated by imagick, by code:

$image1 = new \imagick(dirname(dirname(dirname(__DIR__))) . "/1.jpg");
$image2 = new \imagick(dirname(dirname(dirname(__DIR__))) . "/2.jpg");

$result = $image1->compareImages($image2, \Imagick::METRIC_MEANSQUAREERROR);
$result[0]->setImageFormat("jpg");

header("Content-Type: image/jpeg");
echo $result[0];
exit();

Can I indicate changes in a different way? For example, differences marked red frame.

Upvotes: 2

Views: 1437

Answers (2)

Danack
Danack

Reputation: 25701

This answer is similar to the one above, except it's implemented in PHP.

Also, if you're going to do something like this, please present the difference image as an animation. Either a Gif or using javascript to swap between images. Having the regions marked be flashing makes life so much easier for users.

Also, the processing time can be sped up a little by doing two smaller compares, one vertical and one horizontal compared to one big compare.

$image1 = new Imagick(__DIR__."/compare1.jpg");
$image2 = new Imagick(__DIR__."/compare2.jpg");

$image1->compositeImage($image2, \Imagick::COMPOSITE_DIFFERENCE, 0, 0);

$overlay = clone $image1;
$overlay->negateImage(false);
$overlay->setImageAlphaChannel(\Imagick::ALPHACHANNEL_DEACTIVATE);
$overlay->transformImageColorSpace(\Imagick::COLORSPACE_GRAY);

// Doing one big compare is slow
//$overlay->statisticImage(\Imagick::STATISTIC_MINIMUM, 20, 20);
// Doing a horizontal and vertical compare is faster
$overlay->statisticImage(\Imagick::STATISTIC_MINIMUM, 20, 2);
$overlay->statisticImage(\Imagick::STATISTIC_MINIMUM, 2, 20);

$overlay->statisticImage(\Imagick::STATISTIC_GRADIENT, 4, 4);

$red = new Imagick();
$red->newPseudoImage(
    $overlay->getImageWidth(),
    $overlay->getImageHeight(),
    'xc:red'
);

$red->compositeImage($overlay, \Imagick::COMPOSITE_COPYOPACITY, 0, 0);

$withOutline = clone $image2;
$withOutline->compositeImage($red, \Imagick::COMPOSITE_ATOP, 0, 0);

$outputGif = new Imagick();
$outputGif->addImage($image2);
$outputGif->addImage($withOutline);

$outputGif = $outputGif->deconstructImages();
$outputGif->setImageFormat('gif');    
header("Content-Type: image/gif");
echo $outputGif->getImagesBlob();

As you can see the flashing detection makes it much easier to see:

enter image description here

Upvotes: 3

Mark Setchell
Mark Setchell

Reputation: 207425

That is actually non-trivial :-) There are a couple of issues, firstly you need to identify the differences - you can do that by differencing (-compose difference). Then you need to threshold the differences according to how much they differ, but because you are using JPEG which is lossy, there are quantisation end encoding differences at every pixel. Once you have the differences, you need to outline them - but if you outline them naively, you will get a box around every singel pixel that differs instead of one box around each shape, so you need to do some blurring and thresholding (or something else) to merge nearby differences into single lumps. Once you have found the lumps, you need to put a frame around them - you could either do a "Connected Components Analysis" or something else to find the edges of the lumps - I chose an "Edge Out Morphology". Then you need to colour the edges and composite them back on top of the original image. The command looks like this on the command line:

convert a.jpg \
   \( +clone b.jpg -compose difference -composite                   \
      -threshold 1% -separate -evaluate-sequence Add                \
      -blur 0x5 -threshold 10%                                      \
      -morphology edgeout diamond:3                                 \
      -fill red -opaque white -transparent black -write mask.png \) \
      -compose srcover -composite  result.png

enter image description here

It will of course look different depending on which of the original two pictures you composite the outlines onto - I chose to composite onto the first one, but you could choose the second.

I wrote out the mask into the file mask.png so you can see what I am actually building up inside the parentheses, and I put a border around it so you can see it below:

enter image description here

Here is another variation for you to experiment with - it is hard to know what will work best with all your images:-)

convert a.jpg \
   \( +clone b.jpg -compose difference -composite -threshold 1%    \
      -statistic maximum 25x25                                     \
      -morphology edgeout diamond:5                                \
      -fill red -opaque white -transparent black -write mask.png   \
   \) -compose srcover -composite  result.png

Upvotes: 3

Related Questions