From d750d43aa2fd6c8e5dde5861c19afe612d57911b Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Wed, 11 Jun 2025 17:37:18 +0300 Subject: [PATCH] Merge pull request #27432 from dkurt:d.kurtaev/ipp_distTransform Correct IPP distanceTransform results with single thread #27432 ### Pull Request Readiness Checklist resolves #24082 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 --- modules/imgproc/src/distransform.cpp | 16 +++++++++ .../imgproc/test/test_distancetransform.cpp | 33 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index 6a7026c8c8..66be0e8ea5 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -796,6 +796,22 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe ippFree( pBuffer ); if (status>=0) { + // https://github.com/opencv/opencv/issues/24082 + // There is probably a rounding issue that leads to non-deterministic behavior + // between runs on positions closer to zeros by x-axis in straight direction. + // As a workaround, we detect the distances that expected to be exact + // number of pixels and round manually. + static const float correctionDiff = 1.0f / (1 << 11); + for (int i = 0; i < dst.rows; ++i) + { + float* row = dst.ptr(i); + for (int j = 0; j < dst.cols; ++j) + { + float rounded = static_cast(cvRound(row[j])); + if (fabs(row[j] - rounded) <= correctionDiff) + row[j] = rounded; + } + } CV_IMPL_ADD(CV_IMPL_IPP); return; } diff --git a/modules/imgproc/test/test_distancetransform.cpp b/modules/imgproc/test/test_distancetransform.cpp index bf272cd224..9692655fab 100644 --- a/modules/imgproc/test/test_distancetransform.cpp +++ b/modules/imgproc/test/test_distancetransform.cpp @@ -416,4 +416,37 @@ TEST(Imgproc_DistanceTransform, precise_long_dist) EXPECT_EQ(cv::norm(expected, dist, NORM_INF), 0); } +TEST(Imgproc_DistanceTransform, ipp_deterministic_corner) +{ + setNumThreads(1); + + Mat src(1, 4096, CV_8U, Scalar(255)), dist; + src.at(0, 0) = 0; + distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE); + for (int i = 0; i < src.cols; ++i) + { + float expected = static_cast(i); + ASSERT_EQ(expected, dist.at(0, i)) << cv::format("diff: %e", expected - dist.at(0, i)); + } +} + +TEST(Imgproc_DistanceTransform, ipp_deterministic) +{ + setNumThreads(1); + RNG& rng = TS::ptr()->get_rng(); + Mat src(1, 800, CV_8U, Scalar(255)), dist; + int p1 = cvtest::randInt(rng) % src.cols; + int p2 = cvtest::randInt(rng) % src.cols; + int p3 = cvtest::randInt(rng) % src.cols; + src.at(0, p1) = 0; + src.at(0, p2) = 0; + src.at(0, p3) = 0; + distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE); + for (int i = 0; i < src.cols; ++i) + { + float expected = static_cast(min(min(abs(i - p1), abs(i - p2)), abs(i - p3))); + ASSERT_EQ(expected, dist.at(0, i)) << cv::format("diff: %e", expected - dist.at(0, i)); + } +} + }} // namespace