+import subprocess
+import sys
+import time
+
+import Image
+import ImageChops
+import ImageEnhance
+
+
+class Comparer:
+ '''Image comparer.'''
+
+ def __init__(self, ref_image, src_image, alpha = False):
+ self.ref_im = Image.open(ref_image)
+ self.src_im = Image.open(src_image)
+
+ # Crop to the minimum size
+ ref_w, ref_h = self.ref_im.size
+ src_w, src_h = self.src_im.size
+ w = min(ref_w, src_w)
+ h = min(ref_h, src_h)
+ self.ref_im = self.ref_im.crop((0, ref_h - h, w, ref_h))
+ self.src_im = self.src_im.crop((0, src_h - h, w, src_h))
+
+ # Ignore alpha
+ if not alpha:
+ self.ref_im = self.ref_im.convert('RGB')
+ self.src_im = self.src_im.convert('RGB')
+
+ self.diff = ImageChops.difference(self.src_im, self.ref_im)
+
+ def write_diff(self, diff_image, fuzz = 0.05):
+ # make a difference image similar to ImageMagick's compare utility
+ mask = ImageEnhance.Brightness(self.diff).enhance(1.0/fuzz)
+ mask = mask.convert('L')
+
+ lowlight = Image.new('RGB', self.src_im.size, (0xff, 0xff, 0xff))
+ highlight = Image.new('RGB', self.src_im.size, (0xf1, 0x00, 0x1e))
+ diff_im = Image.composite(highlight, lowlight, mask)
+
+ diff_im = Image.blend(self.src_im, diff_im, 0xcc/255.0)
+ diff_im.save(diff_image)
+
+ def precision(self):
+ # See also http://effbot.org/zone/pil-comparing-images.htm
+ h = self.diff.histogram()
+ square_error = 0
+ for i in range(1, 256):
+ square_error += sum(h[i : 3*256: 256])*i*i
+ rel_error = float(square_error*2 + 1) / float(self.diff.size[0]*self.diff.size[1]*3*255*255*2)
+ bits = -math.log(rel_error)/math.log(2.0)
+ return bits
+
+ def ae(self, chantol = 4, pixeltol = 0.03):
+ # Compute absolute error
+ # chantol = color channel tolerance
+ # pixeltol = ratio of pixels we allow to go completely off
+
+ # TODO: this is approximate due to the grayscale conversion
+ h = self.diff.convert('L').histogram()
+
+ ae = sum(h[int(chantol) + 1 : 256])
+
+ return ae <= pixeltol*self.diff.size[0]*self.diff.size[1]