OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Module that outputs an JSON summary containing the comparision of images.""" | 6 """Module that outputs an JSON summary containing the comparision of images.""" |
7 | 7 |
8 import csv | 8 import csv |
9 import imp | 9 import imp |
10 import json | 10 import json |
11 import optparse | 11 import optparse |
12 import os | 12 import os |
13 import posixpath | 13 import posixpath |
14 import sys | 14 import sys |
15 | 15 |
16 sys.path.append( | 16 sys.path.append( |
17 os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir)) | 17 os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir)) |
18 import json_summary_constants | 18 import json_summary_constants |
19 | 19 |
20 | 20 |
21 def WriteJsonSummary(img_root, nopatch_json, nopatch_img_dir_name, | 21 def WriteJsonSummary(img_root, nopatch_json, nopatch_images_base_url, |
22 withpatch_json, withpatch_img_dir_name, output_file_path, | 22 withpatch_json, withpatch_images_base_url, |
23 gs_output_dir, gs_skp_dir, slave_num, gm_json_path, | 23 output_file_path, gs_output_dir, gs_skp_dir, slave_num, |
24 imagediffdb_path, skpdiff_output_csv): | 24 gm_json_path, imagediffdb_path): |
25 """Outputs the JSON summary of image comparisions. | 25 """Outputs the JSON summary of image comparisions. |
26 | 26 |
27 Args: | 27 Args: |
28 img_root: (str) The root directory on local disk where we store all images. | 28 img_root: (str) The root directory on local disk where we store all images. |
29 nopatch_json: (str) Location of the nopatch render_pictures JSON summary | 29 nopatch_json: (str) Location of the nopatch render_pictures JSON summary |
30 file. | 30 file. |
31 nopatch_img_dir_name: (str) Name of the directory within img_root that | 31 nopatch_images_base_url: (str) URL of directory containing all nopatch |
32 contains all nopatch images. | 32 images. |
33 withpatch_json: (str) Location of the withpatch render_pictures JSON summary | 33 withpatch_json: (str) Location of the withpatch render_pictures JSON summary |
34 file. | 34 file. |
35 withpatch_img_dir_name: (str) Name of the directory within img_root that | 35 withpatch_images_base_url: (str) URL of directory containing all withpatch |
36 contains all withpatch images. | 36 images. |
37 output_file_path: (str) The local path to the JSON file that will be | 37 output_file_path: (str) The local path to the JSON file that will be |
38 created by this function which will contain a summary of all file | 38 created by this function which will contain a summary of all file |
39 differences for this slave. | 39 differences for this slave. |
40 gs_output_dir: (str) The directory the JSON summary file and images will be | 40 gs_output_dir: (str) The directory the JSON summary file and images will be |
41 outputted to in Google Storage. | 41 outputted to in Google Storage. |
42 gs_skp_dir: (str) The Google Storage directory that contains the SKPs of | 42 gs_skp_dir: (str) The Google Storage directory that contains the SKPs of |
43 this cluster telemetry slave. | 43 this cluster telemetry slave. |
44 slave_num: (str) The number of the cluster telemetry slave that is running | 44 slave_num: (str) The number of the cluster telemetry slave that is running |
45 this script. | 45 this script. |
46 gm_json_path: (str) Local complete path to gm_json.py in Skia trunk. | 46 gm_json_path: (str) Local complete path to gm_json.py in Skia trunk. |
47 imagediffdb_path: (str) Local complete path to imagediffdb.py in Skia trunk. | 47 imagediffdb_path: (str) Local complete path to imagediffdb.py in Skia trunk. |
48 skpdiff_output_csv: (str) Local complete path to the CSV output of skpdiff. | |
49 """ | 48 """ |
50 | 49 |
51 assert os.path.isfile(gm_json_path), 'Must specify a valid path to gm_json.py' | 50 assert os.path.isfile(gm_json_path), 'Must specify a valid path to gm_json.py' |
52 gm_json_mod = imp.load_source(gm_json_path, gm_json_path) | 51 gm_json_mod = imp.load_source(gm_json_path, gm_json_path) |
53 assert os.path.isfile(imagediffdb_path), ( | 52 assert os.path.isfile(imagediffdb_path), ( |
54 'Must specify a valid path to imagediffdb.py') | 53 'Must specify a valid path to imagediffdb.py') |
55 imagediffdb_mod = imp.load_source(imagediffdb_path, imagediffdb_path) | 54 imagediffdb_mod = imp.load_source(imagediffdb_path, imagediffdb_path) |
56 | 55 |
57 files_to_checksums1 = GetFilesAndChecksums(nopatch_json, gm_json_mod) | 56 files_to_checksums_nopatch = GetFilesAndChecksums(nopatch_json, gm_json_mod) |
58 files_to_checksums2 = GetFilesAndChecksums(withpatch_json, gm_json_mod) | 57 files_to_checksums_withpatch = GetFilesAndChecksums( |
| 58 withpatch_json, gm_json_mod) |
59 | 59 |
60 assert len(files_to_checksums1) == len(files_to_checksums2), ( | 60 assert len(files_to_checksums_nopatch) == len(files_to_checksums_withpatch), ( |
61 'Number of images in both JSON summary files are different') | 61 'Number of images in both JSON summary files are different') |
62 assert files_to_checksums1.keys() == files_to_checksums2.keys(), ( | 62 assert (files_to_checksums_nopatch.keys() == |
63 'File names in both JSON summary files are different') | 63 files_to_checksums_withpatch.keys(), |
| 64 'File names in both JSON summary files are different') |
64 | 65 |
65 # Compare checksums in both directories and output differences. | 66 # Compare checksums in both directories and output differences. |
66 file_differences = [] | 67 file_differences = [] |
67 slave_dict = { | 68 slave_dict = { |
68 json_summary_constants.JSONKEY_SKPS_LOCATION: gs_skp_dir, | 69 json_summary_constants.JSONKEY_SKPS_LOCATION: gs_skp_dir, |
69 json_summary_constants.JSONKEY_FAILED_FILES: file_differences, | 70 json_summary_constants.JSONKEY_FAILED_FILES: file_differences, |
70 json_summary_constants.JSONKEY_FILES_LOCATION_NOPATCH: posixpath.join( | 71 json_summary_constants.JSONKEY_FILES_LOCATION_NOPATCH: posixpath.join( |
71 gs_output_dir, 'slave%s' % slave_num, 'nopatch-images'), | 72 gs_output_dir, 'slave%s' % slave_num, 'nopatch-images'), |
72 json_summary_constants.JSONKEY_FILES_LOCATION_WITHPATCH: posixpath.join( | 73 json_summary_constants.JSONKEY_FILES_LOCATION_WITHPATCH: posixpath.join( |
73 gs_output_dir, 'slave%s' % slave_num, 'withpatch-images'), | 74 gs_output_dir, 'slave%s' % slave_num, 'withpatch-images'), |
74 json_summary_constants.JSONKEY_FILES_LOCATION_DIFFS: posixpath.join( | 75 json_summary_constants.JSONKEY_FILES_LOCATION_DIFFS: posixpath.join( |
75 gs_output_dir, 'slave%s' % slave_num, 'diffs'), | 76 gs_output_dir, 'slave%s' % slave_num, 'diffs'), |
76 json_summary_constants.JSONKEY_FILES_LOCATION_WHITE_DIFFS: posixpath.join( | 77 json_summary_constants.JSONKEY_FILES_LOCATION_WHITE_DIFFS: posixpath.join( |
77 gs_output_dir, 'slave%s' % slave_num, 'whitediffs') | 78 gs_output_dir, 'slave%s' % slave_num, 'whitediffs') |
78 } | 79 } |
79 json_summary = { | 80 json_summary = { |
80 'slave%s' % slave_num: slave_dict | 81 'slave%s' % slave_num: slave_dict |
81 } | 82 } |
82 # Read the skpdiff CSV output. | |
83 page_to_perceptual_similarity = {} | |
84 for row in csv.DictReader(open(skpdiff_output_csv, 'r')): | |
85 page_to_perceptual_similarity[row['key']] = float( | |
86 row[' perceptual'].strip()) | |
87 | 83 |
88 for file1 in files_to_checksums1: | 84 image_diff_db = imagediffdb_mod.ImageDiffDB(storage_root=img_root) |
89 algo1, checksum1 = files_to_checksums1[file1] | 85 for filename in files_to_checksums_nopatch: |
90 algo2, checksum2 = files_to_checksums2[file1] | 86 algo_nopatch, checksum_nopatch = files_to_checksums_nopatch[filename] |
91 assert algo1 == algo2, 'Different algorithms found' | 87 algo_withpatch, checksum_withpatch = files_to_checksums_withpatch[filename] |
92 if checksum1 != checksum2: | 88 assert algo_nopatch == algo_withpatch, 'Different checksum algorithms found' |
93 # Call imagediffdb and then the diffs and metrics. | 89 if checksum_nopatch != checksum_withpatch: |
94 image_diff = imagediffdb_mod.DiffRecord( | 90 # TODO(epoger): It seems silly that we add this DiffRecord to ImageDiffDB |
95 storage_root=img_root, | 91 # and then pull it out again right away, but this is a stepping-stone |
96 expected_image_url=None, # Do not need to download any img. | 92 # to using ImagePairSet instead of replicating its behavior here. |
97 expected_image_locator=os.path.splitext(file1)[0], | 93 image_diff_db.add_image_pair( |
98 actual_image_url=None, # Do not need to download any img. | 94 expected_image_url=posixpath.join(nopatch_images_base_url, filename), |
99 actual_image_locator=os.path.splitext(file1)[0], | 95 expected_image_locator=checksum_nopatch, |
100 expected_images_subdir=nopatch_img_dir_name, | 96 actual_image_url=posixpath.join(withpatch_images_base_url, filename), |
101 actual_images_subdir=withpatch_img_dir_name) | 97 actual_image_locator=checksum_withpatch) |
| 98 diff_record = image_diff_db.get_diff_record( |
| 99 expected_image_locator=checksum_nopatch, |
| 100 actual_image_locator=checksum_withpatch) |
102 file_differences.append({ | 101 file_differences.append({ |
103 json_summary_constants.JSONKEY_FILE_NAME: file1, | 102 json_summary_constants.JSONKEY_FILE_NAME: filename, |
104 json_summary_constants.JSONKEY_SKP_LOCATION: posixpath.join( | 103 json_summary_constants.JSONKEY_SKP_LOCATION: posixpath.join( |
105 gs_skp_dir, GetSkpFileName(file1)), | 104 gs_skp_dir, GetSkpFileName(filename)), |
106 json_summary_constants.JSONKEY_NUM_PIXELS_DIFFERING: | 105 json_summary_constants.JSONKEY_NUM_PIXELS_DIFFERING: |
107 image_diff.get_num_pixels_differing(), | 106 diff_record.get_num_pixels_differing(), |
108 json_summary_constants.JSONKEY_PERCENT_PIXELS_DIFFERING: | 107 json_summary_constants.JSONKEY_PERCENT_PIXELS_DIFFERING: |
109 image_diff.get_percent_pixels_differing(), | 108 diff_record.get_percent_pixels_differing(), |
110 json_summary_constants.JSONKEY_WEIGHTED_DIFF_MEASURE: | 109 json_summary_constants.JSONKEY_WEIGHTED_DIFF_MEASURE: |
111 image_diff.get_weighted_diff_measure(), | 110 diff_record.get_weighted_diff_measure(), |
112 json_summary_constants.JSONKEY_MAX_DIFF_PER_CHANNEL: | 111 json_summary_constants.JSONKEY_MAX_DIFF_PER_CHANNEL: |
113 image_diff.get_max_diff_per_channel(), | 112 diff_record.get_max_diff_per_channel(), |
114 json_summary_constants.JSONKEY_PERCEPTUAL_SIMILARITY: | 113 json_summary_constants.JSONKEY_PERCEPTUAL_SIMILARITY: |
115 page_to_perceptual_similarity[file1], | 114 diff_record.get_perceptual_difference(), |
116 }) | 115 }) |
117 if file_differences: | 116 if file_differences: |
118 slave_dict[json_summary_constants.JSONKEY_FAILED_FILES_COUNT] = len( | 117 slave_dict[json_summary_constants.JSONKEY_FAILED_FILES_COUNT] = len( |
119 file_differences) | 118 file_differences) |
120 with open(output_file_path, 'w') as f: | 119 with open(output_file_path, 'w') as f: |
121 f.write(json.dumps(json_summary, indent=4, sort_keys=True)) | 120 f.write(json.dumps(json_summary, indent=4, sort_keys=True)) |
122 | 121 |
123 | 122 |
124 def GetSkpFileName(img_file_name): | 123 def GetSkpFileName(img_file_name): |
125 """Determine the SKP file name from the image's file name.""" | 124 """Determine the SKP file name from the image's file name.""" |
(...skipping 14 matching lines...) Expand all Loading... |
140 | 139 |
141 if '__main__' == __name__: | 140 if '__main__' == __name__: |
142 option_parser = optparse.OptionParser() | 141 option_parser = optparse.OptionParser() |
143 option_parser.add_option( | 142 option_parser.add_option( |
144 '', '--img_root', | 143 '', '--img_root', |
145 help='The root directory on local disk where we store all images.') | 144 help='The root directory on local disk where we store all images.') |
146 option_parser.add_option( | 145 option_parser.add_option( |
147 '', '--nopatch_json', | 146 '', '--nopatch_json', |
148 help='Location of the nopatch render_pictures JSON summary file.') | 147 help='Location of the nopatch render_pictures JSON summary file.') |
149 option_parser.add_option( | 148 option_parser.add_option( |
150 '', '--nopatch_img_dir_name', | 149 '', '--nopatch_images_base_url', |
151 help='Name of the directory within img_root that contains all nopatch ' | 150 help='URL of directory containing all nopatch images.') |
152 'images.') | |
153 option_parser.add_option( | 151 option_parser.add_option( |
154 '', '--withpatch_json', | 152 '', '--withpatch_json', |
155 help='Location of the withpatch render_pictures JSON summary file.') | 153 help='Location of the withpatch render_pictures JSON summary file.') |
156 option_parser.add_option( | 154 option_parser.add_option( |
157 '', '--withpatch_img_dir_name', | 155 '', '--withpatch_images_base_url', |
158 help='Name of the directory within img_root that contains all withpatch ' | 156 help='URL of directory containing all withpatch images.') |
159 'images.') | |
160 option_parser.add_option( | 157 option_parser.add_option( |
161 '', '--output_file_path', | 158 '', '--output_file_path', |
162 help='The local path to the JSON file that will be created by this ' | 159 help='The local path to the JSON file that will be created by this ' |
163 'script which will contain a summary of all file differences for ' | 160 'script which will contain a summary of all file differences for ' |
164 'this slave.') | 161 'this slave.') |
165 option_parser.add_option( | 162 option_parser.add_option( |
166 '', '--gs_output_dir', | 163 '', '--gs_output_dir', |
167 help='The directory the JSON summary file and images will be outputted ' | 164 help='The directory the JSON summary file and images will be outputted ' |
168 'to in Google Storage.') | 165 'to in Google Storage.') |
169 option_parser.add_option( | 166 option_parser.add_option( |
170 '', '--gs_skp_dir', | 167 '', '--gs_skp_dir', |
171 help='The Google Storage directory that contains the SKPs of this ' | 168 help='The Google Storage directory that contains the SKPs of this ' |
172 'cluster telemetry slave.') | 169 'cluster telemetry slave.') |
173 option_parser.add_option( | 170 option_parser.add_option( |
174 '', '--slave_num', | 171 '', '--slave_num', |
175 help='The number of the cluster telemetry slave that is running this ' | 172 help='The number of the cluster telemetry slave that is running this ' |
176 'script.') | 173 'script.') |
177 option_parser.add_option( | 174 option_parser.add_option( |
178 '', '--gm_json_path', | 175 '', '--gm_json_path', |
179 help='Local complete path to gm_json.py in Skia trunk.') | 176 help='Local complete path to gm_json.py in Skia trunk.') |
180 option_parser.add_option( | 177 option_parser.add_option( |
181 '', '--imagediffdb_path', | 178 '', '--imagediffdb_path', |
182 help='Local complete path to imagediffdb.py in Skia trunk.') | 179 help='Local complete path to imagediffdb.py in Skia trunk.') |
183 option_parser.add_option( | |
184 '', '--skpdiff_output_csv', | |
185 help='Local complete path to the CSV output of skpdiff.') | |
186 options, unused_args = option_parser.parse_args() | 180 options, unused_args = option_parser.parse_args() |
187 if (not options.nopatch_json or not options.withpatch_json | 181 if (not options.nopatch_json or not options.withpatch_json |
188 or not options.output_file_path or not options.gs_output_dir | 182 or not options.output_file_path or not options.gs_output_dir |
189 or not options.gs_skp_dir or not options.slave_num | 183 or not options.gs_skp_dir or not options.slave_num |
190 or not options.gm_json_path or not options.img_root | 184 or not options.gm_json_path or not options.img_root |
191 or not options.nopatch_img_dir_name or not options.withpatch_img_dir_name | 185 or not options.nopatch_images_base_url |
192 or not options.imagediffdb_path or not options.skpdiff_output_csv): | 186 or not options.withpatch_images_base_url |
| 187 or not options.imagediffdb_path): |
193 option_parser.error( | 188 option_parser.error( |
194 'Must specify img_root, nopatch_json, nopatch_img_dir_name, ' | 189 'Must specify img_root, nopatch_json, nopatch_images_base_url, ' |
195 'withpatch_json, withpatch_img_dir_name, output_file_path, ' | 190 'withpatch_json, withpatch_images_base_url, output_file_path, ' |
196 'gs_output_dir, gs_skp_dir, slave_num, gm_json_path, ' | 191 'gs_output_dir, gs_skp_dir, slave_num, gm_json_path, ' |
197 'imagediffdb_path and skpdiff_output_csv.') | 192 'and imagediffdb_path.') |
198 | 193 |
199 WriteJsonSummary(options.img_root, options.nopatch_json, | 194 WriteJsonSummary(options.img_root, options.nopatch_json, |
200 options.nopatch_img_dir_name, options.withpatch_json, | 195 options.nopatch_images_base_url, options.withpatch_json, |
201 options.withpatch_img_dir_name, options.output_file_path, | 196 options.withpatch_images_base_url, options.output_file_path, |
202 options.gs_output_dir, options.gs_skp_dir, options.slave_num, | 197 options.gs_output_dir, options.gs_skp_dir, options.slave_num, |
203 options.gm_json_path, options.imagediffdb_path, | 198 options.gm_json_path, options.imagediffdb_path) |
204 options.skpdiff_output_csv) | |
OLD | NEW |