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?
- It used the first image as a pale background.
- 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! :-)