diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index 3e4a266b30..36e2073195 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -51,12 +51,13 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r { CV_INSTRUMENT_REGION() - const float samePointEps = 0.00001f; // used to test if two points are the same + // L2 metric + const float samePointEps = std::max(1e-16f, 1e-6f * (float)std::max(rect1.size.area(), rect2.size.area())); Point2f vec1[4], vec2[4]; Point2f pts1[4], pts2[4]; - std::vector intersection; + std::vector intersection; intersection.reserve(24); rect1.points(pts1); rect2.points(pts2); @@ -219,41 +220,80 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r } } - // Get rid of dupes and order points. - for( int i = 0; i < (int)intersection.size()-1; i++ ) + int N = (int)intersection.size(); + if (N == 0) { - float dx1 = intersection[i + 1].x - intersection[i].x; - float dy1 = intersection[i + 1].y - intersection[i].y; - for( size_t j = i+1; j < intersection.size(); j++ ) - { - float dx = intersection[j].x - intersection[i].x; - float dy = intersection[j].y - intersection[i].y; - double d2 = dx*dx + dy*dy; // can be a really small number, need double here + return INTERSECT_NONE; + } - if( d2 < samePointEps*samePointEps ) + // Get rid of duplicated points + int Nstride = N; + cv::AutoBuffer distPt(N * N); + cv::AutoBuffer ptDistRemap(N); + for (int i = 0; i < N; ++i) + { + const Point2f pt0 = intersection[i]; + ptDistRemap[i] = i; + for (int j = i + 1; j < N; ) + { + const Point2f pt1 = intersection[j]; + float d2 = normL2Sqr(pt1 - pt0); + if(d2 <= samePointEps) { - // Found a dupe, remove it - std::swap(intersection[j], intersection.back()); - intersection.pop_back(); - j--; // restart check + if (j < N - 1) + intersection[j] = intersection[N - 1]; + N--; + continue; } - else if (dx1 * dy - dy1 * dx < 0) + distPt[i*Nstride + j] = d2; + ++j; + } + } + while (N > 8) // we still have duplicate points after samePointEps threshold (eliminate closest points) + { + int minI = 0; + int minJ = 1; + float minD = distPt[1]; + for (int i = 0; i < N - 1; ++i) + { + float* pDist = distPt.data() + Nstride * ptDistRemap[i]; + for (int j = i + 1; j < N; ++j) { - std::swap(intersection[i + 1], intersection[j]); - dx1 = dx; - dy1 = dy; + float d = pDist[ptDistRemap[j]]; + if (d < minD) + { + minD = d; + minI = i; + minJ = j; + } } } + CV_Assert(fabs(normL2Sqr(intersection[minI] - intersection[minJ]) - minD) < 1e-6); // ptDistRemap is not corrupted + // drop minJ point + if (minJ < N - 1) + { + intersection[minJ] = intersection[N - 1]; + ptDistRemap[minJ] = ptDistRemap[N - 1]; + } + N--; } - if( intersection.empty() ) + // order points + for (int i = 0; i < N - 1; ++i) { - return INTERSECT_NONE ; + Point2f diffI = intersection[i + 1] - intersection[i]; + for (int j = i + 2; j < N; ++j) + { + Point2f diffJ = intersection[j] - intersection[i]; + if (diffI.cross(diffJ) < 0) + { + std::swap(intersection[i + 1], intersection[j]); + diffI = diffJ; + } + } } - // If this check fails then it means we're getting dupes, increase samePointEps - CV_Assert( intersection.size() <= 8 ); - + intersection.resize(N); Mat(intersection).copyTo(intersectingRegion); return ret; diff --git a/modules/imgproc/test/test_intersection.cpp b/modules/imgproc/test/test_intersection.cpp index 249950afea..0e419c4702 100644 --- a/modules/imgproc/test/test_intersection.cpp +++ b/modules/imgproc/test/test_intersection.cpp @@ -49,29 +49,18 @@ namespace opencv_test { namespace { #define ACCURACY 0.00001 -class CV_RotatedRectangleIntersectionTest: public cvtest::ArrayTest -{ -public: - -protected: - void run (int); - -private: - void test1(); - void test2(); - void test3(); - void test4(); - void test5(); - void test6(); - void test7(); - void test8(); - void test9(); - void test10(); - void test11(); - void test12(); - void test13(); - void test14(); -}; +// See pics/intersection.png for the scenarios we are testing + +// Test the following scenarios: +// 1 - no intersection +// 2 - partial intersection, rectangle translated +// 3 - partial intersection, rectangle rotated 45 degree on the corner, forms a triangle intersection +// 4 - full intersection, rectangles of same size directly on top of each other +// 5 - partial intersection, rectangle on top rotated 45 degrees +// 6 - partial intersection, rectangle on top of different size +// 7 - full intersection, rectangle fully enclosed in the other +// 8 - partial intersection, rectangle corner just touching. point contact +// 9 - partial intersetion. rectangle side by side, line contact static void compare(const std::vector& test, const std::vector& target) { @@ -80,45 +69,12 @@ static void compare(const std::vector& test, const std::vector ASSERT_TRUE(target.size() < 4 || isContourConvex(target)); for( size_t i = 0; i < test.size(); i++ ) { - double dx = test[i].x - target[i].x; - double dy = test[i].y - target[i].y; - double r = sqrt(dx*dx + dy*dy); + double r = sqrt(normL2Sqr(test[i] - target[i])); ASSERT_LT(r, ACCURACY); } } -void CV_RotatedRectangleIntersectionTest::run(int) -{ - // See pics/intersection.png for the scenarios we are testing - - // Test the following scenarios: - // 1 - no intersection - // 2 - partial intersection, rectangle translated - // 3 - partial intersection, rectangle rotated 45 degree on the corner, forms a triangle intersection - // 4 - full intersection, rectangles of same size directly on top of each other - // 5 - partial intersection, rectangle on top rotated 45 degrees - // 6 - partial intersection, rectangle on top of different size - // 7 - full intersection, rectangle fully enclosed in the other - // 8 - partial intersection, rectangle corner just touching. point contact - // 9 - partial intersetion. rectangle side by side, line contact - - test1(); - test2(); - test3(); - test4(); - test5(); - test6(); - test7(); - test8(); - test9(); - test10(); - test11(); - test12(); - test13(); - test14(); -} - -void CV_RotatedRectangleIntersectionTest::test1() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_1) { // no intersection RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 12.0f); @@ -131,7 +87,7 @@ void CV_RotatedRectangleIntersectionTest::test1() CV_Assert(vertices.empty()); } -void CV_RotatedRectangleIntersectionTest::test2() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_2) { // partial intersection, rectangles translated RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -150,7 +106,7 @@ void CV_RotatedRectangleIntersectionTest::test2() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test3() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_3) { // partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -168,7 +124,7 @@ void CV_RotatedRectangleIntersectionTest::test3() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test4() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_4) { // full intersection, rectangles of same size directly on top of each other RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -187,7 +143,7 @@ void CV_RotatedRectangleIntersectionTest::test4() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test5() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_5) { // partial intersection, rectangle on top rotated 45 degrees RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -210,7 +166,7 @@ void CV_RotatedRectangleIntersectionTest::test5() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test6() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_6) { // 6 - partial intersection, rectangle on top of different size RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -229,7 +185,7 @@ void CV_RotatedRectangleIntersectionTest::test6() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test7() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_7) { // full intersection, rectangle fully enclosed in the other RotatedRect rect1(Point2f(0, 0), Size2f(12.34f, 56.78f), 0.0f); @@ -248,7 +204,7 @@ void CV_RotatedRectangleIntersectionTest::test7() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test8() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_8) { // intersection by a single vertex RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -261,7 +217,7 @@ void CV_RotatedRectangleIntersectionTest::test8() compare(vertices, vector(1, Point2f(1.0f, 1.0f))); } -void CV_RotatedRectangleIntersectionTest::test9() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_9) { // full intersection, rectangle fully enclosed in the other RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -278,7 +234,7 @@ void CV_RotatedRectangleIntersectionTest::test9() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test10() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_10) { // three points of rect2 are inside rect1. RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); @@ -298,7 +254,7 @@ void CV_RotatedRectangleIntersectionTest::test10() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test11() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_11) { RotatedRect rect1(Point2f(0, 0), Size2f(4, 2), 0.0f); RotatedRect rect2(Point2f(0, 0), Size2f(2, 2), -45.0f); @@ -318,7 +274,7 @@ void CV_RotatedRectangleIntersectionTest::test11() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test12() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_12) { RotatedRect rect1(Point2f(0, 0), Size2f(2, 2), 0.0f); RotatedRect rect2(Point2f(0, 1), Size2f(1, 1), 0.0f); @@ -336,7 +292,7 @@ void CV_RotatedRectangleIntersectionTest::test12() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test13() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_13) { RotatedRect rect1(Point2f(0, 0), Size2f(1, 3), 0.0f); RotatedRect rect2(Point2f(0, 1), Size2f(3, 1), 0.0f); @@ -354,7 +310,7 @@ void CV_RotatedRectangleIntersectionTest::test13() compare(vertices, targetVertices); } -void CV_RotatedRectangleIntersectionTest::test14() +TEST(Imgproc_RotatedRectangleIntersection, accuracy_14) { const int kNumTests = 100; const float kWidth = 5; @@ -376,6 +332,38 @@ void CV_RotatedRectangleIntersectionTest::test14() } } -TEST (Imgproc_RotatedRectangleIntersection, accuracy) { CV_RotatedRectangleIntersectionTest test; test.safe_run(); } +TEST(Imgproc_RotatedRectangleIntersection, regression_12221_1) +{ + RotatedRect r1( + Point2f(259.65081787109375, 51.58895492553711), + Size2f(5487.8779296875, 233.8921661376953), + -29.488616943359375); + RotatedRect r2( + Point2f(293.70465087890625, 112.10154724121094), + Size2f(5487.8896484375, 234.87368774414062), + -31.27001953125); + + std::vector intersections; + int interType = cv::rotatedRectangleIntersection(r1, r2, intersections); + EXPECT_EQ(INTERSECT_PARTIAL, interType); + EXPECT_LE(intersections.size(), (size_t)8); +} + +TEST(Imgproc_RotatedRectangleIntersection, regression_12221_2) +{ + RotatedRect r1( + Point2f(239.78500366210938, 515.72021484375), + Size2f(70.23420715332031, 39.74684524536133), + -42.86162567138672); + RotatedRect r2( + Point2f(242.4205322265625, 510.1195373535156), + Size2f(66.85948944091797, 61.46455383300781), + -9.840961456298828); + + std::vector intersections; + int interType = cv::rotatedRectangleIntersection(r1, r2, intersections); + EXPECT_EQ(INTERSECT_PARTIAL, interType); + EXPECT_LE(intersections.size(), (size_t)8); +} }} // namespace