Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Unified Diff: tools/telemetry/telemetry/core/_bitmap.py

Issue 668753002: [Telemetry] Migrate bitmap.py from bitmaptools.cc to numpy (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/core/_bitmap.py
diff --git a/tools/telemetry/telemetry/core/bitmap.py b/tools/telemetry/telemetry/core/_bitmap.py
similarity index 62%
rename from tools/telemetry/telemetry/core/bitmap.py
rename to tools/telemetry/telemetry/core/_bitmap.py
index ead38b1c97c7b0dbc8996b0c2fd051a0f1ab0057..606bd376103b2f71cf5710928d99eba6c21857cb 100644
--- a/tools/telemetry/telemetry/core/bitmap.py
+++ b/tools/telemetry/telemetry/core/_bitmap.py
@@ -10,111 +10,19 @@ tools: crop, find bounding box of a color and compute histogram of color values.
import array
import base64
import cStringIO
-import collections
import struct
import subprocess
from telemetry.core import util
from telemetry.core import platform
+from telemetry.image_processing import histogram
+from telemetry.image_processing import rgba_color
from telemetry.util import support_binaries
util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
import png # pylint: disable=F0401
-def HistogramDistance(hist1, hist2):
- """Earth mover's distance.
-
- http://en.wikipedia.org/wiki/Earth_mover's_distance
- First, normalize the two histograms. Then, treat the two histograms as
- piles of dirt, and calculate the cost of turning one pile into the other.
-
- To do this, calculate the difference in one bucket between the two
- histograms. Then carry it over in the calculation for the next bucket.
- In this way, the difference is weighted by how far it has to move."""
- if len(hist1) != len(hist2):
- raise ValueError('Trying to compare histograms '
- 'of different sizes, %s != %s' % (len(hist1), len(hist2)))
-
- n1 = sum(hist1)
- n2 = sum(hist2)
- if n1 == 0:
- raise ValueError('First histogram has 0 pixels in it.')
- if n2 == 0:
- raise ValueError('Second histogram has 0 pixels in it.')
-
- total = 0
- remainder = 0
- for value1, value2 in zip(hist1, hist2):
- remainder += value1 * n2 - value2 * n1
- total += abs(remainder)
- assert remainder == 0, (
- '%s pixel(s) left over after computing histogram distance.'
- % abs(remainder))
- return abs(float(total) / n1 / n2)
-
-
-class ColorHistogram(
- collections.namedtuple('ColorHistogram', ['r', 'g', 'b', 'default_color'])):
- # pylint: disable=W0232
- # pylint: disable=E1002
-
- def __new__(cls, r, g, b, default_color=None):
- return super(ColorHistogram, cls).__new__(cls, r, g, b, default_color)
-
- def Distance(self, other):
- total = 0
- for i in xrange(3):
- hist1 = self[i]
- hist2 = other[i]
-
- if sum(self[i]) == 0:
- if not self.default_color:
- raise ValueError('Histogram has no data and no default color.')
- hist1 = [0] * 256
- hist1[self.default_color[i]] = 1
- if sum(other[i]) == 0:
- if not other.default_color:
- raise ValueError('Histogram has no data and no default color.')
- hist2 = [0] * 256
- hist2[other.default_color[i]] = 1
-
- total += HistogramDistance(hist1, hist2)
- return total
-
-
-class RgbaColor(collections.namedtuple('RgbaColor', ['r', 'g', 'b', 'a'])):
- """Encapsulates an RGBA color retreived from a Bitmap"""
- # pylint: disable=W0232
- # pylint: disable=E1002
-
- def __new__(cls, r, g, b, a=255):
- return super(RgbaColor, cls).__new__(cls, r, g, b, a)
-
- def __int__(self):
- return (self.r << 16) | (self.g << 8) | self.b
-
- def IsEqual(self, expected_color, tolerance=0):
- """Verifies that the color is within a given tolerance of
- the expected color"""
- r_diff = abs(self.r - expected_color.r)
- g_diff = abs(self.g - expected_color.g)
- b_diff = abs(self.b - expected_color.b)
- a_diff = abs(self.a - expected_color.a)
- return (r_diff <= tolerance and g_diff <= tolerance
- and b_diff <= tolerance and a_diff <= tolerance)
-
- def AssertIsRGB(self, r, g, b, tolerance=0):
- assert self.IsEqual(RgbaColor(r, g, b), tolerance)
-
- def AssertIsRGBA(self, r, g, b, a, tolerance=0):
- assert self.IsEqual(RgbaColor(r, g, b, a), tolerance)
-
-
-WEB_PAGE_TEST_ORANGE = RgbaColor(222, 100, 13)
-WHITE = RgbaColor(255, 255, 255)
-
-
class _BitmapTools(object):
"""Wraps a child process of bitmaptools and allows for one command."""
CROP_PIXELS = 0
@@ -164,7 +72,8 @@ class _BitmapTools(object):
out.fromstring(response)
assert len(out) == 768, (
'The ColorHistogram has the wrong number of buckets: %s' % len(out))
- return ColorHistogram(out[:256], out[256:512], out[512:], ignore_color)
+ return histogram.ColorHistogram(out[:256], out[256:512], out[512:],
+ ignore_color)
def BoundingBox(self, color, tolerance):
response = self._RunCommand(_BitmapTools.BOUNDING_BOX, int(color),
@@ -195,17 +104,14 @@ class Bitmap(object):
@property
def bpp(self):
- """Bytes per pixel."""
return self._bpp
@property
def width(self):
- """Width of the bitmap."""
return self._crop_box[2] if self._crop_box else self._width
@property
def height(self):
- """Height of the bitmap."""
return self._crop_box[3] if self._crop_box else self._height
def _PrepareTools(self):
@@ -217,7 +123,6 @@ class Bitmap(object):
@property
def pixels(self):
- """Flat pixel array of the bitmap."""
if self._crop_box:
self._pixels = self._PrepareTools().CropPixels()
# pylint: disable=unpacking-non-sequence
@@ -235,18 +140,13 @@ class Bitmap(object):
return self._metadata
def GetPixelColor(self, x, y):
- """Returns a RgbaColor for the pixel at (x, y)."""
pixels = self.pixels
base = self._bpp * (y * self._width + x)
if self._bpp == 4:
- return RgbaColor(pixels[base + 0], pixels[base + 1],
- pixels[base + 2], pixels[base + 3])
- return RgbaColor(pixels[base + 0], pixels[base + 1],
- pixels[base + 2])
-
- def WritePngFile(self, path):
- with open(path, "wb") as f:
- png.Writer(**self.metadata).write_array(f, self.pixels)
+ return rgba_color.RgbaColor(pixels[base + 0], pixels[base + 1],
+ pixels[base + 2], pixels[base + 3])
+ return rgba_color.RgbaColor(pixels[base + 0], pixels[base + 1],
+ pixels[base + 2])
@staticmethod
def FromPng(png_data):
@@ -258,13 +158,11 @@ class Bitmap(object):
with open(path, "rb") as f:
return Bitmap.FromPng(f.read())
- @staticmethod
- def FromBase64Png(base64_png):
- return Bitmap.FromPng(base64.b64decode(base64_png))
+ def WritePngFile(self, path):
+ with open(path, "wb") as f:
+ png.Writer(**self.metadata).write_array(f, self.pixels)
def IsEqual(self, other, tolerance=0):
- """Determines whether two Bitmaps are identical within a given tolerance."""
-
# Dimensions must be equal
if self.width != other.width or self.height != other.height:
return False
@@ -283,9 +181,6 @@ class Bitmap(object):
return True
def Diff(self, other):
- """Returns a new Bitmap that represents the difference between this image
- and another Bitmap."""
-
# Output dimensions will be the maximum of the two input dimensions
out_width = max(self.width, other.width)
out_height = max(self.height, other.height)
@@ -298,12 +193,12 @@ class Bitmap(object):
if x < self.width and y < self.height:
c0 = self.GetPixelColor(x, y)
else:
- c0 = RgbaColor(0, 0, 0, 0)
+ c0 = rgba_color.RgbaColor(0, 0, 0, 0)
if x < other.width and y < other.height:
c1 = other.GetPixelColor(x, y)
else:
- c1 = RgbaColor(0, 0, 0, 0)
+ c1 = rgba_color.RgbaColor(0, 0, 0, 0)
offset = x * 3
diff[y][offset] = abs(c0.r - c1.r)
@@ -323,13 +218,9 @@ class Bitmap(object):
return diff
def GetBoundingBox(self, color, tolerance=0):
- """Finds the minimum box surrounding all occurences of |color|.
- Returns: (top, left, width, height), match_count
- Ignores the alpha channel."""
return self._PrepareTools().BoundingBox(color, tolerance)
def Crop(self, left, top, width, height):
- """Crops the current bitmap down to the specified box."""
cur_box = self._crop_box or (0, 0, self._width, self._height)
cur_left, cur_top, cur_width, cur_height = cur_box
@@ -342,12 +233,4 @@ class Bitmap(object):
return self
def ColorHistogram(self, ignore_color=None, tolerance=0):
- """Computes a histogram of the pixel colors in this Bitmap.
- Args:
- ignore_color: An RgbaColor to exclude from the bucket counts.
- tolerance: A tolerance for the ignore_color.
-
- Returns:
- A ColorHistogram namedtuple with 256 integers in each field: r, g, and b.
- """
return self._PrepareTools().Histogram(ignore_color, tolerance)

Powered by Google App Engine
This is Rietveld 408576698