blob: 91055233db77894058232ff8995c0652ca235cd9 [file] [log] [blame]
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Classes related to the possible matching algorithms for Skia Gold."""
from typing import List, Union
class Parameters():
"""Constants for Skia Gold algorithm parameters.
These correspond to the constants defined in goldctl's
imgmatching/constants.go.
"""
# The max number of pixels in an image that can differ and still allow the
# fuzzy comparison to pass.
MAX_DIFFERENT_PIXELS = 'fuzzy_max_different_pixels'
# The max RGBA difference between two pixels that is still considered valid.
# For example, if a pixel differs by (1, 2, 3, 0), then the threshold would
# need to be 6 or higher in order for the fuzzy comparison to pass.
PIXEL_DELTA_THRESHOLD = 'fuzzy_pixel_delta_threshold'
# How many pixels along the border of the image to ignore. 0 is typical for
# most tests. 1 is useful for tests that have edges that go all the way to the
# borders of the image, as Sobel filters do not get applied to pixels that are
# on the image border. Larger values are supported, but don't have much of a
# use case.
IGNORED_BORDER_THICKNESS = 'fuzzy_ignored_border_thickness'
# A number in the range [0, 255] specifying how much of an image should be
# blacked out when using a Sobel filter. 0 results in the most pixels being
# blacked out, while 255 results in no pixels being blacked out, i.e. no
# Sobel filter functionality.
EDGE_THRESHOLD = 'sobel_edge_threshold'
class SkiaGoldMatchingAlgorithm():
ALGORITHM_KEY = 'image_matching_algorithm'
"""Abstract base class for all algorithms."""
def GetCmdline(self) -> List[str]:
"""Gets command line parameters for the algorithm.
Returns:
A list of strings representing the algorithm's parameters. The returned
list is suitable for extending an existing goldctl imgtest add
commandline, which will cause goldctl to use the specified algorithm
instead of the default.
"""
return _GenerateOptionalKey(SkiaGoldMatchingAlgorithm.ALGORITHM_KEY,
self.Name())
def Name(self) -> str:
"""Returns a string representation of the algorithm."""
raise NotImplementedError()
class ExactMatchingAlgorithm(SkiaGoldMatchingAlgorithm):
"""Class for the default exact matching algorithm in Gold."""
def GetCmdline(self) -> List[str]:
return []
def Name(self) -> str:
return 'exact'
class FuzzyMatchingAlgorithm(SkiaGoldMatchingAlgorithm):
"""Class for the fuzzy matching algorithm in Gold."""
def __init__(self,
max_different_pixels: int,
pixel_delta_threshold: int,
ignored_border_thickness: int = 0):
super().__init__()
assert int(max_different_pixels) >= 0
assert int(pixel_delta_threshold) >= 0
assert int(ignored_border_thickness) >= 0
self._max_different_pixels = max_different_pixels
self._pixel_delta_threshold = pixel_delta_threshold
self._ignored_border_thickness = ignored_border_thickness
def GetCmdline(self) -> List[str]:
retval = super().GetCmdline()
retval.extend(
_GenerateOptionalKey(Parameters.MAX_DIFFERENT_PIXELS,
self._max_different_pixels))
retval.extend(
_GenerateOptionalKey(Parameters.PIXEL_DELTA_THRESHOLD,
self._pixel_delta_threshold))
retval.extend(
_GenerateOptionalKey(Parameters.IGNORED_BORDER_THICKNESS,
self._ignored_border_thickness))
return retval
def Name(self) -> str:
return 'fuzzy'
class SobelMatchingAlgorithm(FuzzyMatchingAlgorithm):
"""Class for the Sobel filter matching algorithm in Gold.
Technically a superset of the fuzzy matching algorithm.
"""
def __init__(self,
max_different_pixels: int,
pixel_delta_threshold: int,
edge_threshold: int,
ignored_border_thickness: int = 0):
super().__init__(max_different_pixels, pixel_delta_threshold,
ignored_border_thickness)
assert int(edge_threshold) >= 0
assert int(edge_threshold) <= 255
if edge_threshold == 255:
raise RuntimeError(
'Sobel matching with edge threshold set to 255 is the same as fuzzy '
'matching.')
self._edge_threshold = edge_threshold
def GetCmdline(self) -> List[str]:
retval = super().GetCmdline()
retval.extend(
_GenerateOptionalKey(Parameters.EDGE_THRESHOLD, self._edge_threshold))
return retval
def Name(self) -> str:
return 'sobel'
def _GenerateOptionalKey(key: str, value: Union[int, str]) -> List[str]:
return ['--add-test-optional-key', '%s:%s' % (key, value)]