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

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

Issue 117823008: Adding additional optional parameters to imagediffdb to make calling from Cluster Telemetry possibl… (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Created 7 years 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 """ 3 """
epoger 2013/12/20 19:46:37 Have you confirmed that its self-test still passes
rmistry 2013/12/20 20:00:04 Yes I ran it before and after my changes.
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 """
11 11
12 import contextlib 12 import contextlib
13 import logging 13 import logging
14 import os 14 import os
15 import shutil 15 import shutil
16 import urllib 16 import urllib
17 try: 17 try:
18 from PIL import Image, ImageChops 18 from PIL import Image, ImageChops
19 except ImportError: 19 except ImportError:
20 raise ImportError('Requires PIL to be installed; see ' 20 raise ImportError('Requires PIL to be installed; see '
21 + 'http://www.pythonware.com/products/pil/') 21 + 'http://www.pythonware.com/products/pil/')
22 22
23 IMAGE_SUFFIX = '.png' 23 DEFAULT_IMAGE_SUFFIX = '.png'
24 DEFAULT_IMAGES_SUBDIR = 'images'
24 25
25 IMAGES_SUBDIR = 'images'
26 DIFFS_SUBDIR = 'diffs' 26 DIFFS_SUBDIR = 'diffs'
27 WHITEDIFFS_SUBDIR = 'whitediffs' 27 WHITEDIFFS_SUBDIR = 'whitediffs'
28 28
29 VALUES_PER_BAND = 256 29 VALUES_PER_BAND = 256
30 30
31 31
32 class DiffRecord(object): 32 class DiffRecord(object):
33 """ Record of differences between two images. """ 33 """ Record of differences between two images. """
34 34
35 def __init__(self, storage_root, 35 def __init__(self, storage_root,
36 expected_image_url, expected_image_locator, 36 expected_image_url, expected_image_locator,
37 actual_image_url, actual_image_locator): 37 actual_image_url, actual_image_locator,
38 images_subdir=DEFAULT_IMAGES_SUBDIR,
39 actual_images_subdir=DEFAULT_IMAGES_SUBDIR,
40 use_diff_image_locator=True,
41 image_suffix=DEFAULT_IMAGE_SUFFIX):
38 """Download this pair of images (unless we already have them on local disk), 42 """Download this pair of images (unless we already have them on local disk),
39 and prepare a DiffRecord for them. 43 and prepare a DiffRecord for them.
40 44
41 TODO(epoger): Make this asynchronously download images, rather than blocking 45 TODO(epoger): Make this asynchronously download images, rather than blocking
42 until the images have been downloaded and processed. 46 until the images have been downloaded and processed.
43 47
44 Args: 48 Args:
45 storage_root: root directory on local disk within which we store all 49 storage_root: root directory on local disk within which we store all
46 images 50 images
47 expected_image_url: file or HTTP url from which we will download the 51 expected_image_url: file or HTTP url from which we will download the
48 expected image 52 expected image
49 expected_image_locator: a unique ID string under which we will store the 53 expected_image_locator: a unique ID string under which we will store the
50 expected image within storage_root (probably including a checksum to 54 expected image within storage_root (probably including a checksum to
51 guarantee uniqueness) 55 guarantee uniqueness)
52 actual_image_url: file or HTTP url from which we will download the 56 actual_image_url: file or HTTP url from which we will download the
53 actual image 57 actual image
54 actual_image_locator: a unique ID string under which we will store the 58 actual_image_locator: a unique ID string under which we will store the
55 actual image within storage_root (probably including a checksum to 59 actual image within storage_root (probably including a checksum to
56 guarantee uniqueness) 60 guarantee uniqueness)
61 images_subdir: the subdirectory images are stored in. Optional parameter,
epoger 2013/12/20 19:46:37 what are "images" as opposed to "actual images"?
rmistry 2013/12/20 20:00:04 Ah right. Done.
62 the default value is in DEFAULT_IMAGES_SUBDIR.
epoger 2013/12/20 19:46:37 Thank you for being descriptive, but I think we're
rmistry 2013/12/20 20:00:04 Done.
63 actual_images_subdir: the subdirectory actual images are stored in.
64 Optional parameter, the default value is in DEFAULT_IMAGES_SUBDIR.
65 use_diff_image_locator: If True outputs differences as
66 'expected_image_locator-vs-actual_image_locator' else outputs
67 differences using the expected_image_locator if expected_image_locator
epoger 2013/12/20 19:46:37 Confusing description... so, if this is False, but
rmistry 2013/12/20 20:00:04 Agreed. Done.
68 is equal to the actual_image_locator. Optional parameter, the default
69 value is True.
70 image_suffix: the suffix of images. Optional parameter, the default
71 value is in DEFAULT_IMAGE_SUFFIX.
57 """ 72 """
58 # Download the expected/actual images, if we don't have them already. 73 # Download the expected/actual images, if we don't have them already.
59 expected_image = _download_and_open_image( 74 expected_image = _download_and_open_image(
60 os.path.join(storage_root, IMAGES_SUBDIR, 75 os.path.join(storage_root, images_subdir,
61 str(expected_image_locator) + IMAGE_SUFFIX), 76 str(expected_image_locator) + image_suffix),
epoger 2013/12/20 19:46:37 Warning: if you are not using checksums to generat
rmistry 2013/12/20 20:00:04 I tested this through the python interpreter and i
epoger 2013/12/20 20:05:08 Sounds like a good idea. More than anything, I wa
rmistry 2013/12/20 20:53:27 Added a TODO.
62 expected_image_url) 77 expected_image_url)
63 actual_image = _download_and_open_image( 78 actual_image = _download_and_open_image(
64 os.path.join(storage_root, IMAGES_SUBDIR, 79 os.path.join(storage_root, actual_images_subdir,
65 str(actual_image_locator) + IMAGE_SUFFIX), 80 str(actual_image_locator) + image_suffix),
66 actual_image_url) 81 actual_image_url)
67 82
68 # Generate the diff image (absolute diff at each pixel) and 83 # Generate the diff image (absolute diff at each pixel) and
69 # max_diff_per_channel. 84 # max_diff_per_channel.
70 diff_image = _generate_image_diff(actual_image, expected_image) 85 diff_image = _generate_image_diff(actual_image, expected_image)
71 diff_histogram = diff_image.histogram() 86 diff_histogram = diff_image.histogram()
72 (diff_width, diff_height) = diff_image.size 87 (diff_width, diff_height) = diff_image.size
73 self._weighted_diff_measure = _calculate_weighted_diff_metric( 88 self._weighted_diff_measure = _calculate_weighted_diff_metric(
74 diff_histogram, diff_width * diff_height) 89 diff_histogram, diff_width * diff_height)
75 self._max_diff_per_channel = _max_per_band(diff_histogram) 90 self._max_diff_per_channel = _max_per_band(diff_histogram)
76 91
77 # Generate the whitediff image (any differing pixels show as white). 92 # Generate the whitediff image (any differing pixels show as white).
78 # This is tricky, because when you convert color images to grayscale or 93 # This is tricky, because when you convert color images to grayscale or
79 # black & white in PIL, it has its own ideas about thresholds. 94 # black & white in PIL, it has its own ideas about thresholds.
80 # We have to force it: if a pixel has any color at all, it's a '1'. 95 # We have to force it: if a pixel has any color at all, it's a '1'.
81 bands = diff_image.split() 96 bands = diff_image.split()
82 graydiff_image = ImageChops.lighter(ImageChops.lighter( 97 graydiff_image = ImageChops.lighter(ImageChops.lighter(
83 bands[0], bands[1]), bands[2]) 98 bands[0], bands[1]), bands[2])
84 whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND) 99 whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND)
85 .convert('1', dither=Image.NONE)) 100 .convert('1', dither=Image.NONE))
86 101
87 # Final touches on diff_image: use whitediff_image as an alpha mask. 102 # Final touches on diff_image: use whitediff_image as an alpha mask.
88 # Unchanged pixels are transparent; differing pixels are opaque. 103 # Unchanged pixels are transparent; differing pixels are opaque.
89 diff_image.putalpha(whitediff_image) 104 diff_image.putalpha(whitediff_image)
90 105
91 # Store the diff and whitediff images generated above. 106 # Store the diff and whitediff images generated above.
92 diff_image_locator = _get_difference_locator( 107 if not use_diff_image_locator and (
93 expected_image_locator=expected_image_locator, 108 expected_image_locator == actual_image_locator):
94 actual_image_locator=actual_image_locator) 109 basename = expected_image_locator + image_suffix
95 basename = str(diff_image_locator) + IMAGE_SUFFIX 110 else:
111 diff_image_locator = _get_difference_locator(
112 expected_image_locator=expected_image_locator,
113 actual_image_locator=actual_image_locator)
114 basename = str(diff_image_locator) + image_suffix
96 _save_image(diff_image, os.path.join( 115 _save_image(diff_image, os.path.join(
97 storage_root, DIFFS_SUBDIR, basename)) 116 storage_root, DIFFS_SUBDIR, basename))
98 _save_image(whitediff_image, os.path.join( 117 _save_image(whitediff_image, os.path.join(
99 storage_root, WHITEDIFFS_SUBDIR, basename)) 118 storage_root, WHITEDIFFS_SUBDIR, basename))
100 119
101 # Calculate difference metrics. 120 # Calculate difference metrics.
102 (self._width, self._height) = diff_image.size 121 (self._width, self._height) = diff_image.size
103 self._num_pixels_differing = ( 122 self._num_pixels_differing = (
104 whitediff_image.histogram()[VALUES_PER_BAND - 1]) 123 whitediff_image.histogram()[VALUES_PER_BAND - 1])
105 124
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 and actual_image. 337 and actual_image.
319 338
320 Args: 339 Args:
321 expected_image_locator: locator string pointing at expected image 340 expected_image_locator: locator string pointing at expected image
322 actual_image_locator: locator string pointing at actual image 341 actual_image_locator: locator string pointing at actual image
323 342
324 Returns: locator where the diffs between expected and actual images can be 343 Returns: locator where the diffs between expected and actual images can be
325 found 344 found
326 """ 345 """
327 return "%s-vs-%s" % (expected_image_locator, actual_image_locator) 346 return "%s-vs-%s" % (expected_image_locator, actual_image_locator)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698