| #!/usr/bin/env vpython3 |
| # 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. |
| |
| import argparse |
| import logging |
| import sys |
| |
| import gold_inexact_matching.binary_search_parameter_optimizer\ |
| as binary_optimizer |
| import gold_inexact_matching.brute_force_parameter_optimizer as brute_optimizer |
| import gold_inexact_matching.local_minima_parameter_optimizer\ |
| as local_optimizer |
| from gold_inexact_matching import optimizer_set |
| |
| # Script to find suitable values for Skia Gold inexact matching. |
| # |
| # Inexact matching in Skia Gold has three tunable parameters: |
| # 1. The max number of differing pixels. |
| # 2. The max delta for any single pixel. |
| # 3. The threshold for a Sobel filter. |
| # |
| # Ideally, we use the following hierarchy of comparison approaches: |
| # 1. Exact matching. |
| # 2. Exact matching after a Sobel filter is applied. |
| # 3. Fuzzy matching after a Sobel filter is applied. |
| # |
| # However, there may be cases where only using a Sobel filter requires masking a |
| # very large amount of the image compared to Sobel + very conservative fuzzy |
| # matching. |
| # |
| # Even if such cases are not hit, the process of determining good values for the |
| # parameters is quite tedious since it requires downloading images from Gold and |
| # manually running multiple calls to `goldctl match`. |
| # |
| # This script attempts to remedy both issues by handling all of the trial and |
| # error and suggesting potential parameter values for the user to choose from. |
| |
| |
| def CreateArgumentParser(): |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| script_parser = parser.add_argument_group('Script Arguments') |
| script_parser.add_argument('-v', |
| '--verbose', |
| dest='verbose_count', |
| default=0, |
| action='count', |
| help='Verbose level (multiple times for more') |
| |
| subparsers = parser.add_subparsers(help='Optimization algorithm') |
| |
| binary_parser = subparsers.add_parser( |
| 'binary_search', |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
| help='Perform a binary search to optimize a single parameter. The best ' |
| 'option if you only want to tune one parameter.') |
| binary_parser.set_defaults( |
| clazz=binary_optimizer.BinarySearchParameterOptimizer) |
| binary_optimizer.BinarySearchParameterOptimizer.AddArguments(binary_parser) |
| |
| local_parser = subparsers.add_parser( |
| 'local_minima', |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
| help='Perform a BFS to find local minima using weights for each ' |
| 'parameter. Slower than binary searching, but supports an arbitrary ' |
| 'number of parameters.') |
| local_parser.set_defaults(clazz=local_optimizer.LocalMinimaParameterOptimizer) |
| local_optimizer.LocalMinimaParameterOptimizer.AddArguments(local_parser) |
| |
| brute_parser = subparsers.add_parser( |
| 'brute_force', |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
| help='Brute force all possible combinations. VERY, VERY slow, but can ' |
| 'potentially find better values than local_minima.') |
| brute_parser.set_defaults(clazz=brute_optimizer.BruteForceParameterOptimizer) |
| brute_optimizer.BruteForceParameterOptimizer.AddArguments(brute_parser) |
| |
| return parser |
| |
| |
| def SetLoggingVerbosity(args): |
| logger = logging.getLogger() |
| if args.verbose_count == 0: |
| logger.setLevel(logging.WARNING) |
| elif args.verbose_count == 1: |
| logger.setLevel(logging.INFO) |
| else: |
| logger.setLevel(logging.DEBUG) |
| |
| |
| def main(): |
| parser = CreateArgumentParser() |
| args = parser.parse_args() |
| SetLoggingVerbosity(args) |
| optimizer = optimizer_set.OptimizerSet(args, args.clazz) |
| optimizer.RunOptimization() |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |