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:
@@ -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
|
||||
|
||||
@@ -130,8 +130,31 @@ void findSecondPoint(const PT *pts, int i, Point2f ¢er, float &radius)
|
||||
|
||||
|
||||
template<typename PT>
|
||||
static void findMinEnclosingCircle(const PT *pts, int count, Point2f ¢er, float &radius)
|
||||
static void findMinEnclosingCircle(const PT *pts_in, int count, Point2f ¢er, 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);
|
||||
|
||||
Reference in New Issue
Block a user