0029847: Visualization, Image_Diff - Tolerance is not effective for 24/32bit image...
authordipts <tobias@schachte.net>
Wed, 6 Jun 2018 19:42:11 +0000 (22:42 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 14 Jun 2018 11:03:13 +0000 (14:03 +0300)
Image_Color - removed semibroken summ/difference operators.
Image_Diff now uses signed integer for computing differnce between ubyte3 components;
properly compare squared tolerance.

Image_Diff - dropped declaration of Image_ColorXXX24.
RGB color difference is now computed using Chebyshev distance instead of Euclidean distance
Image_PixMap - added methods RawValue()/ChangeRawValue() returning a pointer
to image where specified pixel data is defined.

src/Image/Image_Color.hxx
src/Image/Image_Diff.cxx
src/Image/Image_PixMap.hxx
tests/bugs/vis/bug29847 [new file with mode: 0644]

index fa282be14debb76fb812da4c6d3c7ee53217d2e2..acdfced9c784750a7a58fc83fb0108b15ab3df2c 100644 (file)
@@ -426,144 +426,4 @@ public:
 
 };
 
-//! Addition operator
-template<typename ColorType_t>
-inline ColorType_t Image_ColorSumm3 (const ColorType_t& theA, const ColorType_t& theB)
-{
-  ColorType_t aRes = {{typename ColorType_t::ComponentType_t (theA.v[0] + theB.v[0]),
-                       typename ColorType_t::ComponentType_t (theA.v[1] + theB.v[1]),
-                       typename ColorType_t::ComponentType_t (theA.v[2] + theB.v[2])}};
-  return aRes;
-}
-
-inline Image_ColorRGB operator+ (const Image_ColorRGB& theA, const Image_ColorRGB& theB)
-{
-  return Image_ColorSumm3 (theA, theB);
-}
-
-inline Image_ColorBGR operator+ (const Image_ColorBGR& theA, const Image_ColorBGR& theB)
-{
-  return Image_ColorSumm3 (theA, theB);
-}
-
-inline Image_ColorRGBF operator+ (const Image_ColorRGBF& theA, const Image_ColorRGBF& theB)
-{
-  return Image_ColorSumm3 (theA, theB);
-}
-
-inline Image_ColorBGRF operator+ (const Image_ColorBGRF& theA, const Image_ColorBGRF& theB)
-{
-  return Image_ColorSumm3 (theA, theB);
-}
-
-template<typename ColorType_t>
-inline ColorType_t Image_ColorSumm4 (const ColorType_t& theA, const ColorType_t& theB)
-{
-  ColorType_t aRes = {{typename ColorType_t::ComponentType_t (theA.v[0] + theB.v[0]),
-                       typename ColorType_t::ComponentType_t (theA.v[1] + theB.v[1]),
-                       typename ColorType_t::ComponentType_t (theA.v[2] + theB.v[2]),
-                       typename ColorType_t::ComponentType_t (theA.v[3] + theB.v[3])}};
-  return aRes;
-}
-
-inline Image_ColorRGBA operator+ (const Image_ColorRGBA& theA, const Image_ColorRGBA& theB)
-{
-  return Image_ColorSumm4 (theA, theB);
-}
-
-inline Image_ColorBGRA operator+ (const Image_ColorBGRA& theA, const Image_ColorBGRA& theB)
-{
-  return Image_ColorSumm4 (theA, theB);
-}
-
-inline Image_ColorRGB32 operator+ (const Image_ColorRGB32& theA, const Image_ColorRGB32& theB)
-{
-  return Image_ColorSumm4 (theA, theB);
-}
-
-inline Image_ColorBGR32 operator+ (const Image_ColorBGR32& theA, const Image_ColorBGR32& theB)
-{
-  return Image_ColorSumm4 (theA, theB);
-}
-
-inline Image_ColorRGBAF operator+ (const Image_ColorRGBAF& theA, const Image_ColorRGBAF& theB)
-{
-  return Image_ColorSumm4 (theA, theB);
-}
-
-inline Image_ColorBGRAF operator+ (const Image_ColorBGRAF& theA, const Image_ColorBGRAF& theB)
-{
-  return Image_ColorSumm4 (theA, theB);
-}
-
-//! Subtraction operator
-template<typename ColorType_t>
-inline ColorType_t Image_ColorSub3 (const ColorType_t& theA, const ColorType_t& theB)
-{
-  ColorType_t aRes = {{typename ColorType_t::ComponentType_t (theA.v[0] - theB.v[0]),
-                       typename ColorType_t::ComponentType_t (theA.v[1] - theB.v[1]),
-                       typename ColorType_t::ComponentType_t (theA.v[2] - theB.v[2])}};
-  return aRes;
-}
-
-inline Image_ColorRGB operator- (const Image_ColorRGB& theA, const Image_ColorRGB& theB)
-{
-  return Image_ColorSub3 (theA, theB);
-}
-
-inline Image_ColorBGR operator- (const Image_ColorBGR& theA, const Image_ColorBGR& theB)
-{
-  return Image_ColorSub3 (theA, theB);
-}
-
-inline Image_ColorRGBF operator- (const Image_ColorRGBF& theA, const Image_ColorRGBF& theB)
-{
-  return Image_ColorSub3 (theA, theB);
-}
-
-inline Image_ColorBGRF operator- (const Image_ColorBGRF& theA, const Image_ColorBGRF& theB)
-{
-  return Image_ColorSub3 (theA, theB);
-}
-
-template<typename ColorType_t>
-inline ColorType_t Image_ColorSub4 (const ColorType_t& theA, const ColorType_t& theB)
-{
-  ColorType_t aRes = {{typename ColorType_t::ComponentType_t (theA.v[0] - theB.v[0]),
-                       typename ColorType_t::ComponentType_t (theA.v[1] - theB.v[1]),
-                       typename ColorType_t::ComponentType_t (theA.v[2] - theB.v[2]),
-                       typename ColorType_t::ComponentType_t (theA.v[3] - theB.v[3])}};
-  return aRes;
-}
-
-inline Image_ColorRGBA operator- (const Image_ColorRGBA& theA, const Image_ColorRGBA& theB)
-{
-  return Image_ColorSub4 (theA, theB);
-}
-
-inline Image_ColorBGRA operator- (const Image_ColorBGRA& theA, const Image_ColorBGRA& theB)
-{
-  return Image_ColorSub4 (theA, theB);
-}
-
-inline Image_ColorRGB32 operator- (const Image_ColorRGB32& theA, const Image_ColorRGB32& theB)
-{
-  return Image_ColorSub4 (theA, theB);
-}
-
-inline Image_ColorBGR32 operator- (const Image_ColorBGR32& theA, const Image_ColorBGR32& theB)
-{
-  return Image_ColorSub4 (theA, theB);
-}
-
-inline Image_ColorRGBAF operator- (const Image_ColorRGBAF& theA, const Image_ColorRGBAF& theB)
-{
-  return Image_ColorSub4 (theA, theB);
-}
-
-inline Image_ColorBGRAF operator- (const Image_ColorBGRAF& theA, const Image_ColorBGRAF& theB)
-{
-  return Image_ColorSub4 (theA, theB);
-}
-
 #endif // _Image_Color_H__
index 032fb43526c2600d9158e7ebb888ea163888e3d2..84f0dd704eda03fe095b3ca52808cc284a6e2dcc 100644 (file)
@@ -27,29 +27,6 @@ IMPLEMENT_STANDARD_RTTIEXT(Image_Diff,Standard_Transient)
 namespace
 {
 
-  //! POD structure for packed RGB color value (3 bytes)
-  struct Image_ColorXXX24
-  {
-    Standard_Byte v[3];
-    typedef Standard_Byte ComponentType_t;         //!< Component type
-  };
-
-  static Image_ColorXXX24 operator- (const Image_ColorXXX24& theA,
-                                     const Image_ColorXXX24& theB)
-  {
-    return Image_ColorSub3 (theA, theB);
-  }
-
-  //! Dot squared for difference of two colors
-  static Standard_Integer dotSquared (const Image_ColorXXX24& theColor)
-  {
-    // explicitly convert to integer
-    const Standard_Integer r = theColor.v[0];
-    const Standard_Integer g = theColor.v[1];
-    const Standard_Integer b = theColor.v[2];
-    return r * r + g * g + b * b;
-  }
-
   //! Number of neighbor pixels.
   static const Standard_Size Image_Diff_NbOfNeighborPixels = 8;
 
@@ -78,10 +55,10 @@ namespace
       case Image_Format_RGBA:
       case Image_Format_BGRA:
       {
-        const Image_ColorXXX24& aColor = theData.Value<Image_ColorXXX24> (theY, theX);
-        return aColor.v[0] == 0
-            && aColor.v[1] == 0
-            && aColor.v[2] == 0;
+        const Standard_Byte* aColor = theData.RawValue (theY, theX);
+        return aColor[0] == 0
+            && aColor[1] == 0
+            && aColor[2] == 0;
       }
       default:
       {
@@ -201,15 +178,13 @@ Standard_Integer Image_Diff::Compare()
     case Image_Format_Alpha:
     {
       // Tolerance of comparison operation for color
-      Standard_Integer aDiff = 255;
-      const Standard_Real    aMaxDiffColor  = aDiff * aDiff;
-      const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance);
+      const Standard_Integer aDiffThreshold = Standard_Integer(255.0 * myColorTolerance);
       for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow)
       {
         for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
         {
-          aDiff = Standard_Integer(myImageNew->Value<unsigned char> (aRow, aCol)) - Standard_Integer(myImageRef->Value<unsigned char> (aRow, aCol));
-          if (aDiff * aDiff > aDiffThreshold)
+          const Standard_Integer aDiff = Standard_Integer(myImageNew->Value<unsigned char> (aRow, aCol)) - Standard_Integer(myImageRef->Value<unsigned char> (aRow, aCol));
+          if (Abs (aDiff) > aDiffThreshold)
           {
             myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow));
             ++aNbDiffColors;
@@ -227,9 +202,7 @@ Standard_Integer Image_Diff::Compare()
     {
       // Tolerance of comparison operation for color
       // Maximum difference between colors (white - black) = 100%
-      Image_ColorXXX24 aDiff = {{255, 255, 255}};
-      const Standard_Real    aMaxDiffColor  = dotSquared (aDiff);
-      const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance);
+      const Standard_Integer aDiffThreshold = Standard_Integer(255.0 * myColorTolerance);
 
       // we don't care about RGB/BGR/RGBA/BGRA/RGB32/BGR32 differences
       // because we just compute summ of r g b components
@@ -237,8 +210,13 @@ Standard_Integer Image_Diff::Compare()
       {
         for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
         {
-          aDiff = myImageNew->Value<Image_ColorXXX24> (aRow, aCol) - myImageRef->Value<Image_ColorXXX24> (aRow, aCol);
-          if (dotSquared (aDiff) > aDiffThreshold)
+          // compute Chebyshev distance between two colors
+          const Standard_Byte* aColorRef = myImageRef->RawValue (aRow, aCol);
+          const Standard_Byte* aColorNew = myImageNew->RawValue (aRow, aCol);
+          const int aDiff = NCollection_Vec3<int> (int(aColorRef[0]) - int(aColorNew[0]),
+                                                   int(aColorRef[1]) - int(aColorNew[1]),
+                                                   int(aColorRef[2]) - int(aColorNew[2])).cwiseAbs().maxComp();
+          if (aDiff > aDiffThreshold)
           {
             myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow));
             ++aNbDiffColors;
@@ -251,19 +229,18 @@ Standard_Integer Image_Diff::Compare()
     {
       // Tolerance of comparison operation for color
       // Maximum difference between colors (white - black) = 100%
-      NCollection_Vec3<float> aDiff (1.0f, 1.0f, 1.0f);
-      const Standard_Real    aMaxDiffColor  = aDiff.SquareModulus();
-      const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance);
+      const float aDiffThreshold = float(myColorTolerance);
       for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow)
       {
         for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
         {
+          // compute Chebyshev distance between two colors
           const Quantity_ColorRGBA aPixel1Rgba = myImageRef->PixelColor (Standard_Integer(aCol), Standard_Integer(aRow));
           const Quantity_ColorRGBA aPixel2Rgba = myImageNew->PixelColor (Standard_Integer(aCol), Standard_Integer(aRow));
           const NCollection_Vec3<float>& aPixel1 = aPixel1Rgba.GetRGB();
           const NCollection_Vec3<float>& aPixel2 = aPixel2Rgba.GetRGB();
-          aDiff = aPixel2 - aPixel1;
-          if (aDiff.SquareModulus() > aDiffThreshold)
+          const float aDiff = (aPixel2 - aPixel1).cwiseAbs().maxComp();
+          if (aDiff > aDiffThreshold)
           {
             myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow));
             ++aNbDiffColors;
@@ -304,7 +281,6 @@ Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const
     }
   }
 
-  const Image_ColorXXX24   aWhite24 = {{255, 255, 255}};
   const Quantity_ColorRGBA aWhiteRgba (1.0f, 1.0f, 1.0f, 1.0f);
 
   // initialize black image for dump
@@ -336,7 +312,7 @@ Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const
       {
         for (NCollection_Vector<Standard_Integer>::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next())
         {
-          theDiffImage.ChangeValue<Image_ColorXXX24> (UnpackY(aPixelIter.Value()), UnpackX(aPixelIter.Value())) = aWhite24;
+          memset (theDiffImage.ChangeRawValue (UnpackY(aPixelIter.Value()), UnpackX(aPixelIter.Value())), 255, 3);
         }
         break;
       }
@@ -383,7 +359,7 @@ Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const
         for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next())
         {
           Standard_Integer aDiffPixel (aPixelIter.Key());
-          theDiffImage.ChangeValue<Image_ColorXXX24> (UnpackY(aDiffPixel), UnpackX(aDiffPixel)) = aWhite24;
+          memset (theDiffImage.ChangeValue<Standard_Byte*> (UnpackY(aDiffPixel), UnpackX(aDiffPixel)), 255, 3);
         }
         break;
       }
index 7394af9828c2cc5155942375030baaa44faa2fde..a87ac62be2da59cb3f8c11c661b81a4514a04db4 100644 (file)
@@ -263,6 +263,22 @@ public: //! @name low-level API for batch-processing (pixels reading / compariso
     return *reinterpret_cast<ColorType_t* >(myData.ChangeValue (theRow, theCol));
   }
 
+  //! Access image pixel as raw data pointer.
+  //! This method does not perform any type checks - use on own risk (check Format() before)!
+  const Standard_Byte* RawValue (Standard_Size theRow,
+                                 Standard_Size theCol) const
+  {
+    return myData.Value (theRow, theCol);
+  }
+
+  //! Access image pixel as raw data pointer.
+  //! This method does not perform any type checks - use on own risk (check Format() before)!
+  Standard_Byte* ChangeRawValue (Standard_Size theRow,
+                                 Standard_Size theCol)
+  {
+    return myData.ChangeValue (theRow, theCol);
+  }
+
 public:
 
   Standard_DEPRECATED("This member is deprecated, use Image_Format enumeration instead")
diff --git a/tests/bugs/vis/bug29847 b/tests/bugs/vis/bug29847
new file mode 100644 (file)
index 0000000..e4f1ccf
--- /dev/null
@@ -0,0 +1,18 @@
+puts "============"
+puts "0029847: Visualization, Image_Diff - Tolerance is not effective for 24/32bit image formats"
+puts "============"
+puts ""
+
+pload VISUALIZATION
+vclear
+vinit View1
+vsetcolorbg 127 127 127
+vdump $imagedir/${casename}_127.png
+vsetcolorbg 130 130 130
+vdump $imagedir/${casename}_130.png
+set aNbDiff0  [diffimage $imagedir/${casename}_127.png $imagedir/${casename}_130.png $imagedir/${casename}_0.png  -toleranceOfColor 0]
+set aNbDiff1  [diffimage $imagedir/${casename}_127.png $imagedir/${casename}_130.png $imagedir/${casename}_1.png  -toleranceOfColor 0.1]
+set aNbDiff01 [diffimage $imagedir/${casename}_127.png $imagedir/${casename}_130.png $imagedir/${casename}_01.png -toleranceOfColor 0.01]
+if { $aNbDiff0  != 167281 } { puts "Error: difference with tolerance 0.0  is incorrect" }
+if { $aNbDiff1  != 0      } { puts "Error: difference with tolerance 0.1  is incorrect" }
+if { $aNbDiff01 != 167281 } { puts "Error: difference with tolerance 0.01 is incorrect" }