1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00

Merge pull request #28548 from 0AnshuAditya0:fix-minEnclosingCircle-welzl-28546

imgproc: fix minEnclosingCircle O(n^3) worst case by adding Welzl shuffle #28548

Welzl's algorithm requires random permutation of input points to
achieve expected O(n) time. Without shuffling, sorted inputs such
as those produced by findContours() trigger O(n^3) worst case.

Fix: copy input to std::vector<PT> and apply cv::randShuffle()
before processing. Uses OpenCV's RNG so cv::setRNGSeed() ensures
reproducible behavior.

Original benchmark (5088 contour points, Release, AVX2):
findContours output: 3.93 ms → 0.033 ms (119x speedup)
Random points: 0.051 ms → 0.049 ms (no regression)

Perf test results (this PR, Release, AVX2):
| Input | N | Time |
|-------|---|------|
| Sequential circle points | 10000 | 0.03 ms |
| Sequential circle points | 5000 | 0.01 ms |
| Random points (CV_32F) | 100000 | 1.87 ms |
| Random points (CV_32S) | 100000 | 2.81 ms |

Fixes #28546

### Pull Request Readiness Checklist

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
- [ ] 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:
Anshu
2026-03-04 17:46:07 +05:30
committed by GitHub
parent 5e91b461bc
commit fe160f3eed
2 changed files with 59 additions and 1 deletions
+35
View File
@@ -106,4 +106,39 @@ PERF_TEST_P(TestBoundingRect, BoundingRect,
SANITY_CHECK_NOTHING();
}
typedef TestBaseWithParam< tuple<MatDepth, int> > TestMinEnclosingCircle;
PERF_TEST_P(TestMinEnclosingCircle, minEnclosingCircle,
Combine(
testing::Values(CV_32S, CV_32F),
Values(400, 1000, 10000, 100000)
))
{
int ptType = get<0>(GetParam());
int n = get<1>(GetParam());
Mat pts(n, 2, ptType);
declare.in(pts, WARMUP_RNG);
Point2f center;
float radius;
TEST_CYCLE() minEnclosingCircle(pts, center, radius);
SANITY_CHECK_NOTHING();
}
typedef TestBaseWithParam<int> TestMinEnclosingCircleWorstCase;
PERF_TEST_P(TestMinEnclosingCircleWorstCase, minEnclosingCircle_sequential,
Values(400, 1000, 5000, 10000))
{
int n = GetParam();
vector<Point2f> contour;
for(int i = 0; i < n; ++i) {
float angle = (float)(i * 2 * CV_PI / n);
contour.push_back(Point2f(cos(angle) * 100, sin(angle) * 100));
}
Point2f center;
float radius;
TEST_CYCLE() minEnclosingCircle(contour, center, radius);
SANITY_CHECK_NOTHING();
}
} } // namespace
+24 -1
View File
@@ -130,8 +130,31 @@ void findSecondPoint(const PT *pts, int i, Point2f &center, float &radius)
template<typename PT>
static void findMinEnclosingCircle(const PT *pts, int count, Point2f &center, float &radius)
static void findMinEnclosingCircle(const PT *pts_in, int count, Point2f &center, float &radius)
{
// Welzl's algorithm requires random permutation for expected O(n) time.
// Without shuffling, sorted inputs trigger O(n^3) worst case.
cv::AutoBuffer<PT, 1024> pts_buf(count);
std::copy(pts_in, pts_in + count, pts_buf.data());
PT* pts = pts_buf.data();
if (count > 10)
{
Cv32suf x0, y0, xn, yn;
x0.f = (float)pts[0].x;
y0.f = (float)pts[0].y;
xn.f = (float)pts[count-1].x;
yn.f = (float)pts[count-1].y;
uint32_t seed = (uint32_t)count ^ x0.u ^ (y0.u << 8) ^ (xn.u << 16) ^ (yn.u << 24);
cv::RNG rng(seed);
for (int i = 1; i < count; ++i)
{
int j = rng.uniform(0, i + 1);
std::swap(pts[i], pts[j]);
}
}
center.x = (float)(pts[0].x + pts[1].x) / 2.0f;
center.y = (float)(pts[0].y + pts[1].y) / 2.0f;
float dx = (float)(pts[0].x - pts[1].x);