1# Copyright 2014 The Chromium 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 5from __future__ import division 6 7from telemetry.internal.util import external_modules 8from telemetry.util import color_histogram 9from telemetry.util import rgba_color 10import png 11 12cv2 = external_modules.ImportOptionalModule('cv2') 13np = external_modules.ImportRequiredModule('numpy') 14 15 16def Channels(image): 17 return image.shape[2] 18 19def Width(image): 20 return image.shape[1] 21 22def Height(image): 23 return image.shape[0] 24 25def Pixels(image): 26 return bytearray(np.uint8(image[:, :, ::-1]).flat) # Convert from bgr to rgb. 27 28def GetPixelColor(image, x, y): 29 bgr = image[y][x] 30 return rgba_color.RgbaColor(bgr[2], bgr[1], bgr[0]) 31 32def WritePngFile(image, path): 33 if cv2 is not None: 34 cv2.imwrite(path, image) 35 else: 36 with open(path, "wb") as f: 37 metadata = {} 38 metadata['size'] = (Width(image), Height(image)) 39 metadata['alpha'] = False 40 metadata['bitdepth'] = 8 41 img = image[:, :, ::-1] 42 pixels = img.reshape(-1).tolist() 43 png.Writer(**metadata).write_array(f, pixels) 44 45def FromRGBPixels(width, height, pixels, bpp): 46 img = np.array(pixels, order='F', dtype=np.uint8) 47 img.resize((height, width, bpp)) 48 if bpp == 4: 49 img = img[:, :, :3] # Drop alpha. 50 return img[:, :, ::-1] # Convert from rgb to bgr. 51 52def FromPngFile(path): 53 if cv2 is not None: 54 img = cv2.imread(path, cv2.CV_LOAD_IMAGE_COLOR) 55 if img is None: 56 raise ValueError('Image at path {0} could not be read'.format(path)) 57 return img 58 else: 59 with open(path, "rb") as f: 60 return FromPng(f.read()) 61 62def FromPng(png_data): 63 if cv2 is not None: 64 file_bytes = np.asarray(bytearray(png_data), dtype=np.uint8) 65 return cv2.imdecode(file_bytes, cv2.CV_LOAD_IMAGE_COLOR) 66 else: 67 width, height, pixels, meta = png.Reader(bytes=png_data).read_flat() 68 return FromRGBPixels(width, height, pixels, 4 if meta['alpha'] else 3) 69 70def _SimpleDiff(image1, image2): 71 if cv2 is not None: 72 return cv2.absdiff(image1, image2) 73 else: 74 amax = np.maximum(image1, image2) 75 amin = np.minimum(image1, image2) 76 return amax - amin 77 78def AreEqual(image1, image2, tolerance, likely_equal): 79 if image1.shape != image2.shape: 80 return False 81 self_image = image1 82 other_image = image2 83 if tolerance: 84 if likely_equal: 85 return np.amax(_SimpleDiff(image1, image2)) <= tolerance 86 else: 87 for row in xrange(Height(image1)): 88 if np.amax(_SimpleDiff(image1[row], image2[row])) > tolerance: 89 return False 90 return True 91 else: 92 if likely_equal: 93 return (self_image == other_image).all() 94 else: 95 for row in xrange(Height(image1)): 96 if not (self_image[row] == other_image[row]).all(): 97 return False 98 return True 99 100def Diff(image1, image2): 101 self_image = image1 102 other_image = image2 103 if image1.shape[2] != image2.shape[2]: 104 raise ValueError('Cannot diff images of differing bit depth') 105 if image1.shape[:2] != image2.shape[:2]: 106 width = max(Width(image1), Width(image2)) 107 height = max(Height(image1), Height(image2)) 108 self_image = np.zeros((width, height, image1.shape[2]), np.uint8) 109 other_image = np.zeros((width, height, image1.shape[2]), np.uint8) 110 self_image[0:Height(image1), 0:Width(image1)] = image1 111 other_image[0:Height(image2), 0:Width(image2)] = image2 112 return _SimpleDiff(self_image, other_image) 113 114def GetBoundingBox(image, color, tolerance): 115 if cv2 is not None: 116 color = np.array([color.b, color.g, color.r]) 117 img = cv2.inRange(image, np.subtract(color[0:3], tolerance), 118 np.add(color[0:3], tolerance)) 119 count = cv2.countNonZero(img) 120 if count == 0: 121 return None, 0 122 contours, _ = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) 123 contour = np.concatenate(contours) 124 return cv2.boundingRect(contour), count 125 else: 126 if tolerance: 127 color = np.array([color.b, color.g, color.r]) 128 colorm = color - tolerance 129 colorp = color + tolerance 130 b = image[:, :, 0] 131 g = image[:, :, 1] 132 r = image[:, :, 2] 133 w = np.where(((b >= colorm[0]) & (b <= colorp[0]) & 134 (g >= colorm[1]) & (g <= colorp[1]) & 135 (r >= colorm[2]) & (r <= colorp[2]))) 136 else: 137 w = np.where((image[:, :, 0] == color.b) & 138 (image[:, :, 1] == color.g) & 139 (image[:, :, 2] == color.r)) 140 if len(w[0]) == 0: 141 return None, 0 142 return (w[1][0], w[0][0], w[1][-1] - w[1][0] + 1, w[0][-1] - w[0][0] + 1), \ 143 len(w[0]) 144 145def Crop(image, left, top, width, height): 146 img_height, img_width = image.shape[:2] 147 if (left < 0 or top < 0 or 148 (left + width) > img_width or 149 (top + height) > img_height): 150 raise ValueError('Invalid dimensions') 151 return image[top:top + height, left:left + width] 152 153def GetColorHistogram(image, ignore_color, tolerance): 154 if cv2 is not None: 155 mask = None 156 if ignore_color is not None: 157 color = np.array([ignore_color.b, ignore_color.g, ignore_color.r]) 158 mask = ~cv2.inRange(image, np.subtract(color, tolerance), 159 np.add(color, tolerance)) 160 161 flatten = np.ndarray.flatten 162 hist_b = flatten(cv2.calcHist([image], [0], mask, [256], [0, 256])) 163 hist_g = flatten(cv2.calcHist([image], [1], mask, [256], [0, 256])) 164 hist_r = flatten(cv2.calcHist([image], [2], mask, [256], [0, 256])) 165 else: 166 filtered = image.reshape(-1, 3) 167 if ignore_color is not None: 168 color = np.array([ignore_color.b, ignore_color.g, ignore_color.r]) 169 colorm = np.array(color) - tolerance 170 colorp = np.array(color) + tolerance 171 in_range = ((filtered[:, 0] < colorm[0]) | (filtered[:, 0] > colorp[0]) | 172 (filtered[:, 1] < colorm[1]) | (filtered[:, 1] > colorp[1]) | 173 (filtered[:, 2] < colorm[2]) | (filtered[:, 2] > colorp[2])) 174 filtered = np.compress(in_range, filtered, axis=0) 175 if len(filtered[:, 0]) == 0: 176 return color_histogram.ColorHistogram(np.zeros((256)), np.zeros((256)), 177 np.zeros((256)), ignore_color) 178 hist_b = np.bincount(filtered[:, 0], minlength=256) 179 hist_g = np.bincount(filtered[:, 1], minlength=256) 180 hist_r = np.bincount(filtered[:, 2], minlength=256) 181 182 return color_histogram.ColorHistogram(hist_r, hist_g, hist_b, ignore_color) 183