Index: chrome/test/functional/ispy/common/image_tools.py |
=================================================================== |
--- chrome/test/functional/ispy/common/image_tools.py (revision 261231) |
+++ chrome/test/functional/ispy/common/image_tools.py (working copy) |
@@ -1,322 +0,0 @@ |
-# Copyright 2013 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. |
- |
-"""Utilities for performing pixel-by-pixel image comparision.""" |
- |
-import itertools |
-import StringIO |
-from PIL import Image |
- |
- |
-def _AreTheSameSize(images): |
- """Returns whether a set of images are the size size. |
- |
- Args: |
- images: a list of images to compare. |
- |
- Returns: |
- boolean. |
- |
- Raises: |
- Exception: One image or fewer is passed in. |
- """ |
- if len(images) > 1: |
- return all(images[0].size == img.size for img in images[1:]) |
- else: |
- raise Exception('No images passed in.') |
- |
- |
-def _GetDifferenceWithMask(image1, image2, mask=None, |
- masked_color=(225, 225, 225, 255), |
- same_color=(255, 255, 255, 255), |
- different_color=(210, 0, 0, 255)): |
- """Returns an image representing the difference between the two images. |
- |
- This function computes the difference between two images taking into |
- account a mask if it is provided. The final three arguments represent |
- the coloration of the generated image. |
- |
- Args: |
- image1: the first image to compare. |
- image2: the second image to compare. |
- mask: an optional mask image consisting of only black and white pixels |
- where white pixels indicate the portion of the image to be masked out. |
- masked_color: the color of a masked section in the resulting image. |
- same_color: the color of an unmasked section that is the same. |
- between images 1 and 2 in the resulting image. |
- different_color: the color of an unmasked section that is different |
- between images 1 and 2 in the resulting image. |
- |
- Returns: |
- A 2-tuple with an image representing the unmasked difference between the |
- two input images and the number of different pixels. |
- |
- Raises: |
- Exception: if image1, image2, and mask are not the same size. |
- """ |
- image_mask = mask |
- if not mask: |
- image_mask = Image.new('RGBA', image1.size, (0, 0, 0, 255)) |
- if not _AreTheSameSize([image1, image2, image_mask]): |
- raise Exception('images and mask must be the same size.') |
- image_diff = Image.new('RGBA', image1.size, (0, 0, 0, 255)) |
- data = [] |
- diff_pixels = 0 |
- for m, px1, px2 in itertools.izip(image_mask.getdata(), |
- image1.getdata(), |
- image2.getdata()): |
- if m == (255, 255, 255, 255): |
- data.append(masked_color) |
- elif px1 == px2: |
- data.append(same_color) |
- else: |
- data.append(different_color) |
- diff_pixels += 1 |
- |
- image_diff.putdata(data) |
- return (image_diff, diff_pixels) |
- |
- |
-def CreateMask(images): |
- """Computes a mask for a set of images. |
- |
- Returns a difference mask that is computed from the images |
- which are passed in. The mask will have a white pixel |
- anywhere that the input images differ and a black pixel |
- everywhere else. |
- |
- Args: |
- images: list of images to compute the mask from. |
- |
- Returns: |
- an image of only black and white pixels where white pixels represent |
- areas in the input images that have differences. |
- |
- Raises: |
- Exception: if the images passed in are not of the same size. |
- Exception: if fewer than one image is passed in. |
- """ |
- if not images: |
- raise Exception('mask must be created from one or more images.') |
- mask = Image.new('RGBA', images[0].size, (0, 0, 0, 255)) |
- image = images[0] |
- for other_image in images[1:]: |
- mask = _GetDifferenceWithMask( |
- image, |
- other_image, |
- mask, |
- masked_color=(255, 255, 255, 255), |
- same_color=(0, 0, 0, 255), |
- different_color=(255, 255, 255, 255))[0] |
- return mask |
- |
- |
-def AddMasks(masks): |
- """Combines a list of mask images into one mask image. |
- |
- Args: |
- masks: a list of mask-images. |
- |
- Returns: |
- a new mask that represents the sum of the masked |
- regions of the passed in list of mask-images. |
- |
- Raises: |
- Exception: if masks is an empty list, or if masks are not the same size. |
- """ |
- if not masks: |
- raise Exception('masks must be a list containing at least one image.') |
- if len(masks) > 1 and not _AreTheSameSize(masks): |
- raise Exception('masks in list must be of the same size.') |
- white = (255, 255, 255, 255) |
- black = (0, 0, 0, 255) |
- masks_data = [mask.getdata() for mask in masks] |
- image = Image.new('RGBA', masks[0].size, black) |
- image.putdata([white if white in px_set else black |
- for px_set in itertools.izip(*masks_data)]) |
- return image |
- |
- |
-def ConvertDiffToMask(diff): |
- """Converts a Diff image into a Mask image. |
- |
- Args: |
- diff: the diff image to convert. |
- |
- Returns: |
- a new mask image where everything that was masked or different in the diff |
- is now masked. |
- """ |
- white = (255, 255, 255, 255) |
- black = (0, 0, 0, 255) |
- diff_data = diff.getdata() |
- image = Image.new('RGBA', diff.size, black) |
- image.putdata([black if px == white else white for px in diff_data]) |
- return image |
- |
- |
-def VisualizeImageDifferences(image1, image2, mask=None): |
- """Returns an image repesenting the unmasked differences between two images. |
- |
- Iterates through the pixel values of two images and an optional |
- mask. If the pixel values are the same, or the pixel is masked, |
- (0,0,0) is stored for that pixel. Otherwise, (255,255,255) is stored. |
- This ultimately produces an image where unmasked differences between |
- the two images are white pixels, and everything else is black. |
- |
- Args: |
- image1: an RGB image |
- image2: another RGB image of the same size as image1. |
- mask: an optional RGB image consisting of only white and black pixels |
- where the white pixels represent the parts of the images to be masked |
- out. |
- |
- Returns: |
- A 2-tuple with an image representing the unmasked difference between the |
- two input images and the number of different pixels. |
- |
- Raises: |
- Exception: if the two images and optional mask are different sizes. |
- """ |
- return _GetDifferenceWithMask(image1, image2, mask) |
- |
- |
-def InflateMask(image, passes): |
- """A function that adds layers of pixels around the white edges of a mask. |
- |
- This function evaluates a 'frontier' of valid pixels indices. Initially, |
- this frontier contains all indices in the image. However, with each pass |
- only the pixels' indices which were added to the mask by inflation |
- are added to the next pass's frontier. This gives the algorithm a |
- large upfront cost that scales negligably when the number of passes |
- is increased. |
- |
- Args: |
- image: the RGBA PIL.Image mask to inflate. |
- passes: the number of passes to inflate the image by. |
- |
- Returns: |
- A RGBA PIL.Image. |
- """ |
- inflated = Image.new('RGBA', image.size) |
- new_dataset = list(image.getdata()) |
- old_dataset = list(image.getdata()) |
- |
- frontier = set(range(len(old_dataset))) |
- new_frontier = set() |
- |
- l = [-1, 1] |
- |
- def _ShadeHorizontal(index, px): |
- col = index % image.size[0] |
- if px == (255, 255, 255, 255): |
- for x in l: |
- if 0 <= col + x < image.size[0]: |
- if old_dataset[index + x] != (255, 255, 255, 255): |
- new_frontier.add(index + x) |
- new_dataset[index + x] = (255, 255, 255, 255) |
- |
- def _ShadeVertical(index, px): |
- row = index / image.size[0] |
- if px == (255, 255, 255, 255): |
- for x in l: |
- if 0 <= row + x < image.size[1]: |
- if old_dataset[index + image.size[0] * x] != (255, 255, 255, 255): |
- new_frontier.add(index + image.size[0] * x) |
- new_dataset[index + image.size[0] * x] = (255, 255, 255, 255) |
- |
- for _ in range(passes): |
- for index in frontier: |
- _ShadeHorizontal(index, old_dataset[index]) |
- _ShadeVertical(index, old_dataset[index]) |
- old_dataset, new_dataset = new_dataset, new_dataset |
- frontier, new_frontier = new_frontier, set() |
- inflated.putdata(new_dataset) |
- return inflated |
- |
- |
-def TotalDifferentPixels(image1, image2, mask=None): |
- """Computes the number of different pixels between two images. |
- |
- Args: |
- image1: the first RGB image to be compared. |
- image2: the second RGB image to be compared. |
- mask: an optional RGB image of only black and white pixels |
- where white pixels indicate the parts of the image to be masked out. |
- |
- Returns: |
- the number of differing pixels between the images. |
- |
- Raises: |
- Exception: if the images to be compared and the mask are not the same size. |
- """ |
- image_mask = mask |
- if not mask: |
- image_mask = Image.new('RGBA', image1.size, (0, 0, 0, 255)) |
- if _AreTheSameSize([image1, image2, image_mask]): |
- total_diff = 0 |
- for px1, px2, m in itertools.izip(image1.getdata(), |
- image2.getdata(), |
- image_mask.getdata()): |
- if m == (255, 255, 255, 255): |
- continue |
- elif px1 != px2: |
- total_diff += 1 |
- else: |
- continue |
- return total_diff |
- else: |
- raise Exception('images and mask must be the same size') |
- |
- |
-def SameImage(image1, image2, mask=None): |
- """Returns a boolean representing whether the images are the same. |
- |
- Returns a boolean indicating whether two images are similar |
- enough to be considered the same. Essentially wraps the |
- TotalDifferentPixels function. |
- |
- |
- Args: |
- image1: an RGB image to compare. |
- image2: an RGB image to compare. |
- mask: an optional image of only black and white pixels |
- where white pixels are masked out |
- |
- Returns: |
- True if the images are similar, False otherwise. |
- |
- Raises: |
- Exception: if the images (and mask) are different sizes. |
- """ |
- different_pixels = TotalDifferentPixels(image1, image2, mask) |
- return different_pixels == 0 |
- |
- |
-def EncodePNG(image): |
- """Returns the PNG file-contents of the image. |
- |
- Args: |
- image: an RGB image to be encoded. |
- |
- Returns: |
- a base64 encoded string representing the image. |
- """ |
- f = StringIO.StringIO() |
- image.save(f, 'PNG') |
- encoded_image = f.getvalue() |
- f.close() |
- return encoded_image |
- |
- |
-def DecodePNG(png): |
- """Returns a RGB image from PNG file-contents. |
- |
- Args: |
- encoded_image: PNG file-contents of an RGB image. |
- |
- Returns: |
- an RGB image |
- """ |
- return Image.open(StringIO.StringIO(png)) |