0

I'm using ImageChops to check if two images are different by doing:

currentPic = Image.open('current.png').convert('RGB')
previousPic = Image.open('previous.png').convert('RGB')
diff = ImageChops.difference(currentPic, previousPic)
if diffUserHome.getbbox():
   print("Images are different herey !!")

I was trying to modify this code to check if the two images are the same. Do you know a way, for me to check if two pictures are exactly the same using the ImageChops library?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Chief Madog
  • 1,738
  • 4
  • 28
  • 55
  • This might help you: https://stackoverflow.com/a/12175980/12070811 –  Apr 25 '21 at 14:23
  • Thanks @sfacky for the comment, but this computes hash value of an image, it would only work if they where the same file, not if they look the same or have 0 pixel difference – Chief Madog Apr 25 '21 at 15:11
  • 1
    You need to be clearer about what you mean by being the same. Is a 64x80 solid black GIF the same as 64x80 solid black PNG? They will have different checksums. Is a 64x80 solid black 8-bit PNG the same as a 64x80 solid black 16-bit PNG? What about a sold black palettised PNG? Is a 64x80 solid black PNG the same as a 64x80 solid black PNG created by exactly the same code but with a timestamp in the metadata 1 second later? – Mark Setchell Apr 25 '21 at 15:27
  • hi @Mark Setchell i need to check if it is the same pixels. if you want to be more specific.and that there isn't different pixels inside it – Chief Madog Apr 25 '21 at 15:47
  • The answer I linked is the 2nd one, and it hashes the pixel data, not the file –  Apr 26 '21 at 09:23

3 Answers3

1

You can use Image.getdata() to get all the pixel values of the difference image. Casting these to a NumPy array makes it easy to sum over all the pixel values. That sum is zero if and only if all the pixel values between the original two images are the same, because the pixel values cannot be negative.

from PIL import Image, ImageChops
import numpy as np

def are_same_image(image1, image2):
    if np.sum(np.array(ImageChops.difference(image1, image2).getdata())) == 0:
        return True
    return False
Arne
  • 9,990
  • 2
  • 18
  • 28
0

ImageChops.difference() function creates a new image for you by subtracting the two images pixel by pixel from each other. If the resulting image is completely black, the images you extracted are exactly the same. This is because the rgb value of black is {0,0,0}. Image.getbbox() function calculates bounding boxes of non-zero regions in the image. This function returns 0 if it cannot find a non-zero point. Using these, a simple function can be made as follows.

from PIL import Image, ImageChops

def isDiffirent(current,previous):
    diff = ImageChops.difference(current,previous)
    if diff.getbbox():
        return 1
    else:
        return 0
kozmoonot
  • 56
  • 4
0

I won't use ImageChops.difference here, since it can't handle different image modes, cf.

from PIL import Image, ImageChops

# Read images
img1 = Image.open('image.png').convert('L')
img2 = Image.open('image.png').convert('L').convert('F')

diff = ImageChops.difference(img1, img2)

Although both images are identical (w.r.t. the pixels' intensities), we get the following ValueError:

Traceback (most recent call last):
  File "...", line 7, in <module>
    diff = ImageChops.difference(img1, img2)
  File "...\lib\site-packages\PIL\ImageChops.py", line 102, in difference
    return image1._new(image1.im.chop_difference(image2.im))
ValueError: images do not match

In general, I'd agree to use NumPy's vectorization abilities to speed up calculations, but there also might be this quite simple Pillow only approach:

  1. Check number of bands. If they don't match, images must be different.
  2. Manually calculate the absolute intensity differences for each pixel (i.e. what ImageChops.difference actually does), but be sure to support any two image modes. This is slightly different for single and multi channel images.
  3. Sum differences over all pixels as suggested before. If that sum is greater than 0, images must be different.

That'd be my code:

from PIL import Image

# Read images
img1 = Image.open('path/to/your/image.png').convert('RGB')
img2 = Image.open('path/to/your/image.png').convert('RGB')

# Check for different number of channels
if img1.im.bands != img2.im.bands:
    print('Images are different; number of channels do not match.')
    exit(-1)

# Get image (pixel) data
imdata1 = list(img1.getdata())
imdata2 = list(img2.getdata())

# Calculate pixel-wise absolute differences, and sum those differences
diff = 0
if img1.im.bands == 1:
    diff = sum([abs(float(i1) - float(i2)) for i1, i2 in zip(imdata1, imdata2)])
else:
    diff = sum([abs(float(i1) - float(i2)) for i1, i2 in
                zip([i for p in imdata1 for i in p],
                    [i for p in imdata2 for i in p])])
if diff > 0:
    print('Images are different; pixel-wise difference > 0.')
    exit(-1)

print('Images are the same.')

For some image, the code as-is will return:

Images are the same.

Also, for the mentioned case in the beginning, we'll get this output. Nevertheless, for some input like

img1 = Image.open('image.png').convert('L')
img2 = Image.open('image.png').convert('F')

the output most likely will be:

Images are different; pixel-wise difference > 0.

The direct conversion to mode F will result in some fractional parts for the single intensity values, such there's a difference to plain converting to mode L.

Please let me know, if you have use-cases, for which that code fails. I'm curious, if I missed some edge cases here!

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
PyCharm:       2021.1.1
Pillow:        8.2.0
----------------------------------------
HansHirse
  • 18,010
  • 10
  • 38
  • 67