Can we programmatically compare different images of same resolutions?
Asked Answered
S

1

20

Is there a good and reliable way to compare images of same format and same resolution and get difference between them?

In the best-case scenario I am looking for some numerical representation of image that can be then compared because I have to compare many images.

Sunfast answered 15/1, 2015 at 23:32 Comment(2)
The question is very broad, maybe even "too broad" (because there are many possible difference measures for images), but it's not what I'd call "opinion based" - see the current answer.Campion
I don't think it is "too broad" since I tagged it c# and c. On top of that following answer explains everything really good.Sunfast
D
46

You can use ImageMagick's compare command to do this.

(If you are successful with the command line, you could then move on to use one of ImageMagick's APIs: these are, amongst others, available for C ('MagickWand'), C++ ('Magick++'), Java ('JMagick'), LISP ('L-Magick'), .NET ('Magick.NET'), Perl ('PerlMagick'), PHP ('IMagick'), Python ('PythonMagick') and Ruby ('RMagick'). -- Then implement the respective functions into an application of your own.)

The only requirement is: the images need to have identical dimensions in width and height, measured in the number of pixels. So you do not even need the same format as you assumed.

The difference can be returned in different ways:

  • Generate a visual representation of the differences, where the pixel with deltas are somehow highlighted in a delta image.

  • Generate a textual and/or statistical representation of the differences, where the output is one or several numbers, or just the count of pixels which are different, or some other metric.


Example

Here are four example images which can compared. They each are similarly looking, have a size of 322x429 pixels -- but there are some finer differences in colorization and format: the top right one is a JPEG, the other three are PNGs:

 

 

Visual comparisons

Here is the most simple command to compare the top two images and produce a visual 'delta':

compare                              \
  https://i.sstatic.net/GBax7.png \
  https://i.sstatic.net/D9IAV.jpg \
  delta1.pdf

This compares a PNG with the JPEG and produces a PDF as output. For a visual impression of this output see image below on the left (since PDFs cannot be displayed here, I did resort to produce a PNG and use this for display instead).

What did this simplest of all ImageMagick compare commands exactly do?

  1. It used the first image as a pale background.
  2. It overlayed red, fully opaque pixels on each location where the color of the respective pixel in the second image deviates from the first one.

(I could have added -highlight-color blue and -lowlight-color yellow or any other color definitions if I wouldn't want the default red highlighting)

What if I didn't want such an over-exact comparison of pixel colors? What if I'd like a red pixel only when there is a more considerable color distance between the respective pixels?

Easy: add a 'fuzz' factor to the command line!

compare                              \
  https://i.sstatic.net/GBax7.png \
  https://i.sstatic.net/D9IAV.jpg \
 -fuzz 2.5%                          \
  delta2.png

 

When running without additional arguments the first (most simple) compare command, ImageMagick silently added a specification of its default comparison method, which is called -compose src-over.

Of course we can override this and use a different composition mode. How to learn about the available composition modes? The command convert -list compose will enumerate them for us! Here is the complete list -- it contains 67 different ones on my system:

Atop Blend Blur Bumpmap ChangeMask Clear ColorBurn ColorDodge Colorize CopyBlack CopyBlue CopyCyan CopyGreen Copy CopyMagenta CopyOpacity CopyRed CopyYellow Darken DarkenIntensity DivideDst DivideSrc Dst Difference Displace Dissolve Distort DstAtop DstIn DstOut DstOver Exclusion HardLight HardMix Hue In Lighten LightenIntensity LinearBurn LinearDodge LinearLight Luminize Mathematics MinusDst MinusSrc Modulate ModulusAdd ModulusSubtract Multiply None Out Overlay Over PegtopLight PinLight Plus Replace Saturate Screen SoftLight Src SrcAtop SrcIn SrcOut SrcOver VividLight Xor

Let's try each and every one:

for i in $(convert -list compose|tr "\n" " "); do \
  compare                                         \
     https://i.sstatic.net/GBax7.png           \
     https://i.sstatic.net/D9IAV.jpg           \
    -compose ${i}                                 \
     delta-${i}.png ;                             \
done

Of course it would be too much to now show each and every resulting delta image. The most interesting ones are below:

  • top left is -compose Difference
  • top right is -compose DivideSrc
  • center left is -compose CopyBlue
  • center right is -compose MinusDst
  • bottom left is -compose Hue
  • bottom right is -compose LightenIntensity

Note: if you swap the order of the two compared images, the two resulting deltas could be significantly different two!

 

 

 

Now you can already start your own experiments with visually comparing two similar images.

Metrical results

To generate metrics about the differences of two images, you can run a command like this:

compare                               \
  -metric rmse                        \
   https://i.sstatic.net/GBax7.png \
   https://i.sstatic.net/D9IAV.jpg \
   null:

rmse is the acronym for root mean squared error. The result of the above example images gives:

 1339.53 (0.02044)

How many different metric methods are supported?

The following command enumerates all available comparison metrics methods on a given system, for the current version of ImageMagick:

compare -list metric 

On my notebook, these are:

AE Fuzz MAE MEPP MSE NCC PAE PHASH PSNR RMSE

The meanings of these abbreviations are:

AE     absolute error count, number of different pixels (`-fuzz` effected)
FUZZ   mean color distance
MAE    mean absolute error (normalized), average channel error distance
MEPP   mean error per pixel (normalized mean error, normalized peak error)
MSE    mean error squared, average of the channel error squared
NCC    normalized cross correlation
PAE    peak absolute (normalized peak absolute)
PHASH  perceptual hash
PSNR   peak signal to noise ratio
RMSE   root mean squared (normalized root mean squared)

An interesting (and relatively recent) metric is phash ('perceptual hash'). It is the only one that does not require identical dimensions for the two compared images. It really is the best 'metric' to narrow down similarly looking images (or at least to reliably exclude these image pairs which look very different) without really "looking at them", on the command line and programatically.

One good experiment to run is when you compare an image to itself. It shows how the respective metric translates 'identity' into its own environment:

for i in $(compare -list metric); do     \
    compare                              \
     -metric $i                          \
      https://i.sstatic.net/GBax7.png \
      https://i.sstatic.net/GBax7.png \
      null:                              \
done

This is the result:

AE    :   0
Fuzz  :   0 (0)
MAE   :   0 (0)
MEPP  :   0 (0, 0)
MSE   :   0 (0)
NCC   :   1.00001
PAE   :   0 (0)
PHASH :   0
PSNR  :   inf
RMSE :    0 (0)

As you can see, every single metric method returns 0, apart from two: PSNR returns infinity and NCC returns 1.00001.

Run this same command and compare a pure white patch of 100x100 pixels to a pure black one:

for i in $(compare -list metric); do \
  compare                            \
     -metric $i                      \
     -size 100x100                   \
      xc:white                       \
      xc:black                       \
      null:                          \
done

This returns the following result:

AE    :   10000
Fuzz  :   65535 (1)
MAE   :   65535 (1)
MEPP  :   1.96605e+09 (1.00003, 1)
MSE   :   65535 (1)
NCC   :   0
PAE   :   65535 (1)
PHASH :   417.941
PSNR  :   0
RMSE  :   65535 (1)

The AE metric is as expected: 10000 pixels (from -size 100x100) are different. Most other results are also easy to understand once you read up and grokked what the respective metric definitions mean....

Finally, let's look at the output of each available metric when comparing the top two images above (PNG and JPEG):

for i in $(compare -list metric); do     \
    compare -metric $i                   \
      https://i.sstatic.net/GBax7.png \
      https://i.sstatic.net/D9IAV.jpg \
      null:                              \
done

AE    :   74200
Fuzz  :   1339.53 (0.02044)
MAE   :   499.997 (0.00762946)
MEPP  :   2.07206e+08 (0.000417654, 0.435294)
MSE   :   27.3801 (0.000417793)
NCC   :   0.99709
PAE   :   28527 (0.435294)
PHASH :   0.745389
PSNR  :   33.7904
RMSE  :   1339.53 (0.02044)

Now pick your metric! :-)

Divaricate answered 16/1, 2015 at 1:56 Comment(2)
Great answer. And when I searched their website: imagemagick.org I found also C API, so this will fit perfectly into my project.Sunfast
Thank you in particular for showing how to compute the comparison metric without saving the comparison image.Glottalized

© 2022 - 2024 — McMap. All rights reserved.