thunderstorm
Posts: 8
Joined: Sat Sep 02, 2017 9:39 pm

Comparing two images using PIL

Wed Oct 11, 2017 11:37 pm

I'm trying to compare two images and get a number value. I googled and found this example https://stackoverflow.com/questions/192 ... -linux-way.

But I get a valueerror need more then 0 values to unpack. I'm not sure why. Thanks everyone

Code: Select all

import math, operator
from PIL import Image
def compare(file1, file2):
    image1 = Image.open("/home/pi/Desktop/1.jpg)
    image2 = Image.open("/home/pi/Desktop/2.jpg")
    h1 = image1.histogram()
    h2 = image2.histogram()
    rms = math.sqrt(reduce(operator.add,
                           map(lambda a,b: (a-b)**2, h1, h2))/len(h1))
    return rms

if __name__=='__main__':
    import sys
    file1, file2 = sys.argv[1:]
    print compare(file1, file2)

KnarfB
Posts: 158
Joined: Wed Dec 14, 2016 10:47 am
Location: Germany

Re: Comparing two images using PIL

Thu Oct 12, 2017 5:53 am

You are comparing the histograms of two images, not the images themselves. If you take a copy of one image, rotate it, mirror it or randomly permute all individual pixels, the histogram does not change and both images will eventually be classified as identical.

User avatar
OutoftheBOTS
Posts: 400
Joined: Tue Aug 01, 2017 10:06 am

Re: Comparing two images using PIL

Thu Oct 12, 2017 8:12 am

There is many ways to compare 2 images to find what's different, this is the base for motion detection.

For more advanced image processing the OpenCV library is much better than the standard PIL library

Motion detection basically wants to know where the difference is between the 2 images.

The very most basic motion detection works like this:

convert the 2 images to gray scale
blur the images slightly to remove fine detail
find the absolute difference between the images (the value between each corresponding pixel in each image)
threshold it so you only get the pixels that r more different than the threshold amount
errode away small lone pixels just to leave larger areas
what ever is left after this is the bits that have changed between the 2 images

User avatar
paddyg
Posts: 2005
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Comparing two images using PIL

Thu Oct 12, 2017 2:32 pm

For complicated things, or if you want to do more general image processing OpenCV is a must. But you can do quite a lot of image comparison things using numpy which has the advantage of already being installed on the RPi (and it actually faster for some things). But has the disadvantage of being a bit alien if you're not used to thinking in terms of multidimensional arrays. So for instance

Code: Select all

import numpy as np
from PIL import Image
def compare(file1, file2):
  im = [None, None] # to hold two arrays
  for i, f in enumerate([file1, file2]):
    im[i] = (np.array(Image.open(f)
                 .convert('L')            # convert to grayscale using PIL
                 .resize((32,32), resample=Image.BICUBIC)) # reduce size and smooth a bit using PIL
                 ).astype(np.int)   # convert from unsigned bytes to signed int using numpy
  return np.abs(im[0] - im[1]).sum() 
Although your code example using the histogram doesn't do it, you can compare images by taking the length of the vector from pixel a to matching pixel b in RGB space but it's generally better and much faster to convert to luminance as suggested by @OutoftheBOTS and use the absolute value of the difference.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

User avatar
OutoftheBOTS
Posts: 400
Joined: Tue Aug 01, 2017 10:06 am

Re: Comparing two images using PIL

Thu Oct 12, 2017 9:09 pm

Great response paddyg

You have done something very similar to my version using OpenCV but using a low level version.

I have a couple of question for you.

You resized to 32 x 32, I assume this is to remove the fine detail and average some pixels out similar to the blur part of my process??

I was also about to ask why did you convert from unsigned byte to signed int but then just realized that if you subtract the 2 then then the difference is possible to be negative before you take the ABS and sum up.

I must say that I am impressed with that simple little routine and will probably use it in the future myself :)

PiGraham
Posts: 2679
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Comparing two images using PIL

Thu Oct 12, 2017 9:27 pm

thunderstorm wrote:
Wed Oct 11, 2017 11:37 pm
I'm trying to compare two images and get a number value.
What comparison do you want to make?
Comparing histograms can be a good technique for similar images. It is relatively unaffected by scale or rotation and quick to compute, but it ignores details and what the image actually contains as objects etc.

If you want to spot difference between similar images that are not subject to scale or rotation pixel subtraction is often used. See Motion, a video security package that does this to spot movement in video.

If you want to check if an image has been edited you will need different techniques than if you want to know if two images both feature cats.

For the latter you might need something like TensorFlow to recognise objects.

User avatar
paddyg
Posts: 2005
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Comparing two images using PIL

Thu Oct 12, 2017 10:56 pm

@OutoftheBOTS, thanks. For a one-off comparison of two files speed is probably a secondary consideration after making the code understandable (which is a bit of an issue with numpy, especially long-winded lines like the above). Yes blurring was stretching a point (aka untrue!) but it is possible even though there isn't a 2D convolution.

Code: Select all

img[1:-1,1:-1] = (
     img[:-2,:-2] * 0.045 + img[:-2,1:-1] * 0.07 + img[:-2,2:] * 0.045 + 
     img[1:-1,:-2] * 0.07 + img[1:-1,1:-1] * 0.5 + img[1:-1,2:] * 0.07 +
     img[2:,:-2] * 0.045  + img[2:,1:-1] * 0.07  + img[2:,2:] * 0.045)
Almost certainly better to add the blurring in the PIL part of the import

Code: Select all

  im[i] = (np.array(Image.open(f)
                 .convert('L')            # convert to grayscale using PIL
                 .resize((32,32), resample=Image.BICUBIC) # reduce size and smooth a bit using PIL
                 .filter(ImageFilter.GaussianBlur(radius=2))) # blur using PIL
                 ).astype(np.int)   # convert from unsigned bytes to signed int using numpy
But at this point the line has become 150 characters long excluding comments and should definitely be broken into more digestible chunks.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

User avatar
OutoftheBOTS
Posts: 400
Joined: Tue Aug 01, 2017 10:06 am

Re: Comparing two images using PIL

Fri Oct 13, 2017 5:41 am

When PIL resized the image what method does it use??
If you resizing to 1/10th then does it just take every 10 pixel or does it average out those 10 pixels??
Because if during the resizing it is averaging then it won't be a need to blur as the fine detail has already been averaged out.

I need to get better at using and understanding Numpy arrays, when I need then for a bit fast processing of large data (usually images) I usually google around till I can find the needed bit of code and cut and paste.

User avatar
paddyg
Posts: 2005
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Comparing two images using PIL

Fri Oct 13, 2017 9:51 am

The method I used there was BICUBIC which is a form of weighted contribution that gives good results, and as you say, probably doesn't then need bluring. http://pillow.readthedocs.io/en/4.2.x/h ... pt-filters out of curiosity I did a quick comparison (below)

I think finding code snippets online, despite all the moaning of purists, is actually the best way to learn an awful lot of programming. As with this very thread, there is normally a specific issue and a lot of constructive discussion and criticism. Hence the rise and rise of stackoverflow!
Paddy
img_BILINEAR.jpg
img_BILINEAR.jpg (4.42 KiB) Viewed 430 times
img_NEAREST.jpg
img_NEAREST.jpg (5.99 KiB) Viewed 430 times
img_BICUBIC.jpg
img_BICUBIC.jpg (4.79 KiB) Viewed 430 times
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

knute
Posts: 302
Joined: Thu Oct 23, 2014 12:14 am

Re: Comparing two images using PIL

Fri Oct 13, 2017 8:36 pm

paddyg wrote:
Thu Oct 12, 2017 2:32 pm
Although your code example using the histogram doesn't do it, you can compare images by taking the length of the vector from pixel a to matching pixel b in RGB space but it's generally better and much faster to convert to luminance as suggested by @OutoftheBOTS and use the absolute value of the difference.
Any chance you could expound on the vector and luminance points? Maybe how to calculate luminance from RGB values?

Thanks.

User avatar
paddyg
Posts: 2005
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Comparing two images using PIL

Fri Oct 13, 2017 9:41 pm

Sure. Luminance is just another word for grayscale. i.e. it's an array with a single (0-255) value at each pixel x,y location, so 2D. With PIL you can produce this using Image.convert('L') as in the example I posted. (I did it as the first operation as it's very quick to do and reduces the work of the subsequent Image.resize() by two thirds)

If you already have your image in the form of a numpy ndarray you can convert it by

Code: Select all

      im = (im[:,:,:3] * [0.2989, 0.5870, 0.1140]).sum(axis=2)
which in words would be:
take a slice of the image using all the x and y values but only the first three values of the RGBA axis,
multiply the R value by 0.2989, G x 0.5870, B x 0.114 (which is a good approx to how our eyes perceive relative colours)
finally sum the values along the 3rd axis (axis numbers start at zero 0=>y, 1=>x, 2=>RGBA)

To find the difference between two images in RGB space you 'simply' use Pythagoras. i.e. square root of (red distance squared + green distance squared + blue distance squared). In numpy:

Code: Select all

  diff = ((im1[:,:,0] - im2[:,:,0]) ** 2 + (im1[:,:,1] - im2[:,:,1]) ** 2  + (im1[:,:,2] - im2[:,:,2]) ** 2) ** 0.5
  # diff.sum() # aggregates the total of differences
But the histogram code wasn't doing this as Image.histogram() produces a 1D array laid out as the number of pixels with red value 0 then 1 then 2...255 followed by the number of pixels with green value 0, 1, 2 etc
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

knute
Posts: 302
Joined: Thu Oct 23, 2014 12:14 am

Re: Comparing two images using PIL

Sat Oct 14, 2017 11:21 pm

paddyg wrote:
Fri Oct 13, 2017 9:41 pm
Sure. Luminance is just another word for grayscale. i.e. it's an array with a single (0-255) value at each pixel x,y location, so 2D. With PIL you can produce this using Image.convert('L') as in the example I posted. (I did it as the first operation as it's very quick to do and reduces the work of the subsequent Image.resize() by two thirds)

If you already have your image in the form of a numpy ndarray you can convert it by

Code: Select all

      im = (im[:,:,:3] * [0.2989, 0.5870, 0.1140]).sum(axis=2)
which in words would be:
take a slice of the image using all the x and y values but only the first three values of the RGBA axis,
multiply the R value by 0.2989, G x 0.5870, B x 0.114 (which is a good approx to how our eyes perceive relative colours)
finally sum the values along the 3rd axis (axis numbers start at zero 0=>y, 1=>x, 2=>RGBA)

To find the difference between two images in RGB space you 'simply' use Pythagoras. i.e. square root of (red distance squared + green distance squared + blue distance squared). In numpy:

Code: Select all

  diff = ((im1[:,:,0] - im2[:,:,0]) ** 2 + (im1[:,:,1] - im2[:,:,1]) ** 2  + (im1[:,:,2] - im2[:,:,2]) ** 2) ** 0.5
  # diff.sum() # aggregates the total of differences
But the histogram code wasn't doing this as Image.histogram() produces a 1D array laid out as the number of pixels with red value 0 then 1 then 2...255 followed by the number of pixels with green value 0, 1, 2 etc
So the conversion to grayscale does the same as multiplying the RGB values by .2989, .5870 and .114 respectively?

I wrote a motion detection program in Java. In that I just compare the R+G+B in 1% sized blocks of the images. That just measures total brightness of the block. I don't see that using the Pythagorean method would produce a different result as increasing the brightness of one color and reducing another would result in the same value if the colors were reversed. I was concerned that a color change but not a brightness change would not be detected.

You can see the code of that program at: http://knutejohnson.com/pi.

Thanks very much for taking the time to answer my question.

User avatar
OutoftheBOTS
Posts: 400
Joined: Tue Aug 01, 2017 10:06 am

Re: Comparing two images using PIL

Sun Oct 15, 2017 2:33 am

For comparing 2 images you wouldn't need to adjust brightness of 1 channel over another. When you convert to grey scale for human eyes to look at it you normally adjust the weighting of different channels to produce a grey scale that is what the human eye would see this is called the Luminance where if you don't weight the channels and you use the exact amount of light then this is called the radiance or intensity.

This is because the human eye sees different colour as different levels of brightness even they r the same level of light.

here's a video I made to help out some kids with there Lego robot. take a look at from 1:30 https://www.youtube.com/watch?v=RExMuQ29dUQ

Try this as an experiment get 3 pieces of paper, a red one, a blue one and a green one. Try to pick 3 pieces of paper that appear to your eye to be about the same brightness then use a colour sensor to read the actual amount of brightness of each paper and you will be surprised by the result.

knute
Posts: 302
Joined: Thu Oct 23, 2014 12:14 am

Re: Comparing two images using PIL

Sun Oct 15, 2017 3:44 pm

Thanks for the information and for posting the video.

Return to “Python”

Who is online

Users browsing this forum: No registered users and 6 guests