The root mean square difference between two images using Python and PIL

I need to have a function like the one found here: http://effbot.org/zone/pil-comparing-images.htm , which calculates the rms difference between the two images. The code is as follows:

import ImageChops import math, operator def rmsdiff(im1, im2): "Calculate the root-mean-square difference between two images" h = ImageChops.difference(im1, im2).histogram() # calculate rms return math.sqrt(reduce(operator.add, map(lambda h, i: h*(i**2), h, range(256)) ) / (float(im1.size[0]) * im1.size[1])) 

Attempting to run this code results in the following error: TypeError: unsupported operand type for ** or pow (): "NoneType" and "int". What is the reason for this?

+4
source share
5 answers

The problem is that it creates a histogram that has no values ​​(or indeed None values), where there is no corresponding pixel value.

i.e. when you find the difference of two images, the resulting image does not have pixels that are, say, at a distance of 43 units, so h [43] = None.

Later, you try to access the number of pixels at each brightness in the range (256) and put it in a square, which makes him wonder what None ** 2 should have.

Consider changing range(256) to h.keys() .

In addition, you use h to mean two different things, think about changing the name of one or, better yet, both of them for meaningful names.

+4
source

Wild guess here, but try this on your last line and see if it works:

 return math.sqrt(sum(h*(i**2) for i, h in enumerate(h))) / (float(im1.size[0]) * im1.size[1])) 

I'm not sure why you got the TypeError that you are describing, but if you use the line of code above and keep getting it, something seriously strange is happening.

+2
source

It seems that map and reduce are not needed here.

An improved version of rmsdiff could be:

 def rmsdiff(im1, im2): "Calculate the root-mean-square difference between two images" diff = ImageChops.difference(im1, im2) h = diff.histogram() sq = (value*((idx%256)**2) for idx, value in enumerate(h)) sum_of_squares = sum(sq) rms = math.sqrt(sum_of_squares/float(im1.size[0] * im1.size[1])) return rms 

Here is the source. The improvement proposed by Mark Krautheim is important for at least one reason in accordance with my tests: contrary to the original version, it leads to a return of 0.0 when comparing the image with itself.

+2
source

See here https://gist.github.com/bo858585/5377492 . This script divides all jpg images from the user's directory (specify it) into groups by their similarity using the rms value (without dividing by sqrt (3) - pixel - RGB vector with 3 numbers) the differences between each pair are corresponding (according to the position in the matrix 20 * 20) pixels of two comparing images. The script summarize these distances between pairs of pixels and divide this sum by the maximum possible distance - this way the script gets the role of similarity between the two images. Before comparing all images with a resolution of 20 * 20. You can change MAX_DISTANCE (from 0 to 400), and the script will group more or less similar images into one group.

0
source

Consider resolving this issue with an existing solution such as scikit-image :

 from PIL import Image # No need for ImageChops import math from skimage import img_as_float from skimage.measure import compare_mse as mse def rmsdiff(im1, im2): """Calculates the root mean square error (RSME) between two images""" return math.sqrt(mse(img_as_float(im1), img_as_float(im2))) 

Alternatively write something short using NumPy :

 from PIL import Image, ImageChops import math import numpy as np def rmsdiff(im1, im2): """Calculates the root mean square error (RSME) between two images""" errors = np.asarray(ImageChops.difference(im1, im2)) / 255 return math.sqrt(np.mean(np.square(errors))) 

Note that both methods handle pixel intensities, as in the range [0.0, 1.0], and not [0, 255].

0
source

Source: https://habr.com/ru/post/1313592/


All Articles