| 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))
|
|
|