| 1 | #!/usr/bin/env python |
|---|
| 2 | |
|---|
| 3 | import sys |
|---|
| 4 | import png |
|---|
| 5 | import array |
|---|
| 6 | |
|---|
| 7 | def usage(): |
|---|
| 8 | return """%s <file1.png> <file2.png>""" % sys.argv[0] |
|---|
| 9 | |
|---|
| 10 | def rgb2rgba(px, w, h): |
|---|
| 11 | npx = array.array('B') |
|---|
| 12 | nbytes = w * h * 3 |
|---|
| 13 | i = 0 |
|---|
| 14 | while i < nbytes: |
|---|
| 15 | npx.append(px[i]) |
|---|
| 16 | npx.append(px[i+1]) |
|---|
| 17 | npx.append(px[i+2]) |
|---|
| 18 | npx.append(255) |
|---|
| 19 | i = i + 3 |
|---|
| 20 | return npx |
|---|
| 21 | |
|---|
| 22 | class Buffer: |
|---|
| 23 | def __init__(self, w, h, px, format): |
|---|
| 24 | self.width = w |
|---|
| 25 | self.height = h |
|---|
| 26 | if format == "rgb": |
|---|
| 27 | self.pixels = rgb2rgba(px, w, h) |
|---|
| 28 | else: |
|---|
| 29 | self.pixels = px |
|---|
| 30 | self.bpp = 4 # bytes per pixel |
|---|
| 31 | |
|---|
| 32 | def npixels(self): |
|---|
| 33 | return self.width * self.height |
|---|
| 34 | |
|---|
| 35 | def nbytes(self): |
|---|
| 36 | return self.npixels() * self.bpp |
|---|
| 37 | |
|---|
| 38 | def compute_histogram(self): |
|---|
| 39 | hists = [[0 for x in xrange(256)] for y in range(self.bpp)] |
|---|
| 40 | nbytes = self.nbytes() |
|---|
| 41 | px = self.pixels |
|---|
| 42 | stride = self.bpp |
|---|
| 43 | imsize = self.nbytes() |
|---|
| 44 | for channel in range(self.bpp): |
|---|
| 45 | hist = hists[channel] |
|---|
| 46 | i = channel |
|---|
| 47 | while i < imsize: |
|---|
| 48 | value = px[i] |
|---|
| 49 | hist[value] += 1 |
|---|
| 50 | i = i + stride |
|---|
| 51 | return hists |
|---|
| 52 | |
|---|
| 53 | def rgba_mse(b1, b2, offset): |
|---|
| 54 | imsize = b1.nbytes() |
|---|
| 55 | mse = 0.0 |
|---|
| 56 | p1 = b1.pixels |
|---|
| 57 | p2 = b2.pixels |
|---|
| 58 | stride = b1.bpp |
|---|
| 59 | while offset < imsize: |
|---|
| 60 | diff = p1[offset] - p2[offset] |
|---|
| 61 | mse += diff * diff |
|---|
| 62 | offset += stride |
|---|
| 63 | return mse / b1.npixels() |
|---|
| 64 | |
|---|
| 65 | def msecompare(b1, b2): |
|---|
| 66 | return [rgba_mse(b1, b2, c) for c in xrange(4)] |
|---|
| 67 | |
|---|
| 68 | def histcompare(b1, b2): |
|---|
| 69 | h1 = b1.compute_histogram() |
|---|
| 70 | h2 = b2.compute_histogram() |
|---|
| 71 | diffs = [0, 0, 0, 0] |
|---|
| 72 | for c in xrange(len(h1)): |
|---|
| 73 | c1 = h1[c] |
|---|
| 74 | c2 = h2[c] |
|---|
| 75 | d = diffs[c] |
|---|
| 76 | for v in xrange(len(c1)): |
|---|
| 77 | d = d + abs(c1[v] - c2[v]) |
|---|
| 78 | diffs[c] = d |
|---|
| 79 | return diffs |
|---|
| 80 | |
|---|
| 81 | def format_from_pngmetadata(m): |
|---|
| 82 | if m['has_alpha']: |
|---|
| 83 | return "rgba" |
|---|
| 84 | else: |
|---|
| 85 | return "rgb" |
|---|
| 86 | |
|---|
| 87 | if __name__ == "__main__": |
|---|
| 88 | if len(sys.argv) < 3: |
|---|
| 89 | print usage() |
|---|
| 90 | sys.exit(1) |
|---|
| 91 | |
|---|
| 92 | file1 = sys.argv[1] |
|---|
| 93 | file2 = sys.argv[2] |
|---|
| 94 | w1, h1, p1, m1 = png.Reader(file1).read() |
|---|
| 95 | w2, h2, p2, m2 = png.Reader(file2).read() |
|---|
| 96 | b1 = Buffer(w1, h1, p1, format_from_pngmetadata(m1)) |
|---|
| 97 | b2 = Buffer(w2, h2, p2, format_from_pngmetadata(m2)) |
|---|
| 98 | print msecompare(b1, b2) |
|---|
| 99 | print histcompare(b1, b2) |
|---|