From ca43568db4c8e1d54bddeac27fa8e95387476556 Mon Sep 17 00:00:00 2001 From: "ernstm@chromium.org" Date: Fri, 29 Mar 2013 04:09:47 +0000 Subject: cc: Made image comparison for pixel tests error tolerant. Added PixelComparator interface and two implementations of it: ExactPixelComparator and FuzzyPixelComparator. The FuzzyPixelComparator tolerates an adjustable amount of error in the image. BUG=180460 Review URL: https://chromiumcodereview.appspot.com/12558003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191288 0039d316-1c4b-4281-b951-d872f2087c98 --- cc/test/pixel_comparator.cc | 213 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 cc/test/pixel_comparator.cc (limited to 'cc/test/pixel_comparator.cc') diff --git a/cc/test/pixel_comparator.cc b/cc/test/pixel_comparator.cc new file mode 100644 index 0000000..53b1f9d --- /dev/null +++ b/cc/test/pixel_comparator.cc @@ -0,0 +1,213 @@ +// Copyright (c) 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. + +#include "cc/test/pixel_comparator.h" + +#include + +#include "base/logging.h" + +namespace cc { + +ExactPixelComparator::ExactPixelComparator(const bool discard_alpha) + : discard_alpha_(discard_alpha) { +} + +bool ExactPixelComparator::Compare(const SkBitmap& actual_bmp, + const SkBitmap& expected_bmp) const { + // Number of pixels with an error + int error_pixels_count = 0; + + // Check that bitmaps have identical dimensions. + DCHECK(actual_bmp.width() == expected_bmp.width() && + actual_bmp.height() == expected_bmp.height()); + + SkAutoLockPixels lock_actual_bmp(actual_bmp); + SkAutoLockPixels lock_expected_bmp(expected_bmp); + + for (int x = 0; x < actual_bmp.width(); ++x) { + for (int y = 0; y < actual_bmp.height(); ++y) { + SkColor actual_color = actual_bmp.getColor(x, y); + SkColor expected_color = expected_bmp.getColor(x, y); + if (discard_alpha_) { + SkColorSetA(actual_color, 0); + SkColorSetA(expected_color, 0); + } + + if (actual_color != expected_color) { + ++error_pixels_count; + LOG(ERROR) << "Pixel error at x=" << x << " y=" << y << "; " + << "actual RGBA=(" + << SkColorGetR(actual_color) << "," + << SkColorGetG(actual_color) << "," + << SkColorGetB(actual_color) << "," + << SkColorGetA(actual_color) << "); " + << "expected RGBA=(" + << SkColorGetR(expected_color) << "," + << SkColorGetG(expected_color) << "," + << SkColorGetB(expected_color) << "," + << SkColorGetA(expected_color) << ")"; + } + } + } + + if (error_pixels_count != 0) { + LOG(ERROR) << "Number of pixel with an error: " << error_pixels_count; + return false; + } + + return true; +} + +FuzzyPixelComparator::FuzzyPixelComparator( + const bool discard_alpha, + const float error_pixels_percentage_limit, + const float small_error_pixels_percentage_limit, + const float avg_abs_error_limit, + const int max_abs_error_limit, + const int small_error_threshold) + : discard_alpha_(discard_alpha), + error_pixels_percentage_limit_(error_pixels_percentage_limit), + small_error_pixels_percentage_limit_(small_error_pixels_percentage_limit), + avg_abs_error_limit_(avg_abs_error_limit), + max_abs_error_limit_(max_abs_error_limit), + small_error_threshold_(small_error_threshold) { +} + +bool FuzzyPixelComparator::Compare(const SkBitmap& actual_bmp, + const SkBitmap& expected_bmp) const { + // Number of pixels with an error + int error_pixels_count = 0; + // Number of pixels with a small error + int small_error_pixels_count = 0; + // The per channel sums of absolute errors over all pixels. + int64 sum_abs_error_r = 0; + int64 sum_abs_error_g = 0; + int64 sum_abs_error_b = 0; + int64 sum_abs_error_a = 0; + // The per channel maximum absolute errors over all pixels. + int max_abs_error_r = 0; + int max_abs_error_g = 0; + int max_abs_error_b = 0; + int max_abs_error_a = 0; + + // Check that bitmaps have identical dimensions. + DCHECK(actual_bmp.width() == expected_bmp.width() && + actual_bmp.height() == expected_bmp.height()); + + // Check that bitmaps are not empty. + DCHECK(actual_bmp.width() > 0 && actual_bmp.height() > 0); + + SkAutoLockPixels lock_actual_bmp(actual_bmp); + SkAutoLockPixels lock_expected_bmp(expected_bmp); + + for (int x = 0; x < actual_bmp.width(); ++x) { + for (int y = 0; y < actual_bmp.height(); ++y) { + SkColor actual_color = actual_bmp.getColor(x, y); + SkColor expected_color = expected_bmp.getColor(x, y); + if (discard_alpha_) { + SkColorSetA(actual_color, 0); + SkColorSetA(expected_color, 0); + } + + if (actual_color != expected_color) { + ++error_pixels_count; + + LOG(ERROR) << "Pixel error at x=" << x << " y=" << y << "; " + << "actual RGBA=(" + << SkColorGetR(actual_color) << "," + << SkColorGetG(actual_color) << "," + << SkColorGetB(actual_color) << "," + << SkColorGetA(actual_color) << "); " + << "expected RGBA=(" + << SkColorGetR(expected_color) << "," + << SkColorGetG(expected_color) << "," + << SkColorGetB(expected_color) << "," + << SkColorGetA(expected_color) << ")"; + + // Compute per channel errors + int error_r = SkColorGetR(actual_color) - SkColorGetR(expected_color); + int error_g = SkColorGetG(actual_color) - SkColorGetG(expected_color); + int error_b = SkColorGetB(actual_color) - SkColorGetB(expected_color); + int error_a = SkColorGetA(actual_color) - SkColorGetA(expected_color); + int abs_error_r = std::abs(error_r); + int abs_error_g = std::abs(error_g); + int abs_error_b = std::abs(error_b); + int abs_error_a = std::abs(error_a); + + // Increment small error counter if error is below threshold + if (abs_error_r <= small_error_threshold_ && + abs_error_g <= small_error_threshold_ && + abs_error_b <= small_error_threshold_ && + abs_error_a <= small_error_threshold_) + ++small_error_pixels_count; + + // Update per channel maximum absolute errors + max_abs_error_r = std::max(max_abs_error_r, abs_error_r); + max_abs_error_g = std::max(max_abs_error_g, abs_error_g); + max_abs_error_b = std::max(max_abs_error_b, abs_error_b); + max_abs_error_a = std::max(max_abs_error_a, abs_error_a); + + // Update per channel absolute error sums + sum_abs_error_r += abs_error_r; + sum_abs_error_g += abs_error_g; + sum_abs_error_b += abs_error_b; + sum_abs_error_a += abs_error_a; + } + } + } + + // Compute error metrics from collected data + int pixels_count = actual_bmp.width() * actual_bmp.height(); + float error_pixels_percentage = 0.0f; + float small_error_pixels_percentage = 0.0f; + if (pixels_count > 0) { + error_pixels_percentage = static_cast(error_pixels_count) / + pixels_count * 100.0f; + small_error_pixels_percentage = + static_cast(small_error_pixels_count) / pixels_count * 100.0f; + } + float avg_abs_error_r = 0.0f; + float avg_abs_error_g = 0.0f; + float avg_abs_error_b = 0.0f; + float avg_abs_error_a = 0.0f; + if (error_pixels_count > 0) { + avg_abs_error_r = static_cast(sum_abs_error_r) / error_pixels_count; + avg_abs_error_g = static_cast(sum_abs_error_g) / error_pixels_count; + avg_abs_error_b = static_cast(sum_abs_error_b) / error_pixels_count; + avg_abs_error_a = static_cast(sum_abs_error_a) / error_pixels_count; + } + + if (error_pixels_percentage > error_pixels_percentage_limit_ || + small_error_pixels_percentage > small_error_pixels_percentage_limit_ || + avg_abs_error_r > avg_abs_error_limit_ || + avg_abs_error_g > avg_abs_error_limit_ || + avg_abs_error_b > avg_abs_error_limit_ || + avg_abs_error_a > avg_abs_error_limit_ || + max_abs_error_r > max_abs_error_limit_ || + max_abs_error_g > max_abs_error_limit_ || + max_abs_error_b > max_abs_error_limit_ || + max_abs_error_a > max_abs_error_limit_) { + LOG(ERROR) << "Percentage of pixels with an error: " + << error_pixels_percentage; + LOG(ERROR) << "Percentage of pixels with errors not greater than " + << small_error_threshold_ << ": " + << small_error_pixels_percentage; + LOG(ERROR) << "Average absolute error (excluding identical pixels): " + << "R=" << avg_abs_error_r << " " + << "G=" << avg_abs_error_g << " " + << "B=" << avg_abs_error_b << " " + << "A=" << avg_abs_error_a; + LOG(ERROR) << "Largest absolute error: " + << "R=" << max_abs_error_r << " " + << "G=" << max_abs_error_g << " " + << "B=" << max_abs_error_b << " " + << "A=" << max_abs_error_a; + return false; + } else { + return true; + } +} + +} // namespace cc -- cgit v1.1