mirror of
https://github.com/opencv/opencv.git
synced 2025-08-06 14:36:36 +08:00
Merge pull request #16136 from mcellis33:mec-nan
* Handle det == 0 in findCircle3pts. Issue 16051 shows a case where findCircle3pts returns NaN for the center coordinates and radius due to dividing by a determinant of 0. In this case, the points are colinear, so the longest distance between any 2 points is the diameter of the minimum enclosing circle. * imgproc(test): update test checks for minEnclosingCircle() * imgproc: fix handling of special cases in minEnclosingCircle()
This commit is contained in:
parent
f8b16fa293
commit
5d15c65e48
@ -60,6 +60,29 @@ static void findCircle3pts(Point2f *pts, Point2f ¢er, float &radius)
|
||||
Point2f midPoint2 = (pts[0] + pts[2]) / 2.0f;
|
||||
float c2 = midPoint2.x * v2.x + midPoint2.y * v2.y;
|
||||
float det = v1.x * v2.y - v1.y * v2.x;
|
||||
if (fabs(det) <= EPS)
|
||||
{
|
||||
// v1 and v2 are colinear, so the longest distance between any 2 points
|
||||
// is the diameter of the minimum enclosing circle.
|
||||
float d1 = normL2Sqr<float>(pts[0] - pts[1]);
|
||||
float d2 = normL2Sqr<float>(pts[0] - pts[2]);
|
||||
float d3 = normL2Sqr<float>(pts[1] - pts[2]);
|
||||
radius = sqrt(std::max(d1, std::max(d2, d3))) * 0.5f + EPS;
|
||||
if (d1 >= d2 && d1 >= d3)
|
||||
{
|
||||
center = (pts[0] + pts[1]) * 0.5f;
|
||||
}
|
||||
else if (d2 >= d1 && d2 >= d3)
|
||||
{
|
||||
center = (pts[0] + pts[2]) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_DbgAssert(d3 >= d1 && d3 >= d2);
|
||||
center = (pts[1] + pts[2]) * 0.5f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
float cx = (c1 * v2.y - c2 * v1.y) / det;
|
||||
float cy = (v1.x * c2 - v2.x * c1) / det;
|
||||
center.x = (float)cx;
|
||||
@ -92,7 +115,13 @@ static void findThirdPoint(const PT *pts, int i, int j, Point2f ¢er, float &
|
||||
ptsf[0] = (Point2f)pts[i];
|
||||
ptsf[1] = (Point2f)pts[j];
|
||||
ptsf[2] = (Point2f)pts[k];
|
||||
findCircle3pts(ptsf, center, radius);
|
||||
Point2f new_center; float new_radius = 0;
|
||||
findCircle3pts(ptsf, new_center, new_radius);
|
||||
if (new_radius > 0)
|
||||
{
|
||||
radius = new_radius;
|
||||
center = new_center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,7 +146,13 @@ void findSecondPoint(const PT *pts, int i, Point2f ¢er, float &radius)
|
||||
}
|
||||
else
|
||||
{
|
||||
findThirdPoint(pts, i, j, center, radius);
|
||||
Point2f new_center; float new_radius = 0;
|
||||
findThirdPoint(pts, i, j, new_center, new_radius);
|
||||
if (new_radius > 0)
|
||||
{
|
||||
radius = new_radius;
|
||||
center = new_center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,7 +178,13 @@ static void findMinEnclosingCircle(const PT *pts, int count, Point2f ¢er, fl
|
||||
}
|
||||
else
|
||||
{
|
||||
findSecondPoint(pts, i, center, radius);
|
||||
Point2f new_center; float new_radius = 0;
|
||||
findSecondPoint(pts, i, new_center, new_radius);
|
||||
if (new_radius > 0)
|
||||
{
|
||||
radius = new_radius;
|
||||
center = new_center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1084,6 +1084,87 @@ int CV_MinCircleTest2::validate_test_results( int test_case_idx )
|
||||
return code;
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* minEnclosingCircle Test 3 *
|
||||
\****************************************************************************************/
|
||||
|
||||
TEST(Imgproc_minEnclosingCircle, basic_test)
|
||||
{
|
||||
vector<Point2f> pts;
|
||||
pts.push_back(Point2f(0, 0));
|
||||
pts.push_back(Point2f(10, 0));
|
||||
pts.push_back(Point2f(5, 1));
|
||||
const float EPS = 1.0e-3f;
|
||||
Point2f center;
|
||||
float radius;
|
||||
|
||||
// pts[2] is within the circle with diameter pts[0] - pts[1].
|
||||
// 2
|
||||
// 0 1
|
||||
// NB: The triangle is obtuse, so the only pts[0] and pts[1] are on the circle.
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
|
||||
// pts[2] is on the circle with diameter pts[0] - pts[1].
|
||||
// 2
|
||||
// 0 1
|
||||
pts[2] = Point2f(5, 5);
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
|
||||
// pts[2] is outside the circle with diameter pts[0] - pts[1].
|
||||
// 2
|
||||
//
|
||||
//
|
||||
// 0 1
|
||||
// NB: The triangle is acute, so all 3 points are on the circle.
|
||||
pts[2] = Point2f(5, 10);
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 3.75, EPS);
|
||||
EXPECT_NEAR(6.25f, radius, EPS);
|
||||
|
||||
// The 3 points are colinear.
|
||||
pts[2] = Point2f(3, 0);
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
|
||||
// 2 points are the same.
|
||||
pts[2] = pts[1];
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 5, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(5, radius, EPS);
|
||||
|
||||
// 3 points are the same.
|
||||
pts[0] = pts[1];
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 10, EPS);
|
||||
EXPECT_NEAR(center.y, 0, EPS);
|
||||
EXPECT_NEAR(0, radius, EPS);
|
||||
}
|
||||
|
||||
TEST(Imgproc_minEnclosingCircle, regression_16051) {
|
||||
vector<Point2f> pts;
|
||||
pts.push_back(Point2f(85, 1415));
|
||||
pts.push_back(Point2f(87, 1415));
|
||||
pts.push_back(Point2f(89, 1414));
|
||||
pts.push_back(Point2f(89, 1414));
|
||||
pts.push_back(Point2f(87, 1412));
|
||||
Point2f center;
|
||||
float radius;
|
||||
minEnclosingCircle(pts, center, radius);
|
||||
EXPECT_NEAR(center.x, 86.9f, 1e-3);
|
||||
EXPECT_NEAR(center.y, 1414.1f, 1e-3);
|
||||
EXPECT_NEAR(2.1024551f, radius, 1e-3);
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* Perimeter Test *
|
||||
\****************************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user