From 1b64851fa88700281f58812e22eff56607eb1dd1 Mon Sep 17 00:00:00 2001 From: Stefano Allegretti Date: Thu, 22 Apr 2021 20:20:12 +0200 Subject: [PATCH] Merge pull request #19951 from stal12:3.4 * Fix #4363 - wrong hierarchy (CV_RETR_TREE) in findContours * Add regression test for findContours * use C++11 => C++98 on 3.4 branch --- modules/imgproc/src/contours.cpp | 32 +++++++++++++++-- modules/imgproc/test/test_contours.cpp | 49 ++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/contours.cpp b/modules/imgproc/src/contours.cpp index e433cdb514..241f1443f5 100644 --- a/modules/imgproc/src/contours.cpp +++ b/modules/imgproc/src/contours.cpp @@ -625,7 +625,8 @@ icvFetchContour( schar *ptr, /* trace contour until certain point is met. - returns 1 if met, 0 else. + returns 1 if met and this is the last contour + encountered by a raster scan reaching the point, 0 else. */ static int icvTraceContour( schar *ptr, int step, schar *stop_ptr, int is_hole ) @@ -668,14 +669,39 @@ icvTraceContour( schar *ptr, int step, schar *stop_ptr, int is_hole ) break; } - if( i3 == stop_ptr || (i4 == i0 && i3 == i1) ) + if (i3 == stop_ptr) { + if (!(*i3 & 0x80)) { + /* it's the only contour */ + return 1; + } + + /* check if this is the last contour */ + /* encountered during a raster scan */ + schar *i5; + int t = s; + while (true) + { + t = (t - 1) & 7; + i5 = i3 + deltas[t]; + if (*i5 != 0) + break; + if (t == 0) + return 1; + } + } + + if( (i4 == i0 && i3 == i1) ) break; i3 = i4; s = (s + 4) & 7; } /* end of border following loop */ } - return i3 == stop_ptr; + else { + return i3 == stop_ptr; + } + + return 0; } diff --git a/modules/imgproc/test/test_contours.cpp b/modules/imgproc/test/test_contours.cpp index a5c924829d..c07d19098b 100644 --- a/modules/imgproc/test/test_contours.cpp +++ b/modules/imgproc/test/test_contours.cpp @@ -485,6 +485,55 @@ TEST(Imgproc_FindContours, border) ASSERT_EQ(0, cvtest::norm(img, img_draw_contours, NORM_INF)); } +TEST(Imgproc_FindContours, regression_4363_shared_nbd) +{ + // Create specific test image + Mat1b img(12, 69, (const uchar&)0); + + img(1, 1) = 1; + + // Vertical rectangle with hole sharing the same NBD + for (int r = 1; r <= 10; ++r) { + for (int c = 3; c <= 5; ++c) { + img(r, c) = 1; + } + } + img(9, 4) = 0; + + // 124 small CCs + for (int r = 1; r <= 7; r += 2) { + for (int c = 7; c <= 67; c += 2) { + img(r, c) = 1; + } + } + + // Last CC + img(9, 7) = 1; + + vector< vector > contours; + vector hierarchy; + findContours(img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); + + bool found = false; + size_t index = 0; + for (vector< vector >::const_iterator i = contours.begin(); i != contours.end(); ++i) + { + const vector& c = *i; + if (!c.empty() && c[0] == Point(7, 9)) + { + found = true; + index = (size_t)(i - contours.begin()); + break; + } + } + EXPECT_TRUE(found) << "Desired result: point (7,9) is a contour - Actual result: point (7,9) is not a contour"; + + if (found) + { + EXPECT_LT(hierarchy[index][3], 0) << "Desired result: (7,9) has no parent - Actual result: parent of (7,9) is another contour. index = " << index; + } +} + TEST(Imgproc_PointPolygonTest, regression_10222) { vector contour;