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

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: added separator between user and system libraries 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 to take into consideration.
44 masked_color: the color of a masked section in the resulting image.
45 same_color: the color of an unmasked section that is the same.
46 between images 1 and 2 in the resulting image.
47 different_color: the color of an unmasked section that is different
48 between images 1 and 2 in the resulting image.
49
50 Returns:
51 an image repesenting the difference between the two images.
52
53 Raises:
54 Exception: if image1, image2, and mask are not the same size.
55 """
56 if mask:
57 if not _AreTheSameSize([image1, image2, mask]):
58 raise Exception('images and mask must be the same size.')
59 image_diff = PIL.Image.new('RGB', image1.size, (0, 0, 0))
60 data = list(
craigdh 2013/06/18 19:16:11 use [] instead of list(). That will result in this
cgrimm 2013/06/18 20:21:01 Done.
61 masked_color if m == (255, 255, 255)
62 else same_color if px1 == px2
63 else different_color
64 for m, px1, px2 in itertools.izip(mask.getdata(),
65 image1.getdata(),
66 image2.getdata())
67 )
68 image_diff.putdata(data)
69 return image_diff
70 else:
71 if not _AreTheSameSize([image1, image2]):
72 raise Exception('images must be the same size.')
73 image_diff = PIL.Image.new('RGB', image1.size, (0, 0, 0))
74 data = list(
craigdh 2013/06/18 19:16:11 ditto
cgrimm 2013/06/18 20:21:01 Done.
75 same_color if px1 == px2
76 else different_color
77 for px1, px2 in itertools.izip(image1.getdata(),
78 image2.getdata())
79 )
80 image_diff.putdata(data)
81 return image_diff
82
83
84 def CreateMask(images):
85 """Computes a mask for a set of images.
86
87 Returns an image that is computed from the images
88 passed in. The mask is generated by continually
craigdh 2013/06/18 19:16:11 Just say the mask will have a white pixel anywhere
cgrimm 2013/06/18 20:21:01 Done.
89 finding the difference between two images in the list
90 with respect to a mask which is initialized as a black
91 image, then setting the value of the mask to the result.
92
93 Args:
94 images: the images to compute the mask from.
95
96 Returns:
97 an image that is a mask of the passed in images.
98
99 Raises:
100 Exception: if the images passed in are not of the same size.
101 Exception: if fewer than two images are passed in.
102 """
103 if len(images) < 2:
104 raise Exception('mask must be created from two or more images.')
105 mask = Image.new('RGB', images[0].size, (0, 0, 0))
106 image = images[0]
107 for other_image in images[1:]:
108 mask = _GetDifferenceWithMask(
109 image,
110 other_image,
111 mask,
112 masked_color=(255, 255, 255))
113 return mask
114
115
116 def VisualizeImageDifferences(image1, image2, mask=None):
117 """Returns an image repesenting the unmasked differences between two images.
118
119 Iterates through the pixel values of two images and an optional
120 mask. If the pixel values are the same, or the pixel is masked,
121 (0,0,0) is stored for that pixel. Otherwise, (255,255,255) is stored.
122 This ultimately produces an image where unmasked differences between
123 the two images are white pixels, and everything else is black.
124
125 Args:
126 image1: an Image
127 image2: another Image of the same size as image1.
128 mask: an optional mask that represents parts of the two
129 images to ignore when generating the difference image.
130
131 Returns:
132 a black and white image representing the unmasked difference between
133 the two input images.
134
135 Raises:
136 Exception: if the two images and optional mask are different sizes.
137 """
138 return _GetDifferenceWithMask(image1, image2, mask)
139
140
141 def TotalDifferentPixels(image1, image2, mask=None):
142 """Computes the number of different pixels between two images.
143
144 Args:
145 image1: the first RGB PIL.Image to be compared.
146 image2: the second RGB PIL.Image to be compared.
147 mask: an optional mask to occlude parts of the images from calculation.
craigdh 2013/06/18 19:16:11 Mention the format of the mask. Is it an image or
cgrimm 2013/06/18 20:21:01 Done.
148
149 Returns:
150 the number of differing pixels between the images.
151
152 Raises:
153 Exception: if the images to be compared and the mask are not the same size.
154 """
155 if mask:
craigdh 2013/06/18 19:16:11 Entirely different codepaths are being followed de
cgrimm 2013/06/18 20:21:01 Similar problem in _GetDifferenceWithMask, fixed b
156 if _AreTheSameSize([image1, image2, mask]):
157 return sum([
craigdh 2013/06/18 19:16:11 remove the []. No need to create a list in memory,
cgrimm 2013/06/18 20:21:01 Done.
158 0 if m == (255, 255, 255)
159 else 1 if px1 != px2
160 else 0
161 for px1, px2, m in itertools.izip(
162 image1.getdata(),
163 image2.getdata(),
164 mask.getdata()
165 )
166 ])
167 else:
168 raise Exception('images and mask must be the same size')
169 else:
170 if _AreTheSameSize([image1, image2]):
171 return sum([
craigdh 2013/06/18 19:16:11 ditto
cgrimm 2013/06/18 20:21:01 Done.
172 1 if px1 != px2
173 else 0
174 for px1, px2 in itertools.izip(
175 image1.getdata(),
176 image2.getdata()
177 )
178 ])
179 else:
180 raise Exception('images must be the same size')
181
182
183 def SameImage(image1, image2, max_different_pixels=0, mask=None):
184 """Returns a boolean representing whether the images are the same.
185
186 Returns a boolean indicating whether two images are similar
187 enough to be considered the same. Essentially wraps the
188 TotalDifferentPixels function and adds a max_different_pixels
189 cutoff for whether or not the images are the same.
190
191 Args:
192 image1: an RGB PIL.Image to compare.
193 image2: an RGB PIL.Image to compare.
194 max_different_pixels: a number that is the cutoff for whether or not
195 the two images are the same.
196 mask: an optional Image that occludes parts of the images from
197 the calculation.
198
199 Returns:
200 True if the images are similar, False otherwise.
201
202 Raises:
203 Exception: if the images (and mask) are different sizes.
204 """
205 different_pixels = TotalDifferentPixels(image1, image2, mask)
206 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