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

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 """
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 expected_images_subdir=DEFAULT_IMAGES_SUBDIR,
39 actual_images_subdir=DEFAULT_IMAGES_SUBDIR,
40 image_suffix=DEFAULT_IMAGE_SUFFIX):
38 """Download this pair of images (unless we already have them on local disk), 41 """Download this pair of images (unless we already have them on local disk),
39 and prepare a DiffRecord for them. 42 and prepare a DiffRecord for them.
40 43
41 TODO(epoger): Make this asynchronously download images, rather than blocking 44 TODO(epoger): Make this asynchronously download images, rather than blocking
42 until the images have been downloaded and processed. 45 until the images have been downloaded and processed.
43 46
44 Args: 47 Args:
45 storage_root: root directory on local disk within which we store all 48 storage_root: root directory on local disk within which we store all
46 images 49 images
47 expected_image_url: file or HTTP url from which we will download the 50 expected_image_url: file or HTTP url from which we will download the
48 expected image 51 expected image
49 expected_image_locator: a unique ID string under which we will store the 52 expected_image_locator: a unique ID string under which we will store the
50 expected image within storage_root (probably including a checksum to 53 expected image within storage_root (probably including a checksum to
51 guarantee uniqueness) 54 guarantee uniqueness)
52 actual_image_url: file or HTTP url from which we will download the 55 actual_image_url: file or HTTP url from which we will download the
53 actual image 56 actual image
54 actual_image_locator: a unique ID string under which we will store the 57 actual_image_locator: a unique ID string under which we will store the
55 actual image within storage_root (probably including a checksum to 58 actual image within storage_root (probably including a checksum to
56 guarantee uniqueness) 59 guarantee uniqueness)
60 expected_images_subdir: the subdirectory expected images are stored in.
61 actual_images_subdir: the subdirectory actual images are stored in.
62 image_suffix: the suffix of images.
57 """ 63 """
58 # Download the expected/actual images, if we don't have them already. 64 # Download the expected/actual images, if we don't have them already.
65 # TODO(rmistry): Add a parameter that makes _download_and_open_image raise
66 # an exception if images are not found locally (instead of trying to
67 # download them).
59 expected_image = _download_and_open_image( 68 expected_image = _download_and_open_image(
60 os.path.join(storage_root, IMAGES_SUBDIR, 69 os.path.join(storage_root, expected_images_subdir,
61 str(expected_image_locator) + IMAGE_SUFFIX), 70 str(expected_image_locator) + image_suffix),
62 expected_image_url) 71 expected_image_url)
63 actual_image = _download_and_open_image( 72 actual_image = _download_and_open_image(
64 os.path.join(storage_root, IMAGES_SUBDIR, 73 os.path.join(storage_root, actual_images_subdir,
65 str(actual_image_locator) + IMAGE_SUFFIX), 74 str(actual_image_locator) + image_suffix),
66 actual_image_url) 75 actual_image_url)
67 76
68 # Generate the diff image (absolute diff at each pixel) and 77 # Generate the diff image (absolute diff at each pixel) and
69 # max_diff_per_channel. 78 # max_diff_per_channel.
70 diff_image = _generate_image_diff(actual_image, expected_image) 79 diff_image = _generate_image_diff(actual_image, expected_image)
71 diff_histogram = diff_image.histogram() 80 diff_histogram = diff_image.histogram()
72 (diff_width, diff_height) = diff_image.size 81 (diff_width, diff_height) = diff_image.size
73 self._weighted_diff_measure = _calculate_weighted_diff_metric( 82 self._weighted_diff_measure = _calculate_weighted_diff_metric(
74 diff_histogram, diff_width * diff_height) 83 diff_histogram, diff_width * diff_height)
75 self._max_diff_per_channel = _max_per_band(diff_histogram) 84 self._max_diff_per_channel = _max_per_band(diff_histogram)
76 85
77 # Generate the whitediff image (any differing pixels show as white). 86 # Generate the whitediff image (any differing pixels show as white).
78 # This is tricky, because when you convert color images to grayscale or 87 # This is tricky, because when you convert color images to grayscale or
79 # black & white in PIL, it has its own ideas about thresholds. 88 # 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'. 89 # We have to force it: if a pixel has any color at all, it's a '1'.
81 bands = diff_image.split() 90 bands = diff_image.split()
82 graydiff_image = ImageChops.lighter(ImageChops.lighter( 91 graydiff_image = ImageChops.lighter(ImageChops.lighter(
83 bands[0], bands[1]), bands[2]) 92 bands[0], bands[1]), bands[2])
84 whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND) 93 whitediff_image = (graydiff_image.point(lambda p: p > 0 and VALUES_PER_BAND)
85 .convert('1', dither=Image.NONE)) 94 .convert('1', dither=Image.NONE))
86 95
87 # Final touches on diff_image: use whitediff_image as an alpha mask. 96 # Final touches on diff_image: use whitediff_image as an alpha mask.
88 # Unchanged pixels are transparent; differing pixels are opaque. 97 # Unchanged pixels are transparent; differing pixels are opaque.
89 diff_image.putalpha(whitediff_image) 98 diff_image.putalpha(whitediff_image)
90 99
91 # Store the diff and whitediff images generated above. 100 # Store the diff and whitediff images generated above.
92 diff_image_locator = _get_difference_locator( 101 diff_image_locator = _get_difference_locator(
93 expected_image_locator=expected_image_locator, 102 expected_image_locator=expected_image_locator,
94 actual_image_locator=actual_image_locator) 103 actual_image_locator=actual_image_locator)
95 basename = str(diff_image_locator) + IMAGE_SUFFIX 104 basename = str(diff_image_locator) + image_suffix
96 _save_image(diff_image, os.path.join( 105 _save_image(diff_image, os.path.join(
97 storage_root, DIFFS_SUBDIR, basename)) 106 storage_root, DIFFS_SUBDIR, basename))
98 _save_image(whitediff_image, os.path.join( 107 _save_image(whitediff_image, os.path.join(
99 storage_root, WHITEDIFFS_SUBDIR, basename)) 108 storage_root, WHITEDIFFS_SUBDIR, basename))
100 109
101 # Calculate difference metrics. 110 # Calculate difference metrics.
102 (self._width, self._height) = diff_image.size 111 (self._width, self._height) = diff_image.size
103 self._num_pixels_differing = ( 112 self._num_pixels_differing = (
104 whitediff_image.histogram()[VALUES_PER_BAND - 1]) 113 whitediff_image.histogram()[VALUES_PER_BAND - 1])
105 114
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 and actual_image. 327 and actual_image.
319 328
320 Args: 329 Args:
321 expected_image_locator: locator string pointing at expected image 330 expected_image_locator: locator string pointing at expected image
322 actual_image_locator: locator string pointing at actual image 331 actual_image_locator: locator string pointing at actual image
323 332
324 Returns: locator where the diffs between expected and actual images can be 333 Returns: locator where the diffs between expected and actual images can be
325 found 334 found
326 """ 335 """
327 return "%s-vs-%s" % (expected_image_locator, actual_image_locator) 336 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