1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import heapq, logging
6
7from PIL import Image
8from PIL import ImageChops
9
10from autotest_lib.client.cros.image_comparison import comparison_result
11from autotest_lib.client.cros.video import method_logger
12
13
14"""
15
16*** Consider using PdiffImageComparer instead of this class ***
17  * This class uses pixel by pixel comparer while PdiffImageComparer encapsules
18  * the perceptualdiff tool available in ChromeOS
19
20"""
21
22
23class RGBImageComparer(object):
24    """
25    Compares two RGB images using built-in python image library.
26
27    """
28
29
30    def __init__(self, rgb_pixel_threshold):
31        self.pixel_threshold = rgb_pixel_threshold
32
33
34    @method_logger.log
35    def compare(self, golden_img_path, test_img_path, box=None):
36        """
37        Compares a test image against a known golden image.
38
39        Both images must be RGB images.
40
41        @param golden_img_path: path, complete path to a golden image.
42        @param test_img_path: path, complete path to a test image.
43        @param box: int tuple, left, upper, right, lower pixel coordinates
44                    defining a box region within which the comparison is made.
45
46        @return: int, number of pixels that are different.
47
48        """
49        golden_image = Image.open(golden_img_path)
50        test_image = Image.open(test_img_path)
51
52        if golden_image.mode != 'RGB':
53            logging.debug('Golden image was not RGB. Converting to RGB.')
54            golden_image = golden_image.convert('RGB')
55
56        if test_image.mode != 'RGB':
57            logging.debug('Test image was not RGB. Converting to RGB.')
58            test_image = test_image.convert('RGB')
59
60        if box is not None:
61            golden_image = golden_image.crop(box)
62            test_image = test_image.crop(box)
63
64        diff_image = ImageChops.difference(golden_image, test_image)
65        maxcolors = diff_image.size[0] * diff_image.size[1]
66        colorstuples = diff_image.getcolors(maxcolors)
67        max_debug_count = 100
68
69        logging.debug("***ALL Color counts: %d", maxcolors)
70        logging.debug(heapq.nlargest(max_debug_count, colorstuples))
71
72        # getcolors returns a list of (count, color) tuples where count is the
73        # number of times the corresponding color in the image.
74
75        above_thres_tuples = [t for t in colorstuples
76                              if any(pixel > self.pixel_threshold
77                                     for pixel in t[1])]
78
79        logging.debug("Color counts above thres.: %d", len(above_thres_tuples))
80        logging.debug(heapq.nlargest(max_debug_count, above_thres_tuples))
81
82        diff_pixels = sum(t[0] for t in above_thres_tuples)
83
84        return comparison_result.ComparisonResult(diff_pixels, '')
85
86
87    def __enter__(self):
88        return self
89
90
91    def __exit__(self, exc_type, exc_val, exc_tb):
92        pass