From: mcellis33 Date: Wed, 18 Dec 2019 14:25:59 +0000 (+0000) Subject: Merge pull request #16136 from mcellis33:mec-nan X-Git-Tag: accepted/tizen/6.0/unified/20201030.111113~1^2~3^2~10 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5d15c65e48eb13509dd8425dc7c4ed3181f818b8;p=platform%2Fupstream%2Fopencv.git 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() --- diff --git a/modules/imgproc/src/shapedescr.cpp b/modules/imgproc/src/shapedescr.cpp index 436c74e..8ba4b41 100644 --- a/modules/imgproc/src/shapedescr.cpp +++ b/modules/imgproc/src/shapedescr.cpp @@ -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(pts[0] - pts[1]); + float d2 = normL2Sqr(pts[0] - pts[2]); + float d3 = normL2Sqr(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; + } } } } diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 3f12140..fc29b7f 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1085,6 +1085,87 @@ int CV_MinCircleTest2::validate_test_results( int test_case_idx ) } /****************************************************************************************\ +* minEnclosingCircle Test 3 * +\****************************************************************************************/ + +TEST(Imgproc_minEnclosingCircle, basic_test) +{ + vector 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 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 * \****************************************************************************************/