1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge pull request #28163 from dkurt:d.kurtaev/convexHull_repeats

Keep convexHull output indices monotone if possible #28163

### Pull Request Readiness Checklist

resolves https://github.com/opencv/opencv/issues/24907 ?
resolves https://github.com/opencv/opencv/issues/4954

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.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Dmitry Kurtaev
2025-12-19 19:41:21 +08:00
committed by GitHub
parent b157553e55
commit 43074571af
2 changed files with 92 additions and 4 deletions
+38 -4
View File
@@ -219,9 +219,9 @@ void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool ret
}
for( i = 0; i < tl_count-1; i++ )
hullbuf[nout++] = int(pointer[tl_stack[i]] - data0);
hullbuf[nout++] = tl_stack[i];
for( i = tr_count - 1; i > 0; i-- )
hullbuf[nout++] = int(pointer[tr_stack[i]] - data0);
hullbuf[nout++] = tr_stack[i];
int stop_idx = tr_count > 2 ? tr_stack[1] : tl_count > 2 ? tl_stack[tl_count - 2] : -1;
// lower half
@@ -257,9 +257,43 @@ void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool ret
}
for( i = 0; i < bl_count-1; i++ )
hullbuf[nout++] = int(pointer[bl_stack[i]] - data0);
hullbuf[nout++] = bl_stack[i];
for( i = br_count-1; i > 0; i-- )
hullbuf[nout++] = int(pointer[br_stack[i]] - data0);
hullbuf[nout++] = br_stack[i];
if (!returnPoints)
{
// Try keep monotonous indices in case of self-intersection.
for (i = 0; i < nout; ++i)
{
auto prev = pointer[hullbuf[(i == 0 ? nout : i) - 1]];
auto next = pointer[hullbuf[(i + 1) % nout]];
auto cur = pointer[hullbuf[i]];
if ((prev < cur && cur < next) || (prev > cur && cur > next))
{
continue;
}
for (int j = hullbuf[i] + 1; j < total; ++j)
{
cur = pointer[j];
if (*pointer[hullbuf[i]] == *cur)
{
if ((prev < cur && cur < next) || (prev > cur && cur > next))
{
hullbuf[i] = j;
break;
}
}
else
break;
}
}
}
for (i = 0; i < nout; ++i)
{
hullbuf[i] = int(pointer[hullbuf[i]] - data0);
}
// try to make the convex hull indices form
// an ascending or descending sequence by the cyclic
+54
View File
@@ -304,9 +304,11 @@ TEST(Imgproc_ConvexityDefects, ordering_4539)
vector<int> hull_ind;
vector<Vec4i> defects;
#if 0 // deprecated behavior
// first, check the original contour as-is, without intermediate fillPoly/drawContours.
convexHull(contour_, hull_ind, false, false);
EXPECT_THROW( convexityDefects(contour_, hull_ind, defects), cv::Exception );
#endif
int scale = 20;
contour_ *= (double)scale;
@@ -319,10 +321,12 @@ TEST(Imgproc_ConvexityDefects, ordering_4539)
findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
convexHull(contours[0], hull_ind, false, false);
#if 0 // deprecated behavior
// the original contour contains self-intersections,
// therefore convexHull does not return a monotonous sequence of points
// and therefore convexityDefects throws an exception
EXPECT_THROW( convexityDefects(contours[0], hull_ind, defects), cv::Exception );
#endif
#if 1
// one way to eliminate the contour self-intersection in this particular case is to apply dilate(),
@@ -1283,6 +1287,56 @@ INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line,
std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f)
));
typedef testing::TestWithParam<tuple<tuple<std::vector<Point>, Mat>, bool> > convexHull_monotonous;
TEST_P(convexHull_monotonous, self_intersecting_contour)
{
std::vector<Point> contour = get<0>(get<0>(GetParam()));
Mat ref = get<1>(get<0>(GetParam())).clone();
bool clockwise = get<1>(GetParam());
if (!clockwise)
{
std::reverse(ref.begin<int>(), ref.end<int>());
}
Mat indices;
convexHull(contour, indices, clockwise, false);
Point minLoc;
minMaxLoc(indices, nullptr, nullptr, &minLoc);
std::rotate(indices.begin<int>(), indices.begin<int>() + minLoc.y, indices.end<int>());
minMaxLoc(ref, nullptr, nullptr, &minLoc);
std::rotate(ref.begin<int>(), ref.begin<int>() + minLoc.y, ref.end<int>());
ASSERT_EQ( cvtest::norm(indices, ref, NORM_INF), 0) << indices;
}
INSTANTIATE_TEST_CASE_P(Imgproc, convexHull_monotonous,
testing::Combine(
testing::Values(
std::make_tuple(
std::vector<Point>{
Point(3, 2), Point(3, 4), Point(2, 5), Point(1, 5),
Point(2, 5), Point(3, 4), Point(6, 4), Point(6, 2)
},
(Mat_<int>(5, 1) << 0, 3, 4, 6, 7)
),
std::make_tuple(
std::vector<Point>{
Point(3, -2), Point(3, -4), Point(2, -5), Point(1, -5),
Point(2, -5), Point(3, -4), Point(6, -4), Point(6, -2)
},
(Mat_<int>(5, 1) << 3, 0, 7, 6, 4)
),
std::make_tuple(
std::vector<Point>{
Point(1, 1), Point(1, 0), Point(0, 0), Point(1, 0), Point(0, 1)
},
(Mat_<int>(4, 1) << 0, 1, 2, 4)
)
),
testing::Bool()
));
}} // namespace
/* End of file. */