| Index: tools/telemetry/telemetry/image_processing/image_util_numpy_impl.py
|
| diff --git a/tools/telemetry/telemetry/image_processing/image_util_numpy_impl.py b/tools/telemetry/telemetry/image_processing/image_util_numpy_impl.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7bb50fe9e7c1edd146f507682c006a8d9492387d
|
| --- /dev/null
|
| +++ b/tools/telemetry/telemetry/image_processing/image_util_numpy_impl.py
|
| @@ -0,0 +1,183 @@
|
| +# Copyright 2014 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.
|
| +
|
| +from __future__ import division
|
| +
|
| +from telemetry.core import util
|
| +from telemetry.image_processing import histogram
|
| +from telemetry.image_processing import rgba_color
|
| +from telemetry.util import external_modules
|
| +
|
| +util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
|
| +import png # pylint: disable=F0401
|
| +
|
| +cv2 = external_modules.ImportOptionalModule('cv2')
|
| +np = external_modules.ImportRequiredModule('numpy')
|
| +
|
| +
|
| +def Channels(image):
|
| + return image.shape[2]
|
| +
|
| +def Width(image):
|
| + return image.shape[1]
|
| +
|
| +def Height(image):
|
| + return image.shape[0]
|
| +
|
| +def Pixels(image):
|
| + return bytearray(np.uint8(image[:, :, ::-1]).flat) # Convert from bgr to rgb.
|
| +
|
| +def GetPixelColor(image, x, y):
|
| + bgr = image[y][x]
|
| + return rgba_color.RgbaColor(bgr[2], bgr[1], bgr[0])
|
| +
|
| +def WritePngFile(image, path):
|
| + assert(path.endswith('png'))
|
| + if cv2 is not None:
|
| + cv2.imwrite(path, image)
|
| + else:
|
| + with open(path, "wb") as f:
|
| + metadata = {}
|
| + metadata['size'] = (Width(image), Height(image))
|
| + metadata['alpha'] = False
|
| + metadata['bitdepth'] = 8
|
| + img = image[:, :, ::-1]
|
| + pixels = img.reshape(-1).tolist()
|
| + png.Writer(**metadata).write_array(f, pixels)
|
| +
|
| +def FromRGBPixels(width, height, pixels, bpp):
|
| + img = np.array(pixels, order='F', dtype=np.uint8)
|
| + img.resize((height, width, bpp))
|
| + if bpp == 4:
|
| + img = img[:, :, :3] # Drop alpha.
|
| + return img[:, :, ::-1] # Convert from rgb to bgr.
|
| +
|
| +def FromPngFile(path):
|
| + if cv2 is not None:
|
| + img = cv2.imread(path, cv2.CV_LOAD_IMAGE_COLOR)
|
| + if img is None:
|
| + raise ValueError('Image at path {0} could not be read'.format(path))
|
| + return img
|
| + else:
|
| + with open(path, "rb") as f:
|
| + return FromPng(f.read())
|
| +
|
| +def FromPng(png_data):
|
| + if cv2 is not None:
|
| + file_bytes = np.asarray(bytearray(png_data), dtype=np.uint8)
|
| + return cv2.imdecode(file_bytes, cv2.CV_LOAD_IMAGE_COLOR)
|
| + else:
|
| + width, height, pixels, meta = png.Reader(bytes=png_data).read_flat()
|
| + return FromRGBPixels(width, height, pixels, 4 if meta['alpha'] else 3)
|
| +
|
| +def _SimpleDiff(image1, image2):
|
| + if cv2 is not None:
|
| + return cv2.absdiff(image1, image2)
|
| + else:
|
| + amax = np.maximum(image1, image2)
|
| + amin = np.minimum(image1, image2)
|
| + return amax - amin
|
| +
|
| +def AreEqual(image1, image2, tolerance, likely_equal):
|
| + if image1.shape != image2.shape:
|
| + return False
|
| + self_image = image1
|
| + other_image = image2
|
| + if tolerance:
|
| + if likely_equal:
|
| + return np.amax(_SimpleDiff(image1, image2)) <= tolerance
|
| + else:
|
| + for row in xrange(Height(image1)):
|
| + if np.amax(_SimpleDiff(image1[row], image2[row])) > tolerance:
|
| + return False
|
| + return True
|
| + else:
|
| + if likely_equal:
|
| + return (self_image == other_image).all()
|
| + else:
|
| + for row in xrange(Height(image1)):
|
| + if not (self_image[row] == other_image[row]).all():
|
| + return False
|
| + return True
|
| +
|
| +def Diff(image1, image2):
|
| + self_image = image1
|
| + other_image = image2
|
| + if image1.shape[2] != image2.shape[2]:
|
| + raise ValueError('Cannot diff images of differing bit depth')
|
| + if image1.shape[:2] != image2.shape[:2]:
|
| + width = max(Width(image1), Width(image2))
|
| + height = max(Height(image1), Height(image2))
|
| + self_image = np.zeros((width, height, image1.shape[2]), np.uint8)
|
| + other_image = np.zeros((width, height, image1.shape[2]), np.uint8)
|
| + self_image[0:Height(image1), 0:Width(image1)] = image1
|
| + other_image[0:Height(image2), 0:Width(image2)] = image2
|
| + return _SimpleDiff(self_image, other_image)
|
| +
|
| +def GetBoundingBox(image, color, tolerance):
|
| + if cv2 is not None:
|
| + color = np.array([color.b, color.g, color.r])
|
| + img = cv2.inRange(image, np.subtract(color[0:3], tolerance),
|
| + np.add(color[0:3], tolerance))
|
| + count = cv2.countNonZero(img)
|
| + if count == 0:
|
| + return None, 0
|
| + contours, _ = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
|
| + contour = np.concatenate(contours)
|
| + return cv2.boundingRect(contour), count
|
| + else:
|
| + if tolerance:
|
| + color = np.array([color.b, color.g, color.r])
|
| + colorm = color - tolerance
|
| + colorp = color + tolerance
|
| + b = image[:, :, 0]
|
| + g = image[:, :, 1]
|
| + r = image[:, :, 2]
|
| + w = np.where(((b >= colorm[0]) & (b <= colorp[0]) &
|
| + (g >= colorm[1]) & (g <= colorp[1]) &
|
| + (r >= colorm[2]) & (r <= colorp[2])))
|
| + else:
|
| + w = np.where((image[:, :, 0] == color.b) &
|
| + (image[:, :, 1] == color.g) &
|
| + (image[:, :, 2] == color.r))
|
| + if len(w[0]) == 0:
|
| + return None, 0
|
| + return (w[1][0], w[0][0], w[1][-1] - w[1][0] + 1, w[0][-1] - w[0][0] + 1), \
|
| + len(w[0])
|
| +
|
| +def Crop(image, left, top, width, height):
|
| + img_height, img_width = image.shape[:2]
|
| + if (left < 0 or top < 0 or
|
| + (left + width) > img_width or
|
| + (top + height) > img_height):
|
| + raise ValueError('Invalid dimensions')
|
| + return image[top:top + height, left:left + width]
|
| +
|
| +def GetColorHistogram(image, ignore_color, tolerance):
|
| + if cv2 is not None:
|
| + mask = None
|
| + if ignore_color is not None:
|
| + color = np.array([ignore_color.b, ignore_color.g, ignore_color.r])
|
| + mask = ~cv2.inRange(image, np.subtract(color, tolerance),
|
| + np.add(color, tolerance))
|
| +
|
| + flatten = np.ndarray.flatten
|
| + hist_b = flatten(cv2.calcHist([image], [0], mask, [256], [0, 256]))
|
| + hist_g = flatten(cv2.calcHist([image], [1], mask, [256], [0, 256]))
|
| + hist_r = flatten(cv2.calcHist([image], [2], mask, [256], [0, 256]))
|
| + else:
|
| + filtered = image.reshape(-1, 3)
|
| + if ignore_color is not None:
|
| + color = np.array([ignore_color.b, ignore_color.g, ignore_color.r])
|
| + colorm = np.array(color) - tolerance
|
| + colorp = np.array(color) + tolerance
|
| + in_range = ((filtered[:, 0] < colorm[0]) | (filtered[:, 0] > colorp[0]) |
|
| + (filtered[:, 1] < colorm[1]) | (filtered[:, 1] > colorp[1]) |
|
| + (filtered[:, 2] < colorm[2]) | (filtered[:, 2] > colorp[2]))
|
| + filtered = np.compress(in_range, filtered, axis = 0)
|
| + hist_b = np.bincount(filtered[:, 0], minlength=256)
|
| + hist_g = np.bincount(filtered[:, 1], minlength=256)
|
| + hist_r = np.bincount(filtered[:, 2], minlength=256)
|
| +
|
| + return histogram.ColorHistogram(hist_r, hist_g, hist_b, ignore_color)
|
|
|