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

Side by Side Diff: gm/rebaseline_server/imagediffdb.py

Issue 334533003: rebaseline_server: delete no-longer-used weightedDiffMeasure (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 6 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
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 """ 3 """
4 Copyright 2013 Google Inc. 4 Copyright 2013 Google Inc.
5 5
6 Use of this source code is governed by a BSD-style license that can be 6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file. 7 found in the LICENSE file.
8 8
9 Calulate differences between image pairs, and store them in a database. 9 Calulate differences between image pairs, and store them in a database.
10 """ 10 """
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
42 WHITEDIFFS_SUBDIR = 'whitediffs' 42 WHITEDIFFS_SUBDIR = 'whitediffs'
43 43
44 VALUES_PER_BAND = 256 44 VALUES_PER_BAND = 256
45 45
46 # Keys used within DiffRecord dictionary representations. 46 # Keys used within DiffRecord dictionary representations.
47 # NOTE: Keep these in sync with static/constants.js 47 # NOTE: Keep these in sync with static/constants.js
48 KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL = 'maxDiffPerChannel' 48 KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL = 'maxDiffPerChannel'
49 KEY__DIFFERENCES__NUM_DIFF_PIXELS = 'numDifferingPixels' 49 KEY__DIFFERENCES__NUM_DIFF_PIXELS = 'numDifferingPixels'
50 KEY__DIFFERENCES__PERCENT_DIFF_PIXELS = 'percentDifferingPixels' 50 KEY__DIFFERENCES__PERCENT_DIFF_PIXELS = 'percentDifferingPixels'
51 KEY__DIFFERENCES__PERCEPTUAL_DIFF = 'perceptualDifference' 51 KEY__DIFFERENCES__PERCEPTUAL_DIFF = 'perceptualDifference'
52 KEY__DIFFERENCES__WEIGHTED_DIFF = 'weightedDiffMeasure'
53 52
54 53
55 class DiffRecord(object): 54 class DiffRecord(object):
56 """ Record of differences between two images. """ 55 """ Record of differences between two images. """
57 56
58 def __init__(self, storage_root, 57 def __init__(self, storage_root,
59 expected_image_url, expected_image_locator, 58 expected_image_url, expected_image_locator,
60 actual_image_url, actual_image_locator, 59 actual_image_url, actual_image_locator,
61 expected_images_subdir=DEFAULT_IMAGES_SUBDIR, 60 expected_images_subdir=DEFAULT_IMAGES_SUBDIR,
62 actual_images_subdir=DEFAULT_IMAGES_SUBDIR, 61 actual_images_subdir=DEFAULT_IMAGES_SUBDIR,
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 except Exception: 109 except Exception:
111 logging.exception('unable to download actual_image_url %s to file %s' % 110 logging.exception('unable to download actual_image_url %s to file %s' %
112 (actual_image_url, actual_image_file)) 111 (actual_image_url, actual_image_file))
113 raise 112 raise
114 113
115 # Generate the diff image (absolute diff at each pixel) and 114 # Generate the diff image (absolute diff at each pixel) and
116 # max_diff_per_channel. 115 # max_diff_per_channel.
117 diff_image = _generate_image_diff(actual_image, expected_image) 116 diff_image = _generate_image_diff(actual_image, expected_image)
118 diff_histogram = diff_image.histogram() 117 diff_histogram = diff_image.histogram()
119 (diff_width, diff_height) = diff_image.size 118 (diff_width, diff_height) = diff_image.size
120 self._weighted_diff_measure = _calculate_weighted_diff_metric(
121 diff_histogram, diff_width * diff_height)
122 self._max_diff_per_channel = _max_per_band(diff_histogram) 119 self._max_diff_per_channel = _max_per_band(diff_histogram)
123 120
124 # Generate the whitediff image (any differing pixels show as white). 121 # Generate the whitediff image (any differing pixels show as white).
125 # This is tricky, because when you convert color images to grayscale or 122 # This is tricky, because when you convert color images to grayscale or
126 # black & white in PIL, it has its own ideas about thresholds. 123 # black & white in PIL, it has its own ideas about thresholds.
127 # We have to force it: if a pixel has any color at all, it's a '1'. 124 # We have to force it: if a pixel has any color at all, it's a '1'.
128 bands = diff_image.split() 125 bands = diff_image.split()
129 graydiff_image = ImageChops.lighter(ImageChops.lighter( 126 graydiff_image = ImageChops.lighter(ImageChops.lighter(
130 bands[0], bands[1]), bands[2]) 127 bands[0], bands[1]), bands[2])
131 whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND) 128 whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND)
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 def get_percent_pixels_differing(self): 179 def get_percent_pixels_differing(self):
183 """Returns the percentage of pixels that differ, as a float between 180 """Returns the percentage of pixels that differ, as a float between
184 0 and 100 (inclusive).""" 181 0 and 100 (inclusive)."""
185 return ((float(self._num_pixels_differing) * 100) / 182 return ((float(self._num_pixels_differing) * 100) /
186 (self._width * self._height)) 183 (self._width * self._height))
187 184
188 def get_perceptual_difference(self): 185 def get_perceptual_difference(self):
189 """Returns the perceptual difference percentage.""" 186 """Returns the perceptual difference percentage."""
190 return self._perceptual_difference 187 return self._perceptual_difference
191 188
192 def get_weighted_diff_measure(self):
193 """Returns a weighted measure of image diffs, as a float between 0 and 100
194 (inclusive).
195
196 TODO(epoger): Delete this function, now that we have perceptual diff?
197 """
198 return self._weighted_diff_measure
199
200 def get_max_diff_per_channel(self): 189 def get_max_diff_per_channel(self):
201 """Returns the maximum difference between the expected and actual images 190 """Returns the maximum difference between the expected and actual images
202 for each R/G/B channel, as a list.""" 191 for each R/G/B channel, as a list."""
203 return self._max_diff_per_channel 192 return self._max_diff_per_channel
204 193
205 def as_dict(self): 194 def as_dict(self):
206 """Returns a dictionary representation of this DiffRecord, as needed when 195 """Returns a dictionary representation of this DiffRecord, as needed when
207 constructing the JSON representation.""" 196 constructing the JSON representation."""
208 return { 197 return {
209 KEY__DIFFERENCES__NUM_DIFF_PIXELS: self._num_pixels_differing, 198 KEY__DIFFERENCES__NUM_DIFF_PIXELS: self._num_pixels_differing,
210 KEY__DIFFERENCES__PERCENT_DIFF_PIXELS: 199 KEY__DIFFERENCES__PERCENT_DIFF_PIXELS:
211 self.get_percent_pixels_differing(), 200 self.get_percent_pixels_differing(),
212 KEY__DIFFERENCES__WEIGHTED_DIFF: self.get_weighted_diff_measure(),
213 KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL: self._max_diff_per_channel, 201 KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL: self._max_diff_per_channel,
214 KEY__DIFFERENCES__PERCEPTUAL_DIFF: self._perceptual_difference, 202 KEY__DIFFERENCES__PERCEPTUAL_DIFF: self._perceptual_difference,
215 } 203 }
216 204
217 205
218 class ImageDiffDB(object): 206 class ImageDiffDB(object):
219 """ Calculates differences between image pairs, maintaining a database of 207 """ Calculates differences between image pairs, maintaining a database of
220 them for download.""" 208 them for download."""
221 209
222 def __init__(self, storage_root): 210 def __init__(self, storage_root):
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 271
284 Raises a KeyError if we don't have a DiffRecord for this image pair. 272 Raises a KeyError if we don't have a DiffRecord for this image pair.
285 """ 273 """
286 key = (_sanitize_locator(expected_image_locator), 274 key = (_sanitize_locator(expected_image_locator),
287 _sanitize_locator(actual_image_locator)) 275 _sanitize_locator(actual_image_locator))
288 return self._diff_dict[key] 276 return self._diff_dict[key]
289 277
290 278
291 # Utility functions 279 # Utility functions
292 280
293 def _calculate_weighted_diff_metric(histogram, num_pixels):
294 """Given the histogram of a diff image (per-channel diff at each
295 pixel between two images), calculate the weighted diff metric (a
296 stab at how different the two images really are).
297
298 TODO(epoger): Delete this function, now that we have perceptual diff?
299
300 Args:
301 histogram: PIL histogram of a per-channel diff between two images
302 num_pixels: integer; the total number of pixels in the diff image
303
304 Returns: a weighted diff metric, as a float between 0 and 100 (inclusive).
305 """
306 # TODO(epoger): As a wild guess at an appropriate metric, weight each
307 # different pixel by the square of its delta value. (The more different
308 # a pixel is from its expectation, the more we care about it.)
309 assert(len(histogram) % VALUES_PER_BAND == 0)
310 num_bands = len(histogram) / VALUES_PER_BAND
311 max_diff = num_pixels * num_bands * (VALUES_PER_BAND - 1)**2
312 total_diff = 0
313 for index in xrange(len(histogram)):
314 total_diff += histogram[index] * (index % VALUES_PER_BAND)**2
315 return float(100 * total_diff) / max_diff
316
317
318 def _max_per_band(histogram): 281 def _max_per_band(histogram):
319 """Given the histogram of an image, return the maximum value of each band 282 """Given the histogram of an image, return the maximum value of each band
320 (a.k.a. "color channel", such as R/G/B) across the entire image. 283 (a.k.a. "color channel", such as R/G/B) across the entire image.
321 284
322 Args: 285 Args:
323 histogram: PIL histogram 286 histogram: PIL histogram
324 287
325 Returns the maximum value of each band within the image histogram, as a list. 288 Returns the maximum value of each band within the image histogram, as a list.
326 """ 289 """
327 max_per_band = [] 290 max_per_band = []
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 405
443 Args: 406 Args:
444 expected_image_locator: locator string pointing at expected image 407 expected_image_locator: locator string pointing at expected image
445 actual_image_locator: locator string pointing at actual image 408 actual_image_locator: locator string pointing at actual image
446 409
447 Returns: already-sanitized locator where the diffs between expected and 410 Returns: already-sanitized locator where the diffs between expected and
448 actual images can be found 411 actual images can be found
449 """ 412 """
450 return "%s-vs-%s" % (_sanitize_locator(expected_image_locator), 413 return "%s-vs-%s" % (_sanitize_locator(expected_image_locator),
451 _sanitize_locator(actual_image_locator)) 414 _sanitize_locator(actual_image_locator))
OLDNEW
« no previous file with comments | « no previous file | gm/rebaseline_server/imagediffdb_test.py » ('j') | gm/rebaseline_server/static/constants.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698