107 lines
3.8 KiB
Python
107 lines
3.8 KiB
Python
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import errno
|
|
from PIL import Image
|
|
import logging
|
|
import subprocess
|
|
import tempfile
|
|
|
|
from autotest_lib.client.cros.image_comparison import comparison_result
|
|
from autotest_lib.client.cros.video import method_logger
|
|
|
|
|
|
class PdiffImageComparer(object):
|
|
"""
|
|
Compares two images using ChromeOS' perceptualdiff binary.
|
|
|
|
"""
|
|
|
|
@method_logger.log
|
|
def compare(self, golden_img_path, test_img_path, box=None):
|
|
"""
|
|
Compares a test image against the specified golden image using the
|
|
terminal tool 'perceptualdiff'.
|
|
|
|
@param golden_img_path: path, complete path to a golden image.
|
|
@param test_img_path: path, complete path to a test image.
|
|
@param box: int tuple, left, upper, right, lower pixel coordinates.
|
|
Defines the rectangular boundary within which to compare.
|
|
@return: int, number of pixels that are different.
|
|
@raise : Whatever _pdiff_compare raises.
|
|
|
|
"""
|
|
if not box:
|
|
return self._pdiff_compare(golden_img_path, test_img_path)
|
|
|
|
ext = '.png'
|
|
tmp_golden_img_file = tempfile.NamedTemporaryFile(suffix=ext)
|
|
tmp_test_img_file = tempfile.NamedTemporaryFile(suffix=ext)
|
|
|
|
with tmp_golden_img_file, tmp_test_img_file:
|
|
tmp_golden_img_path = tmp_golden_img_file.name
|
|
tmp_test_img_path = tmp_test_img_file.name
|
|
|
|
Image.open(golden_img_path).crop(box).save(tmp_golden_img_path)
|
|
Image.open(test_img_path).crop(box).save(tmp_test_img_path)
|
|
|
|
return self._pdiff_compare(tmp_golden_img_path, tmp_test_img_path)
|
|
|
|
|
|
def _pdiff_compare(self, golden_img_path, test_img_path):
|
|
"""
|
|
Invokes perceptualdiff using subprocess tools.
|
|
|
|
@param golden_img_path: path, complete path to a golden image.
|
|
@param test_img_path: path, complete path to a test image.
|
|
@return: int, number of pixels that are different.
|
|
@raise ValueError if image dimensions are not the same.
|
|
@raise OSError: if file does not exist or can not be opened.
|
|
|
|
"""
|
|
|
|
# TODO mussa: Could downsampling the images be good for us?
|
|
|
|
tmp_diff_file = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
|
|
args = ['perceptualdiff', golden_img_path, test_img_path, '-output',
|
|
tmp_diff_file.name]
|
|
|
|
logging.debug("Start process with args : " + str(args))
|
|
|
|
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
output = p.communicate()
|
|
logging.debug('output of perceptual diff command is')
|
|
logging.debug(output)
|
|
|
|
stdoutdata = output[0]
|
|
|
|
mismatch_error = "Image dimensions do not match"
|
|
diff_message = "Images are visibly different"
|
|
filenotfound_message = "Cannot open"
|
|
|
|
#TODO(dhaddock): Check for image not created
|
|
if p.returncode == 0:
|
|
# pdiff exited with 0, images were the same
|
|
return comparison_result.ComparisonResult(0, '', None)
|
|
|
|
if mismatch_error in stdoutdata:
|
|
raise ValueError("pdiff says: " + stdoutdata)
|
|
|
|
if diff_message in stdoutdata:
|
|
diff_pixels = [int(s) for s in stdoutdata.split() if s.isdigit()][0]
|
|
return comparison_result.ComparisonResult(int(diff_pixels), '',
|
|
tmp_diff_file.name)
|
|
|
|
if filenotfound_message in stdoutdata:
|
|
raise OSError(errno.ENOENT, "pdiff says: " + stdoutdata)
|
|
|
|
raise RuntimeError("Unknown result from pdiff: "
|
|
"Output : " + stdoutdata)
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
pass |