Fix bug in distanceTransform (#12278)
authoryuki takehara <y.takehara1014@gmail.com>
Mon, 3 Sep 2018 14:18:10 +0000 (23:18 +0900)
committerVadim Pisarevsky <vadim.pisarevsky@gmail.com>
Mon, 3 Sep 2018 14:18:10 +0000 (17:18 +0300)
* fix 12218

* Update test_distancetransform.cpp

marked the test as "BIGDATA_TEST" in order to skip it on low-mem platforms

* modify test

* use a smaller image in the test

* fix test code

modules/imgproc/src/distransform.cpp
modules/imgproc/test/test_distancetransform.cpp

index b573b3d..173e79e 100644 (file)
@@ -45,7 +45,8 @@ namespace cv
 {
 
 static const int DIST_SHIFT = 16;
-static const int INIT_DIST0 = (INT_MAX >> 2);
+static const int INIT_DIST0 = INT_MAX;
+static const int DIST_MAX   = (INT_MAX >> 2);
 #define  CV_FLT_TO_FIX(x,n)  cvRound((x)*(1<<(n)))
 
 static void
@@ -71,8 +72,8 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
 {
     const int BORDER = 1;
     int i, j;
-    const int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
-    const int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
+    const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
+    const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
     const float scale = 1.f/(1 << DIST_SHIFT);
 
     const uchar* src = _src.ptr();
@@ -89,7 +90,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
     for( i = 0; i < size.height; i++ )
     {
         const uchar* s = src + i*srcstep;
-        int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER;
+        unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER;
 
         for( j = 0; j < BORDER; j++ )
             tmp[-j-1] = tmp[size.width + j] = INIT_DIST0;
@@ -100,8 +101,8 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
                 tmp[j] = 0;
             else
             {
-                int t0 = tmp[j-step-1] + DIAG_DIST;
-                int t = tmp[j-step] + HV_DIST;
+                unsigned int t0 = tmp[j-step-1] + DIAG_DIST;
+                unsigned int t = tmp[j-step] + HV_DIST;
                 if( t0 > t ) t0 = t;
                 t = tmp[j-step+1] + DIAG_DIST;
                 if( t0 > t ) t0 = t;
@@ -116,14 +117,14 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
     for( i = size.height - 1; i >= 0; i-- )
     {
         float* d = (float*)(dist + i*dststep);
-        int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER;
+        unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER;
 
         for( j = size.width - 1; j >= 0; j-- )
         {
-            int t0 = tmp[j];
+            unsigned int t0 = tmp[j];
             if( t0 > HV_DIST )
             {
-                int t = tmp[j+step+1] + DIAG_DIST;
+                unsigned int t = tmp[j+step+1] + DIAG_DIST;
                 if( t0 > t ) t0 = t;
                 t = tmp[j+step] + HV_DIST;
                 if( t0 > t ) t0 = t;
@@ -133,6 +134,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
                 if( t0 > t ) t0 = t;
                 tmp[j] = t0;
             }
+            t0 = (t0 > DIST_MAX) ? DIST_MAX : t0;
             d[j] = (float)(t0 * scale);
         }
     }
@@ -144,9 +146,9 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
 {
     const int BORDER = 2;
     int i, j;
-    const int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
-    const int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
-    const int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT );
+    const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
+    const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
+    const unsigned int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT );
     const float scale = 1.f/(1 << DIST_SHIFT);
 
     const uchar* src = _src.ptr();
@@ -163,7 +165,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
     for( i = 0; i < size.height; i++ )
     {
         const uchar* s = src + i*srcstep;
-        int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER;
+        unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER;
 
         for( j = 0; j < BORDER; j++ )
             tmp[-j-1] = tmp[size.width + j] = INIT_DIST0;
@@ -174,8 +176,8 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
                 tmp[j] = 0;
             else
             {
-                int t0 = tmp[j-step*2-1] + LONG_DIST;
-                int t = tmp[j-step*2+1] + LONG_DIST;
+                unsigned int t0 = tmp[j-step*2-1] + LONG_DIST;
+                unsigned int t = tmp[j-step*2+1] + LONG_DIST;
                 if( t0 > t ) t0 = t;
                 t = tmp[j-step-2] + LONG_DIST;
                 if( t0 > t ) t0 = t;
@@ -198,14 +200,14 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
     for( i = size.height - 1; i >= 0; i-- )
     {
         float* d = (float*)(dist + i*dststep);
-        int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER;
+        unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER;
 
         for( j = size.width - 1; j >= 0; j-- )
         {
-            int t0 = tmp[j];
+            unsigned int t0 = tmp[j];
             if( t0 > HV_DIST )
             {
-                int t = tmp[j+step*2+1] + LONG_DIST;
+                unsigned int t = tmp[j+step*2+1] + LONG_DIST;
                 if( t0 > t ) t0 = t;
                 t = tmp[j+step*2-1] + LONG_DIST;
                 if( t0 > t ) t0 = t;
@@ -223,6 +225,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
                 if( t0 > t ) t0 = t;
                 tmp[j] = t0;
             }
+            t0 = (t0 > DIST_MAX) ? DIST_MAX : t0;
             d[j] = (float)(t0 * scale);
         }
     }
@@ -235,9 +238,9 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
     const int BORDER = 2;
 
     int i, j;
-    const int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
-    const int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
-    const int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT );
+    const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
+    const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
+    const unsigned int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT );
     const float scale = 1.f/(1 << DIST_SHIFT);
 
     const uchar* src = _src.ptr();
@@ -256,7 +259,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
     for( i = 0; i < size.height; i++ )
     {
         const uchar* s = src + i*srcstep;
-        int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER;
+        unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER;
         int* lls = (int*)(labels + i*lstep);
 
         for( j = 0; j < BORDER; j++ )
@@ -271,7 +274,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
             }
             else
             {
-                int t0 = INIT_DIST0, t;
+                unsigned int t0 = INIT_DIST0, t;
                 int l0 = 0;
 
                 t = tmp[j-step*2-1] + LONG_DIST;
@@ -333,16 +336,16 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
     for( i = size.height - 1; i >= 0; i-- )
     {
         float* d = (float*)(dist + i*dststep);
-        int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER;
+        unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER;
         int* lls = (int*)(labels + i*lstep);
 
         for( j = size.width - 1; j >= 0; j-- )
         {
-            int t0 = tmp[j];
+            unsigned int t0 = tmp[j];
             int l0 = lls[j];
             if( t0 > HV_DIST )
             {
-                int t = tmp[j+step*2+1] + LONG_DIST;
+                unsigned int t = tmp[j+step*2+1] + LONG_DIST;
                 if( t0 > t )
                 {
                     t0 = t;
@@ -393,6 +396,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
                 tmp[j] = t0;
                 lls[j] = l0;
             }
+            t0 = (t0 > DIST_MAX) ? DIST_MAX : t0;
             d[j] = (float)(t0 * scale);
         }
     }
index de99a08..75ec5ec 100644 (file)
@@ -283,4 +283,23 @@ void CV_DisTransTest::prepare_to_validation( int /*test_case_idx*/ )
 
 TEST(Imgproc_DistanceTransform, accuracy) { CV_DisTransTest test; test.safe_run(); }
 
+BIGDATA_TEST(Imgproc_DistanceTransform, large_image_12218)
+{
+    const int lls_maxcnt = 79992000;   // labels's maximum count
+    const int lls_mincnt = 1;          // labels's minimum count
+    int i, j, nz;
+    Mat src(8000, 20000, CV_8UC1), dst, labels;
+    for( i = 0; i < src.rows; i++ )
+        for( j = 0; j < src.cols; j++ )
+            src.at<uchar>(i, j) = (j > (src.cols / 2)) ? 0 : 255;
+
+    distanceTransform(src, dst, labels, cv::DIST_L2, cv::DIST_MASK_3, DIST_LABEL_PIXEL);
+
+    double scale = (double)lls_mincnt / (double)lls_maxcnt;
+    labels.convertTo(labels, CV_32SC1, scale);
+    Size size = labels.size();
+    nz = cv::countNonZero(labels);
+    EXPECT_EQ(nz, (size.height*size.width / 2));
+}
+
 }} // namespace