Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 that calculates the root mean square difference between two images. The code looks like this:

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]))

Trying to run this code leads to the following error: TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'. That's the matter with it?

like image 474
pythoncoder Avatar asked Jun 23 '10 01:06

pythoncoder


3 Answers

The problem is that it is creating a histogram that has no values (or really None values) where there is no corresponding pixel value.

i.e. when you are finding the diff of the two images, the resulting image doesn't have any pixels that are, say, 43 units apart, so h[43] = None.

Later, you try to access the number of pixels at each brightness in the range(256), and square it, which is causing it to get confused about what None**2 should be.

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

Also, you are using h to mean two different things, consider changing the name of one or, better still, both of them to meaningful names.

like image 171
Oddthinking Avatar answered Nov 15 '22 00:11

Oddthinking


It seems that map and reduce are not really 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 suggested by Mark Krautheim is important for at least one reason according to my tests: contrary to the original version, it leads to a return value of 0.0 when comparing an image with itself.

like image 45
Tan Avatar answered Nov 14 '22 22:11

Tan


Wild guess here, but try this in 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 offhand why you'd get the TypeError you're describing, but if you use the above line of code and continue to get it, something seriously weird is going on.

like image 36
David Z Avatar answered Nov 14 '22 22:11

David Z