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