Merge pull request #24234 from dkurt:distanceTransform_max_dist

Change max distance at distanceTransform #24234

### Pull Request Readiness Checklist

resolves https://github.com/opencv/opencv/issues/23895
related: https://github.com/opencv/opencv/pull/12278

* DIST_MASK_3 and DIST_MASK_5 maximal distance increased from 8192 to 65533 +/- 1
* Fix squares processing at DIST_MASK_PRECISE
* - [ ] TODO: Check with IPP

```cpp
    cv::Mat gray = cv::imread("opencv/samples/data/stuff.jpg", cv::ImreadModes::IMREAD_GRAYSCALE);

    cv::Mat gray_resize;
    cv::resize(gray, gray_resize, cv::Size(70000,70000), 0.0, 0.0, cv::INTER_LINEAR);

    gray_resize = gray_resize >= 100;

    cv::Mat dist;
    cv::distanceTransform(gray_resize, dist, cv::DIST_L2, cv::DIST_MASK_5, CV_32F);

    double minVal, maxVal;
    minMaxLoc(dist, &minVal, &maxVal);
    dist = 255 * (dist - minVal) / (maxVal - minVal);
    std::cout << minVal << " " << maxVal << std::endl;

    cv::Mat dist_resize;
    cv::resize(dist, dist_resize, cv::Size(1024,1024), 0.0, 0.0, cv::INTER_LINEAR);

    cv::String outfilePath = "test_mask_5.png";
    cv::imwrite(outfilePath, dist_resize);
```

mask | 4.x | PR |
----------|--------------|--------------
DIST_MASK_3 | <img src="https://github.com/opencv/opencv/assets/25801568/23e5de76-a8ba-4eb8-ab03-fa55672834be" width="128"> | <img src="https://github.com/opencv/opencv/assets/25801568/e1149f6a-49d6-47bd-a2a8-20bb7e4dafa4" width="128"> |
DIST_MASK_5 | <img src="https://github.com/opencv/opencv/assets/25801568/98aba29b-8865-4b9a-8066-669b16d175c9" width="128"> | <img src="https://github.com/opencv/opencv/assets/25801568/54f62ed2-9ef6-485f-bd63-48cc96accd7d" width="128"> |
DIST_MASK_PRECISE | <img src="https://github.com/opencv/opencv/assets/25801568/c4d79451-fd7a-461f-98fc-13060c63f473" width="128"> | <img src="https://github.com/opencv/opencv/assets/25801568/b5bfcaf5-bc48-40ba-b8e3-d000e5ab48db" width="128">|

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
pull/24359/head
Dmitry Kurtaev 1 year ago committed by GitHub
parent 1bccc14e05
commit d752bac43f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      modules/imgproc/misc/java/test/ImgprocTest.java
  2. 78
      modules/imgproc/src/distransform.cpp
  3. 48
      modules/imgproc/test/test_distancetransform.cpp

@ -639,7 +639,7 @@ public class ImgprocTest extends OpenCVTestCase {
Imgproc.distanceTransformWithLabels(gray128, dst, labels, Imgproc.DIST_L2, 3);
assertMatEqual(dstLables, labels);
assertMatEqual(getMat(CvType.CV_32FC1, 8192), dst, EPS);
assertMatEqual(getMat(CvType.CV_32FC1, 65533.805), dst, EPS);
}
public void testDrawContoursMatListOfMatIntScalar() {

@ -45,24 +45,23 @@ namespace cv
{
static const int DIST_SHIFT = 16;
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
initTopBottom( Mat& temp, int border )
initTopBottom( Mat& temp, int border, unsigned int value )
{
Size size = temp.size();
unsigned int* ttop = (unsigned int*)temp.ptr<int>(0);
unsigned int* tbottom = (unsigned int*)temp.ptr<int>(size.height - 1);
for( int i = 0; i < border; i++ )
{
int* ttop = temp.ptr<int>(i);
int* tbottom = temp.ptr<int>(size.height - i - 1);
for( int j = 0; j < size.width; j++ )
{
ttop[j] = INIT_DIST0;
tbottom[j] = INIT_DIST0;
ttop[j] = value;
tbottom[j] = value;
}
ttop += size.width;
tbottom -= size.width;
}
}
@ -74,6 +73,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
int i, j;
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 DIST_MAX = UINT_MAX - DIAG_DIST;
const float scale = 1.f/(1 << DIST_SHIFT);
const uchar* src = _src.ptr();
@ -84,7 +84,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
int dststep = (int)(_dist.step/sizeof(dist[0]));
Size size = _src.size();
initTopBottom( _temp, BORDER );
initTopBottom( _temp, BORDER, DIST_MAX );
// forward pass
unsigned int* tmp = (unsigned int*)(temp + BORDER*step) + BORDER;
@ -92,7 +92,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
for( i = 0; i < size.height; i++ )
{
for( j = 0; j < BORDER; j++ )
tmp[-j-1] = tmp[size.width + j] = INIT_DIST0;
tmp[-j-1] = tmp[size.width + j] = DIST_MAX;
for( j = 0; j < size.width; j++ )
{
@ -107,7 +107,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
if( t0 > t ) t0 = t;
t = tmp[j-1] + HV_DIST;
if( t0 > t ) t0 = t;
tmp[j] = t0;
tmp[j] = (t0 > DIST_MAX) ? DIST_MAX : t0;
}
}
tmp += step;
@ -135,7 +135,6 @@ 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);
}
d -= dststep;
@ -151,6 +150,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
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 unsigned int DIST_MAX = UINT_MAX - LONG_DIST;
const float scale = 1.f/(1 << DIST_SHIFT);
const uchar* src = _src.ptr();
@ -161,7 +161,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
int dststep = (int)(_dist.step/sizeof(dist[0]));
Size size = _src.size();
initTopBottom( _temp, BORDER );
initTopBottom( _temp, BORDER, DIST_MAX );
// forward pass
unsigned int* tmp = (unsigned int*)(temp + BORDER*step) + BORDER;
@ -169,7 +169,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
for( i = 0; i < size.height; i++ )
{
for( j = 0; j < BORDER; j++ )
tmp[-j-1] = tmp[size.width + j] = INIT_DIST0;
tmp[-j-1] = tmp[size.width + j] = DIST_MAX;
for( j = 0; j < size.width; j++ )
{
@ -192,7 +192,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
if( t0 > t ) t0 = t;
t = tmp[j-1] + HV_DIST;
if( t0 > t ) t0 = t;
tmp[j] = t0;
tmp[j] = (t0 > DIST_MAX) ? DIST_MAX : t0;
}
}
tmp += step;
@ -228,7 +228,6 @@ 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);
}
d -= dststep;
@ -245,6 +244,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
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 unsigned int DIST_MAX = UINT_MAX - LONG_DIST;
const float scale = 1.f/(1 << DIST_SHIFT);
const uchar* src = _src.ptr();
@ -257,7 +257,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
int lstep = (int)(_labels.step/sizeof(labels[0]));
Size size = _src.size();
initTopBottom( _temp, BORDER );
initTopBottom( _temp, BORDER, DIST_MAX );
// forward pass
const uchar* s = src;
@ -266,7 +266,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
for( i = 0; i < size.height; i++ )
{
for( j = 0; j < BORDER; j++ )
tmp[-j-1] = tmp[size.width + j] = INIT_DIST0;
tmp[-j-1] = tmp[size.width + j] = DIST_MAX;
for( j = 0; j < size.width; j++ )
{
@ -277,7 +277,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels,
}
else
{
unsigned int t0 = INIT_DIST0, t;
unsigned int t0 = DIST_MAX, t;
int l0 = 0;
t = tmp[j-step*2-1] + LONG_DIST;
@ -402,7 +402,6 @@ 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);
}
d -= dststep;
@ -455,7 +454,7 @@ static void getDistanceTransformMask( int maskType, float *metrics )
struct DTColumnInvoker : ParallelLoopBody
{
DTColumnInvoker( const Mat* _src, Mat* _dst, const int* _sat_tab, const int* _sqr_tab)
DTColumnInvoker( const Mat* _src, Mat* _dst, const int* _sat_tab, const unsigned int* _sqr_tab)
{
src = _src;
dst = _dst;
@ -496,12 +495,14 @@ struct DTColumnInvoker : ParallelLoopBody
const Mat* src;
Mat* dst;
const int* sat_tab;
const int* sqr_tab;
const unsigned int* sqr_tab;
};
static const int PRECISE_DIST_MAX = 1 << 16;
struct DTRowInvoker : ParallelLoopBody
{
DTRowInvoker( Mat* _dst, const int* _sqr_tab, const float* _inv_tab )
DTRowInvoker( Mat* _dst, const unsigned int* _sqr_tab, const float* _inv_tab )
{
dst = _dst;
sqr_tab = _sqr_tab;
@ -528,7 +529,7 @@ struct DTRowInvoker : ParallelLoopBody
z[1] = inf;
f[0] = d[0];
for( q = 1, k = 0; q < n; q++ )
for( q = 1, k = 0; q < std::min(PRECISE_DIST_MAX, n); q++ )
{
float fq = d[q];
f[q] = fq;
@ -547,6 +548,25 @@ struct DTRowInvoker : ParallelLoopBody
}
}
}
for(; q < n; q++ )
{
float fq = d[q];
f[q] = fq;
for(;;k--)
{
p = v[k];
float s = (fq - d[p] + static_cast<float>(q + p) * (q - p))*inv_tab[q - p];
if( s > z[k] )
{
k++;
v[k] = q;
z[k] = s;
z[k+1] = inf;
break;
}
}
}
for( q = 0, k = 0; q < n; q++ )
{
@ -559,14 +579,14 @@ struct DTRowInvoker : ParallelLoopBody
}
Mat* dst;
const int* sqr_tab;
const unsigned int* sqr_tab;
const float* inv_tab;
};
static void
trueDistTrans( const Mat& src, Mat& dst )
{
const int inf = INT_MAX;
const unsigned int inf = UINT_MAX;
CV_Assert( src.size() == dst.size() );
@ -575,12 +595,12 @@ trueDistTrans( const Mat& src, Mat& dst )
cv::AutoBuffer<uchar> _buf(std::max(m*2*sizeof(int) + (m*3+1)*sizeof(int), n*2*sizeof(float)));
// stage 1: compute 1d distance transform of each column
int* sqr_tab = (int*)_buf.data();
unsigned int* sqr_tab = (unsigned int*)_buf.data();
int* sat_tab = cv::alignPtr((int*)(sqr_tab + m*2), sizeof(int));
int shift = m*2;
for( i = 0; i < m; i++ )
sqr_tab[i] = i*i;
sqr_tab[i] = i >= PRECISE_DIST_MAX ? inf : static_cast<unsigned int>(i) * i;
for( i = m; i < m*2; i++ )
sqr_tab[i] = inf;
for( i = 0; i < shift; i++ )
@ -598,7 +618,7 @@ trueDistTrans( const Mat& src, Mat& dst )
for( i = 1; i < n; i++ )
{
inv_tab[i] = (float)(0.5/i);
sqr_tab[i] = i*i;
sqr_tab[i] = i >= PRECISE_DIST_MAX ? inf : static_cast<unsigned int>(i) * i;
}
cv::parallel_for_(cv::Range(0, m), cv::DTRowInvoker(&dst, sqr_tab, inv_tab));

@ -40,6 +40,7 @@
//M*/
#include "test_precomp.hpp"
#include <numeric>
namespace opencv_test { namespace {
@ -368,4 +369,51 @@ BIGDATA_TEST(Imgproc_DistanceTransform, issue_23895_5x5_labels)
EXPECT_EQ(nz, 0);
}
TEST(Imgproc_DistanceTransform, max_distance_3x3)
{
Mat src = Mat::ones(1, 70000, CV_8U), dist;
src.at<uint8_t>(0, 0) = 0;
distanceTransform(src, dist, DIST_L2, DIST_MASK_3);
double minVal, maxVal;
minMaxLoc(dist, &minVal, &maxVal);
EXPECT_GE(maxVal, 65533);
}
TEST(Imgproc_DistanceTransform, max_distance_5x5)
{
Mat src = Mat::ones(1, 70000, CV_8U), dist;
src.at<uint8_t>(0, 0) = 0;
distanceTransform(src, dist, DIST_L2, DIST_MASK_5);
double minVal, maxVal;
minMaxLoc(dist, &minVal, &maxVal);
EXPECT_GE(maxVal, 65533);
}
TEST(Imgproc_DistanceTransform, max_distance_5x5_labels)
{
Mat src = Mat::ones(1, 70000, CV_8U), dist, labels;
src.at<uint8_t>(0, 0) = 0;
distanceTransform(src, dist, labels, DIST_L2, DIST_MASK_5);
double minVal, maxVal;
minMaxLoc(dist, &minVal, &maxVal);
EXPECT_GE(maxVal, 65533);
}
TEST(Imgproc_DistanceTransform, precise_long_dist)
{
static const int maxDist = 1 << 16;
Mat src = Mat::ones(1, 70000, CV_8U), dist;
src.at<uint8_t>(0, 0) = 0;
distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE, CV_32F);
Mat expected(src.size(), CV_32F);
std::iota(expected.begin<float>(), expected.end<float>(), 0.f);
expected.colRange(maxDist, expected.cols).setTo(maxDist);
EXPECT_EQ(cv::norm(expected, dist, NORM_INF), 0);
}
}} // namespace

Loading…
Cancel
Save