Fix bug in distanceTransform (#12278)

* 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
This commit is contained in:
yuki takehara 2018-09-03 23:18:10 +09:00 committed by Vadim Pisarevsky
parent c7cf8fb35c
commit cb7ee27cd9
2 changed files with 49 additions and 26 deletions

View 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);
}
}

View 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