[Image-SIG] RMS difference patch
douglas at paradise.net.nz
Thu Jan 17 01:27:16 CET 2008
> I have a patch that adds an RMS Difference method to the PIL.
> Let me know if there is any interest in this. I originally wrote this to
> help implement a video stabilization filter. You could also use
> this to quantify how much a lossy compression algorithm distorts
> an image relative to some other compression algorithm. I thought
> others might find RMS Difference useful.
> This is a patch against the Python Imaging Library Imaging-1.1.6.
> This adds the method ImageChops.difference_rms(im1, im2) which
> returns a float of the RMS difference between the two given
> images, im1 and im2. This is about 10 times faster than doing it
> in Python. The RMS Difference can be used to detect and compare
> changes in images (say, for motion detection) or to compare the
> effect of different image operations. If the two images are
> exactly equal then difference_rms() will return 0.0. As the
> images diverge the RMS value returned will increase. It will
> always be a positive number. In general, the greater the
> difference between the two images then the greater the RMS
> difference will be.
Very good. It is something I could use.
But there may be trouble with integer overflow. You are summing squared
differences in a signed long, which wraps at 2**31. And:
>>> 2**31 / (255 * 255)
>>> 33025 ** 0.5
So won't comparing black and white 182 x 182 images will wrap the
integer into a negative value and cause a floating point error in the
square root. Slightly larger pictures will wrap it right round to 0, and
just give you a false answer.
Comparing pure black and white might seem capricious, but with larger
images the differnce doesn't need to be so great to overflow. For
example, with a 1024x1024 image:
>>> 2 ** (31-20) ** 0.5
A pixel difference of 10 across the 1 megapixel image will wrap the sum
So what can be done? Using an unsigned long would add a little bit of
room and take away the possibility of a negative sum, but it isn't much
The other ways would be to use a long long (uint64_t) or double to sum
in, both of which might be a bit slower. I think doubles can sum
integral values up to around 2**52 (or 53?). That means your black and
white images can be quite big:
>>> ((2**52) / (255*255)) ** 0.5
and the error is less painful (because what happens at that limit is
just that the limit + 1 equals the limit).
I haven't tested your code or anything, but this is a kind of problem I
have met in the past. I'd recommend you try doubles or longer ints and
see whether the slow down is tolerable. I suspect it will be, and that
you could offset it anyway by taking the "++ count" out of the loop and
count = imIn1->xsize * imIn1->ysize;
More information about the Image-SIG