Detecting Image Equality at Different Resolutions

Detecting image equality at different resolutions

Interesting problem, btw :)

Slow-ish solution - excellent chance of success

Use a scale-invariant feature detector to find corresponding features in both images. If the features are matched with with a high score at similar locations, then you have your match.

I'd recommend SIFT which generates a scale & rotation invariant 128-integer descriptor for a feature found in an image. SURF (available in OpenCV) is another (faster) feature point detector.

You can match features across two images via bruteforce (compare each descriptor to a descriptor in the other image) which is O(n^2) but pretty fast (especially in the VL SIFT implementation). But if you need to compare the features in one image to several images (which you might have to) you should build a tree of the features to query it with the other image's features. K-D trees are useful, and OpenCV has a nice implementation.

Fast solution - might work

Downsample your high-res image to the low-res dimensions and use a similarity measure like SAD (where the sum of the differences between block of, say, 3x3 pixels around a pixel in both images is the score) to determine a match.

Comparing images with different resolutions

Two very simple perceptual hashing methods you might give a try before venturing into more complicated territory are based on the Discrete Cosine Transform and the local vs glocal mean of an image:

  1. Convert image to grayscale

    1.1 (EDIT) Make your image zero mean

  2. Crush your image down to thumbnail size, say [32x32]
  3. Run the two dimensional Discrete Cosine Transform
  4. Keep the top left [8 x 8], most significant low frequency components
  5. Binarize the block, based on the sign of the components
  6. Result is a 64 bit hash

And a variant on this theme would be

  1. Convert image to grayscale
  2. Optionally re-size to a predefined size.
  3. Partition the image in a fixed number of blocks
  4. Determine the global mean
  5. Determine the local mean per block
  6. For the hash, write out a 1 or a 0 per block, pending if the local
    mean was larger or smaller than the global mean.

Also, have a look at phash.

Find similar image if resolution was changed

Comparing two images for similarity is a general image processing problem so the solution you develop can be as simple or complex as you want it to be. In your specific case, you'll need a method for making two images the same size and a method for comparing the images.

First, you'll probably want to convert the images to RGB or grayscale arrays for comparison.

I would suggest reducing the size of the larger image to the size of the smaller image. That is less likely to introduce artifacts than increasing the size of the smaller image. Resizing can be accomplished with the Python Pillow library.

Image.resize(size, resample=None, box=None, reducing_gap=None)

https://pillow.readthedocs.io/en/stable/reference/Image.html

The resampling method may have some small effect on the similarity measure. However, you'll probably be fine just using resample = NEAREST.

After making sure the images are the same size, they must be compared. One could compare them using mean squared error (MSE) or structural similarity (SSIM). Luckily, SSIM is already implemented in scikit-image.

from skimage.metrics import structural_similarity as ssim

s = ssim(imageA, imageB)

https://www.pyimagesearch.com/2014/09/15/python-compare-two-images/?_ga=2.129476107.608689084.1632687977-376301729.1627364626

In your case, MSE might work just as well. However, if the average brightness of the image had been changed by some kind of process, you'd want to first subtract that from each image.

If resizing is the only issue, that should be it. If, however, the images may have been flipped, rotated, or cropped, additional steps might be necessary.

Can we programmatically compare different images of same resolutions?

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                              \
http://i.stack.imgur.com/GBax7.png \
http://i.stack.imgur.com/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                              \
http://i.stack.imgur.com/GBax7.png \
http://i.stack.imgur.com/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 \
http://i.stack.imgur.com/GBax7.png \
http://i.stack.imgur.com/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 \
http://i.stack.imgur.com/GBax7.png \
http://i.stack.imgur.com/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 \
http://i.stack.imgur.com/GBax7.png \
http://i.stack.imgur.com/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 \
http://i.stack.imgur.com/GBax7.png \
http://i.stack.imgur.com/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! :-)

detect screen resolution then compare

You can use CSS Media Queries

body {
width:100%;
}
@media all and (max-width: 800px) {
body {
width:1004px;
}
}


Related Topics



Leave a reply



Submit