Explorar o código

ENH: Add testing utility to compare images with a threshold

Add testing utility to compare images with a threshold. This can be useful to
ignore small differences between test images and baseline images due to varying
behavior between Qt versions or platforms.
Max Smolens %!s(int64=7) %!d(string=hai) anos
pai
achega
690faed67e

+ 2 - 0
Libs/Widgets/CMakeLists.txt

@@ -197,6 +197,8 @@ set(KIT_SRCS
   ctkTransferFunctionView.h
   ctkTreeComboBox.cpp
   ctkTreeComboBox.h
+  ctkWidgetsTestingUtilities.cpp
+  ctkWidgetsTestingUtilities.h
   ctkWidgetsUtils.cpp
   ctkWidgetsUtils.h
   ctkWorkflowAbstractPagedWidget.cpp

+ 2 - 0
Libs/Widgets/Testing/Cpp/CMakeLists.txt

@@ -102,6 +102,7 @@ set(TEST_SOURCES
   ctkTransferFunctionRepresentationTest1.cpp
   ctkTransferFunctionRepresentationTest2.cpp
   ctkTreeComboBoxTest1.cpp
+  ctkWidgetsTestingUtilitiesTest.cpp
   ctkWidgetsUtilsTest1.cpp
   ctkWidgetsUtilsTestGrabWidget.cpp
   ctkWorkflowWidgetTest1.cpp
@@ -364,6 +365,7 @@ SIMPLE_TEST( ctkTransferFunctionTest1 )
 SIMPLE_TEST( ctkTransferFunctionRepresentationTest1 )
 SIMPLE_TEST( ctkTransferFunctionRepresentationTest2 )
 SIMPLE_TEST( ctkTreeComboBoxTest1 )
+SIMPLE_TEST( ctkWidgetsTestingUtilitiesTest )
 SIMPLE_TEST( ctkWidgetsUtilsTest1 )
 SIMPLE_TEST( ctkWidgetsUtilsTestGrabWidget )
 SIMPLE_TEST( ctkWorkflowWidgetTest1 )

+ 175 - 0
Libs/Widgets/Testing/Cpp/ctkWidgetsTestingUtilitiesTest.cpp

@@ -0,0 +1,175 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkWidgetsTestingUtilities.h"
+
+// Qt includes
+#include <QColor>
+#include <QImage>
+
+// STD includes
+#include <iostream>
+
+using namespace ctkWidgetsTestingUtilities;
+
+//----------------------------------------------------------------------------
+bool TestCheckCompareImages();
+
+//----------------------------------------------------------------------------
+int ctkWidgetsTestingUtilitiesTest(int , char * [])
+{
+  bool res = true;
+  res = res && TestCheckCompareImages();
+  return res ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+//----------------------------------------------------------------------------
+bool TestCheckCompareImages()
+{
+  {
+    // Invalid format
+    QImage a(1, 1, QImage::Format_Invalid);
+    QImage b(1, 1, QImage::Format_Invalid);
+    if (CompareImages(a, b))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+  }
+
+  {
+    // Unsupported format
+    QImage a(1, 1, QImage::Format_ARGB32);
+    QImage b(1, 1, QImage::Format_ARGB32);
+    if (CompareImages(a, b))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+  }
+
+  {
+    // One image in unsupported format
+    QImage a(1, 1, QImage::Format_RGB32);
+    QImage b(1, 1, QImage::Format_ARGB32);
+    if (CompareImages(a, b))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    QImage c(1, 1, QImage::Format_ARGB32);
+    QImage d(1, 1, QImage::Format_RGB32);
+    if (CompareImages(c, d))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+  }
+
+  {
+    // Images of different size
+    QImage a(1, 1, QImage::Format_RGB32);
+    QImage b(2, 1, QImage::Format_RGB32);
+    if (CompareImages(a, b))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    QImage c(1, 2, QImage::Format_ARGB32);
+    QImage d(1, 1, QImage::Format_RGB32);
+    if (CompareImages(c, d))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+  }
+
+  {
+    // Identical images
+    QImage a(10, 10, QImage::Format_RGB32);
+    a.fill(Qt::green);
+    QImage b(10, 10, QImage::Format_RGB32);
+    b.fill(Qt::green);
+    if (!CompareImages(a, b, 0.0f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Change one pixel in first image
+    a.setPixel(2, 3, qRgb(255, 0, 0));
+    if (CompareImages(a, b, 0.f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Compare with percent threshold not met
+    if (CompareImages(a, b, 0.5f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Compare with percent threshold met
+    if (!CompareImages(a, b, 1.f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Change one pixel in other image
+    // Compare with percent threshold not met
+    b.setPixel(4, 5, qRgb(255, 255, 0));
+    if (CompareImages(a, b, 1.f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Compare with percent threshold met
+    if (!CompareImages(a, b, 2.f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Change one pixel in first image to match second image
+    a.setPixel(4, 5, qRgb(255, 255, 0));
+    if (!CompareImages(a, b, 1.f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+
+    // Identical images
+    b.setPixel(2, 3, qRgb(255, 0, 0));
+    if (!CompareImages(a, b, 0.f))
+      {
+      std::cerr << "Line " << __LINE__ << " - CompareImages failed" << std::endl;
+      return false;
+      }
+  }
+
+  return true;
+}

+ 53 - 0
Libs/Widgets/ctkWidgetsTestingUtilities.cpp

@@ -0,0 +1,53 @@
+// CTK includes
+#include "ctkWidgetsTestingUtilities.h"
+
+// Qt includes
+#include <QImage>
+
+// STD includes
+#include <iostream>
+
+namespace ctkWidgetsTestingUtilities
+{
+//---------------------------------------------------------------------------- */
+bool CompareImages(const QImage& current, const QImage& expected,
+                   float percentThreshold)
+{
+  if (current.width() != expected.width() ||
+      current.height() != expected.height() ||
+      current.format() != expected.format())
+    {
+    return false;
+    }
+
+  if (current.format() != QImage::Format_RGB32)
+    {
+    std::cerr << "ERROR: CompareImages: Unsupported QImage::Format: "
+              << static_cast<int>(current.format()) << std::endl;
+    return false;
+    }
+
+  // Compute number of pixels that differ, masking out alpha channel.
+  // Based on QImage::operator== implementation.
+  unsigned long numDiffs = 0;
+  for (int line = 0; line < current.height(); line++)
+    {
+    int w = current.width();
+    const QRgb* p1 = reinterpret_cast<const QRgb*>(current.constScanLine(line));
+    const QRgb* p2 = reinterpret_cast<const QRgb*>(expected.constScanLine(line));
+    while (w--)
+      {
+      if ((*p1++ & 0x00ffffff) != (*p2++ & 0x00ffffff))
+        {
+        ++numDiffs;
+        }
+      }
+    }
+
+  const int numPixels = current.width() * current.height();
+  const float percentDifferent = (numPixels > 0) ? ((100.f * numDiffs) / numPixels) : 0.f;
+  return (percentDifferent <= percentThreshold);
+}
+
+} // namespace ctkWidgetsTestingUtilities
+

+ 42 - 0
Libs/Widgets/ctkWidgetsTestingUtilities.h

@@ -0,0 +1,42 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkWidgetsTestingUtilities_h
+#define __ctkWidgetsTestingUtilities_h
+
+// CTK includes
+#include <ctkWidgetsExport.h>
+
+class QImage;
+
+/// This module provides functions to facilitate writing tests.
+
+namespace ctkWidgetsTestingUtilities
+{
+
+/// Compare two images for pixel-level similarity. Allow for the specified
+/// percentage of pixels to be different.
+CTK_WIDGETS_EXPORT
+bool CompareImages(const QImage& current, const QImage& expected,
+                   float percentThreshold=0.f);
+
+} // namespace ctkWidgetsTestingUtilities
+
+#endif