blob: 5e88b206ae138939c3f4694bb003b57d73768ec9 [file]
#!/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 csv
import imp
import json
import optparse
import os
import posixpath
import sys
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,
gm_json_path, imagediffdb_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.
gm_json_path: (str) Local complete path to gm_json.py in Skia trunk.
imagediffdb_path: (str) Local complete path to imagediffdb.py in Skia trunk.
"""
assert os.path.isfile(gm_json_path), 'Must specify a valid path to gm_json.py'
gm_json_mod = imp.load_source(gm_json_path, gm_json_path)
assert os.path.isfile(imagediffdb_path), (
'Must specify a valid path to imagediffdb.py')
imagediffdb_mod = imp.load_source(imagediffdb_path, imagediffdb_path)
files_to_checksums_nopatch = GetFilesAndChecksums(nopatch_json, gm_json_mod)
files_to_checksums_withpatch = GetFilesAndChecksums(
withpatch_json, gm_json_mod)
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_mod.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_diff_db.add_image_pair(
expected_image_url=posixpath.join(nopatch_images_base_url, filename),
expected_image_locator=checksum_nopatch,
actual_image_url=posixpath.join(withpatch_images_base_url, filename),
actual_image_locator=checksum_withpatch)
diff_record = image_diff_db.get_diff_record(
expected_image_locator=checksum_nopatch,
actual_image_locator=checksum_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_SIMILARITY:
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(json_location, gm_json_mod):
"""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(
'', '--gm_json_path',
help='Local complete path to gm_json.py in Skia trunk.')
option_parser.add_option(
'', '--imagediffdb_path',
help='Local complete path to imagediffdb.py in Skia trunk.')
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.gm_json_path or not options.img_root
or not options.nopatch_images_base_url
or not options.withpatch_images_base_url
or not options.imagediffdb_path):
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, slave_num, gm_json_path, '
'and imagediffdb_path.')
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.gm_json_path, options.imagediffdb_path)