blob: 23a75adf8ab11dc10dbe52866a53545581ca287e [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module that outputs an JSON summary containing the comparision of images."""
import json
import optparse
import os
import posixpath
import sys
import traceback
sys.path.append(
os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
import json_summary_constants
def WriteJsonSummary(img_root, nopatch_json, nopatch_images_base_url,
withpatch_json, withpatch_images_base_url,
output_file_path, gs_output_dir, gs_skp_dir, slave_num,
additions_to_sys_path):
"""Outputs the JSON summary of image comparisions.
Args:
img_root: (str) The root directory on local disk where we store all images.
nopatch_json: (str) Location of the nopatch render_pictures JSON summary
file.
nopatch_images_base_url: (str) URL of directory containing all nopatch
images.
withpatch_json: (str) Location of the withpatch render_pictures JSON summary
file.
withpatch_images_base_url: (str) URL of directory containing all withpatch
images.
output_file_path: (str) The local path to the JSON file that will be
created by this function which will contain a summary of all file
differences for this slave.
gs_output_dir: (str) The directory the JSON summary file and images will be
outputted to in Google Storage.
gs_skp_dir: (str) The Google Storage directory that contains the SKPs of
this cluster telemetry slave.
slave_num: (str) The number of the cluster telemetry slave that is running
this script.
additions_to_sys_path: ([str]) A list of path components to add to sys.path;
typically used to provide rebaseline_server Python modules.
"""
for dirpath in additions_to_sys_path:
if dirpath not in sys.path:
sys.path.append(dirpath)
# Modules from skia's gm/ and gm/rebaseline_server/ dirs.
try:
import gm_json
import imagediffdb
except ImportError:
print 'sys.path is [%s]' % sys.path
traceback.print_exc()
raise Exception('You need to add gm/ and gm/rebaseline_server to sys.path')
files_to_checksums_nopatch = GetFilesAndChecksums(gm_json, nopatch_json)
files_to_checksums_withpatch = GetFilesAndChecksums(gm_json, withpatch_json)
assert len(files_to_checksums_nopatch) == len(files_to_checksums_withpatch), (
'Number of images in both JSON summary files are different')
assert files_to_checksums_nopatch.keys() == \
files_to_checksums_withpatch.keys(), (
'File names in both JSON summary files are different')
# Compare checksums in both directories and output differences.
file_differences = []
slave_dict = {
json_summary_constants.JSONKEY_SKPS_LOCATION: gs_skp_dir,
json_summary_constants.JSONKEY_FAILED_FILES: file_differences,
json_summary_constants.JSONKEY_FILES_LOCATION_NOPATCH: posixpath.join(
gs_output_dir, 'slave%s' % slave_num, 'nopatch-images'),
json_summary_constants.JSONKEY_FILES_LOCATION_WITHPATCH: posixpath.join(
gs_output_dir, 'slave%s' % slave_num, 'withpatch-images'),
json_summary_constants.JSONKEY_FILES_LOCATION_DIFFS: posixpath.join(
gs_output_dir, 'slave%s' % slave_num, 'diffs'),
json_summary_constants.JSONKEY_FILES_LOCATION_WHITE_DIFFS: posixpath.join(
gs_output_dir, 'slave%s' % slave_num, 'whitediffs')
}
json_summary = {
'slave%s' % slave_num: slave_dict
}
image_diff_db = imagediffdb.ImageDiffDB(storage_root=img_root)
for filename in files_to_checksums_nopatch:
algo_nopatch, checksum_nopatch = files_to_checksums_nopatch[filename]
algo_withpatch, checksum_withpatch = files_to_checksums_withpatch[filename]
assert algo_nopatch == algo_withpatch, 'Different checksum algorithms found'
if checksum_nopatch != checksum_withpatch:
# TODO(epoger): It seems silly that we add this DiffRecord to ImageDiffDB
# and then pull it out again right away, but this is a stepping-stone
# to using ImagePairSet instead of replicating its behavior here.
image_locator_base = os.path.splitext(filename)[0]
image_locator_nopatch = image_locator_base + '_nopatch'
image_locator_withpatch = image_locator_base + '_withpatch'
image_diff_db.add_image_pair(
expected_image_url=posixpath.join(nopatch_images_base_url, filename),
expected_image_locator=image_locator_nopatch,
actual_image_url=posixpath.join(withpatch_images_base_url, filename),
actual_image_locator=image_locator_withpatch)
diff_record = image_diff_db.get_diff_record(
expected_image_locator=image_locator_nopatch,
actual_image_locator=image_locator_withpatch)
file_differences.append({
json_summary_constants.JSONKEY_FILE_NAME: filename,
json_summary_constants.JSONKEY_SKP_LOCATION: posixpath.join(
gs_skp_dir, GetSkpFileName(filename)),
json_summary_constants.JSONKEY_NUM_PIXELS_DIFFERING:
diff_record.get_num_pixels_differing(),
json_summary_constants.JSONKEY_PERCENT_PIXELS_DIFFERING:
diff_record.get_percent_pixels_differing(),
json_summary_constants.JSONKEY_WEIGHTED_DIFF_MEASURE:
diff_record.get_weighted_diff_measure(),
json_summary_constants.JSONKEY_MAX_DIFF_PER_CHANNEL:
diff_record.get_max_diff_per_channel(),
json_summary_constants.JSONKEY_PERCEPTUAL_DIFF:
diff_record.get_perceptual_difference(),
})
if file_differences:
slave_dict[json_summary_constants.JSONKEY_FAILED_FILES_COUNT] = len(
file_differences)
with open(output_file_path, 'w') as f:
f.write(json.dumps(json_summary, indent=4, sort_keys=True))
def GetSkpFileName(img_file_name):
"""Determine the SKP file name from the image's file name."""
# TODO(rmistry): The below relies too much on the current output of render
# pictures to determine the root SKP.
return '%s_.skp' % '_'.join(img_file_name.split('_')[:-1])
def GetFilesAndChecksums(gm_json_mod, json_location):
"""Reads the JSON summary and returns dict of files to checksums."""
data = gm_json_mod.LoadFromFile(json_location)
if data:
return data[gm_json_mod.JSONKEY_ACTUALRESULTS][
gm_json_mod.JSONKEY_ACTUALRESULTS_NOCOMPARISON]
else:
return {}
if '__main__' == __name__:
option_parser = optparse.OptionParser()
option_parser.add_option(
'', '--img_root',
help='The root directory on local disk where we store all images.')
option_parser.add_option(
'', '--nopatch_json',
help='Location of the nopatch render_pictures JSON summary file.')
option_parser.add_option(
'', '--nopatch_images_base_url',
help='URL of directory containing all nopatch images.')
option_parser.add_option(
'', '--withpatch_json',
help='Location of the withpatch render_pictures JSON summary file.')
option_parser.add_option(
'', '--withpatch_images_base_url',
help='URL of directory containing all withpatch images.')
option_parser.add_option(
'', '--output_file_path',
help='The local path to the JSON file that will be created by this '
'script which will contain a summary of all file differences for '
'this slave.')
option_parser.add_option(
'', '--gs_output_dir',
help='The directory the JSON summary file and images will be outputted '
'to in Google Storage.')
option_parser.add_option(
'', '--gs_skp_dir',
help='The Google Storage directory that contains the SKPs of this '
'cluster telemetry slave.')
option_parser.add_option(
'', '--slave_num',
help='The number of the cluster telemetry slave that is running this '
'script.')
option_parser.add_option(
'', '--add_to_sys_path',
action='append',
help='Directory to add to sys.path. May be repeated.')
options, unused_args = option_parser.parse_args()
if (not options.nopatch_json or not options.withpatch_json
or not options.output_file_path or not options.gs_output_dir
or not options.gs_skp_dir or not options.slave_num
or not options.img_root
or not options.nopatch_images_base_url
or not options.withpatch_images_base_url):
option_parser.error(
'Must specify img_root, nopatch_json, nopatch_images_base_url, '
'withpatch_json, withpatch_images_base_url, output_file_path, '
'gs_output_dir, gs_skp_dir, and slave_num.')
WriteJsonSummary(options.img_root, options.nopatch_json,
options.nopatch_images_base_url, options.withpatch_json,
options.withpatch_images_base_url, options.output_file_path,
options.gs_output_dir, options.gs_skp_dir, options.slave_num,
options.add_to_sys_path)