Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: chrome/test/functional/ispy/image_tools.py

Issue 16855010: Python Tools for Pixel-by-Pixel Image Comparison (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: modified comments and fixed split code paths Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/test/functional/ispy/image_tools_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2013 The Chromium Authos. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Utilities for performing pixel-by-pixel image comparision."""
6
7 import itertools
8 import PIL
9 from PIL import Image
10
11
12 def _AreTheSameSize(images):
13 """Returns whether a set of images are the size size.
14
15 Args:
16 images: a list of images to compare.
17
18 Returns:
19 boolean.
20
21 Raises:
22 Exception: One image or fewer is passed in.
23 """
24 if len(images) > 1:
25 return all(images[0].size == img.size for img in images[1:])
26 else:
27 raise Exception('No images passed in.')
28
29
30 def _GetDifferenceWithMask(image1, image2, mask=None,
31 masked_color=(0, 0, 0),
32 same_color=(0, 0, 0),
33 different_color=(255, 255, 255)):
34 """Returns an image representing the difference between the two images.
35
36 This function computes the difference between two images taking into
37 account a mask if it is provided. The final three arguments represent
38 the coloration of the generated image.
39
40 Args:
41 image1: the first image to compare.
42 image2: the second image to compare.
43 mask: an optional mask image consisting of only black and white pixels
44 where white pixels indicate the portion of the image to be masked out.
45 masked_color: the color of a masked section in the resulting image.
46 same_color: the color of an unmasked section that is the same.
47 between images 1 and 2 in the resulting image.
48 different_color: the color of an unmasked section that is different
49 between images 1 and 2 in the resulting image.
50
51 Returns:
52 an image repesenting the difference between the two images.
53
54 Raises:
55 Exception: if image1, image2, and mask are not the same size.
56 """
57 image_mask = mask
58 if not mask:
59 image_mask = PIL.Image.new('RGB', image1.size, (0, 0, 0))
60 if not _AreTheSameSize([image1, image2, image_mask]):
61 raise Exception('images and mask must be the same size.')
62 image_diff = PIL.Image.new('RGB', image1.size, (0, 0, 0))
63 data = [
64 masked_color if m == (255, 255, 255)
65 else same_color if px1 == px2
66 else different_color
67 for m, px1, px2 in itertools.izip(image_mask.getdata(),
68 image1.getdata(),
69 image2.getdata())
70 ]
71 image_diff.putdata(data)
72 return image_diff
73
74
75 def CreateMask(images):
76 """Computes a mask for a set of images.
77
78 Returns a difference mask that is computed from the images
79 which are passed in. The mask will have a white pixel
80 anywhere that the input images differ and a black pixel
81 everywhere else.
82
83 Args:
84 images: the images to compute the mask from.
85
86 Returns:
87 an image of only black and white pixels where white pixels represent
88 areas in the input images that have differences.
89
90 Raises:
91 Exception: if the images passed in are not of the same size.
92 Exception: if fewer than two images are passed in.
93 """
94 if len(images) < 2:
95 raise Exception('mask must be created from two or more images.')
96 mask = Image.new('RGB', images[0].size, (0, 0, 0))
97 image = images[0]
98 for other_image in images[1:]:
99 mask = _GetDifferenceWithMask(
100 image,
101 other_image,
102 mask,
103 masked_color=(255, 255, 255))
104 return mask
105
106
107 def VisualizeImageDifferences(image1, image2, mask=None):
108 """Returns an image repesenting the unmasked differences between two images.
109
110 Iterates through the pixel values of two images and an optional
111 mask. If the pixel values are the same, or the pixel is masked,
112 (0,0,0) is stored for that pixel. Otherwise, (255,255,255) is stored.
113 This ultimately produces an image where unmasked differences between
114 the two images are white pixels, and everything else is black.
115
116 Args:
117 image1: an RGB image
118 image2: another RGB image of the same size as image1.
119 mask: an optional RGB image consisting of only white and black pixels
120 where the white pixels represent the parts of the images to be masked
121 out.
122
123 Returns:
124 a black and white image representing the unmasked difference between
125 the two input images.
126
127 Raises:
128 Exception: if the two images and optional mask are different sizes.
129 """
130 return _GetDifferenceWithMask(image1, image2, mask)
131
132
133 def TotalDifferentPixels(image1, image2, mask=None):
134 """Computes the number of different pixels between two images.
135
136 Args:
137 image1: the first RGB image to be compared.
138 image2: the second RGB image to be compared.
139 mask: an optional RGB image of only black and white pixels
140 where white pixels indicate the parts of the image to be masked out.
141
142 Returns:
143 the number of differing pixels between the images.
144
145 Raises:
146 Exception: if the images to be compared and the mask are not the same size.
147 """
148 image_mask = mask
149 if not mask:
150 image_mask = PIL.Image.new('RGB', image1.size, (0, 0, 0))
151 if _AreTheSameSize([image1, image2, image_mask]):
152 return sum(
153 0 if m == (255, 255, 255)
frankf 2013/06/19 03:53:26 This is unreadable
cgrimm 2013/06/19 16:17:26 changed this and another instance of the same patt
154 else 1 if px1 != px2
155 else 0
156 for px1, px2, m in itertools.izip(
157 image1.getdata(),
158 image2.getdata(),
159 image_mask.getdata()
160 )
161 )
162 else:
163 raise Exception('images and mask must be the same size')
164
165
166 def SameImage(image1, image2, max_different_pixels=0, mask=None):
167 """Returns a boolean representing whether the images are the same.
168
169 Returns a boolean indicating whether two images are similar
170 enough to be considered the same. Essentially wraps the
171 TotalDifferentPixels function and adds a max_different_pixels
172 cutoff for whether or not the images are the same.
173
174 Args:
175 image1: an RGB image to compare.
176 image2: an RGB image to compare.
177 max_different_pixels: a number that is the cutoff for whether or not
frankf 2013/06/19 03:53:26 I thought we decided to remove this.
cgrimm 2013/06/19 16:17:26 Done.
178 the two images are the same.
179 mask: an optional image of only black and white pixels where white pixels
180 are masked out.
181
182 Returns:
183 True if the images are similar, False otherwise.
184
185 Raises:
186 Exception: if the images (and mask) are different sizes.
187 """
188 different_pixels = TotalDifferentPixels(image1, image2, mask)
189 return different_pixels <= max_different_pixels
OLDNEW
« no previous file with comments | « no previous file | chrome/test/functional/ispy/image_tools_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698