From e2f00ce84660e282038530090eb0f9b9f655ac75 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Sun, 28 Jul 2013 00:12:24 +1000 Subject: [PATCH 01/12] New intersection function for rotated rectangles --- modules/imgproc/include/opencv2/imgproc.hpp | 3 + modules/imgproc/src/intersection.cpp | 223 ++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 modules/imgproc/src/intersection.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index dd849ef4c2..b170e6a4e4 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1414,6 +1414,9 @@ CV_EXPORTS_W void fitLine( InputArray points, OutputArray line, int distType, //! checks if the point is inside the contour. Optionally computes the signed distance from the point to the contour boundary CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist ); +//! computes whether two rotated rectangles intersect and returns the vertices of the intersecting region +CV_EXPORTS_W bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ); + CV_EXPORTS Ptr createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8)); } // cv diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp new file mode 100644 index 0000000000..6d6f97d8b3 --- /dev/null +++ b/modules/imgproc/src/intersection.cpp @@ -0,0 +1,223 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Nghia Ho, nghiaho12@yahoo.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of OpenCV Foundation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the OpenCV Foundation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "precomp.hpp" + +namespace cv +{ + +bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ) +{ + const float samePointEps = 0.00001; // used to test if two points are the same, due to numerical error + + Point2f vec1[4], vec2[4]; + Point2f pts1[4], pts2[4]; + + std::vector intersection; + + rect1.points(pts1); + rect2.points(pts2); + + // Line vector + // A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1] + for( int i = 0; i < 4; i++ ) + { + vec1[i].x = pts1[(i+1)%4].x - pts1[i].x; + vec1[i].y = pts1[(i+1)%4].y - pts1[i].y; + + vec2[i].x = pts2[(i+1)%4].x - pts2[i].x; + vec2[i].y = pts2[(i+1)%4].y - pts2[i].y; + } + + // Line test - test all line combos for intersection + for( int i = 0; i < 4; i++ ) + { + for( int j = 0; j < 4; j++ ) + { + // Solve for 2x2 Ax=b + float x21 = pts2[j].x - pts1[i].x; + float y21 = pts2[j].y - pts1[i].y; + + float vx1 = vec1[i].x; + float vy1 = vec1[i].y; + + float vx2 = vec2[j].x; + float vy2 = vec2[j].y; + + float det = vx2*vy1 - vx1*vy2; + + float t1 = (vx2*y21 - vy2*x21) / det; + float t2 = (vx1*y21 - vy1*x21) / det; + + // This takes care of parallel lines + if( !std::isnormal(t1) || !std::isnormal(t2) ) + { + continue; + } + + if( t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f ) + { + float xi = pts1[i].x + vec1[i].x*t1; + float yi = pts1[i].y + vec1[i].y*t1; + + intersection.push_back(Point2f(xi,yi)); + } + } + } + + // Check for vertices from rect1 inside recct2 + for( int i = 0; i < 4; i++ ) + { + // We do a sign test to see which side the point lies. + // If the point all lie on the same sign for all 4 sides of the rect, + // then there's an intersection + int posSign = 0; + int negSign = 0; + + float x = pts1[i].x; + float y = pts1[i].y; + + for( int j = 0; j < 4; j++ ) + { + // line equation: Ax + By + C = 0 + // see which side of the line this point is at + float A = -vec2[j].y; + float B = vec2[j].x; + float C = -(A*pts2[j].x + B*pts2[j].y); + + float s = A*x+ B*y+ C; + + if( s >= 0 ) + { + posSign++; + } + else + { + negSign++; + } + } + + if( posSign == 4 || negSign == 4 ) + { + intersection.push_back(pts1[i]); + } + } + + // Reverse the check - check for vertices from rect2 inside recct1 + for( int i = 0; i < 4; i++ ) + { + // We do a sign test to see which side the point lies. + // If the point all lie on the same sign for all 4 sides of the rect, + // then there's an intersection + int posSign = 0; + int negSign = 0; + + float x = pts2[i].x; + float y = pts2[i].y; + + for( int j = 0; j < 4; j++ ) + { + // line equation: Ax + By + C = 0 + // see which side of the line this point is at + float A = -vec1[j].y; + float B = vec1[j].x; + float C = -(A*pts1[j].x + B*pts1[j].y); + + float s = A*x + B*y + C; + + if( s >= 0 ) + { + posSign++; + } + else + { + negSign++; + } + } + + if( posSign == 4 || negSign == 4 ) + { + intersection.push_back(pts2[i]); + } + } + + // Get rid of dupes + for( int i = 0; i < (int)intersection.size()-1; i++ ) + { + for( size_t j = i+1; j < intersection.size(); j++ ) + { + float dx = intersection[i].x - intersection[j].x; + float dy = intersection[i].y - intersection[j].y; + double d2 = dx*dx + dy*dy; // can be a really small number, need double here + + if( d2 < samePointEps*samePointEps ) + { + // Found a dupe, remove it + std::swap(intersection[j], intersection.back()); + intersection.pop_back(); + i--; // restart check + } + } + } + + if( intersection.empty() ) + { + return false; + } + + intersectingRegion.create(intersection.size(), 1, CV_MAKETYPE(intersectingRegion.depth(), 2) ); + + Mat m = intersectingRegion.getMat(); + + size_t step = !m.isContinuous() ? m.step[0] : sizeof(Point2f); + + for( size_t i = 0; i < intersection.size(); i++ ) + { + *(Point2f*)(m.data + i*step) = intersection[i]; + } + + return true; +} + +} From a0576d7b2a6eac0fcc760ab7806ec30ae3bedb6e Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Sun, 28 Jul 2013 00:46:44 +1000 Subject: [PATCH 02/12] Fixed ret --- modules/imgproc/src/intersection.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index 6d6f97d8b3..9c1fd9f565 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -206,16 +206,18 @@ bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& return false; } - intersectingRegion.create(intersection.size(), 1, CV_MAKETYPE(intersectingRegion.depth(), 2) ); + //intersectingRegion.create(intersection.size(), 2, CV_32F); - Mat m = intersectingRegion.getMat(); + // Mat ret = intersectingRegion.getMat(); - size_t step = !m.isContinuous() ? m.step[0] : sizeof(Point2f); + Mat(intersection).copyTo(intersectingRegion); - for( size_t i = 0; i < intersection.size(); i++ ) - { - *(Point2f*)(m.data + i*step) = intersection[i]; - } +// size_t step = !m.isContinuous() ? m.step[0] : sizeof(Point2f); + +// for( size_t i = 0; i < intersection.size(); i++ ) +// { +// *(Point2f*)(m.data + i*step) = intersection[i]; +// } return true; } From a40f217a38f19caa35d0c708727e74df39eaf661 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Sun, 28 Jul 2013 01:06:55 +1000 Subject: [PATCH 03/12] changed from isnormal to isfinite, the prev ignored zero --- modules/imgproc/include/opencv2/imgproc.hpp | 8 +++++-- modules/imgproc/src/intersection.cpp | 26 +++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index b170e6a4e4..5e2e952954 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -462,7 +462,11 @@ enum { COLOR_BGR2BGRA = 0, COLOR_COLORCVT_MAX = 139 }; - +//! types of intersection between rectangles +enum { INTERSECT_NONE = 0, + INTERSECT_PARTIAL = 1, + INTERSECT_FULL = 2 + }; /*! The Base Class for 1D or Row-wise Filters @@ -1415,7 +1419,7 @@ CV_EXPORTS_W void fitLine( InputArray points, OutputArray line, int distType, CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist ); //! computes whether two rotated rectangles intersect and returns the vertices of the intersecting region -CV_EXPORTS_W bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ); +CV_EXPORTS_W int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ); CV_EXPORTS Ptr createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8)); diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index 9c1fd9f565..7870f78299 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -47,7 +47,7 @@ namespace cv { -bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ) +int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ) { const float samePointEps = 0.00001; // used to test if two points are the same, due to numerical error @@ -59,6 +59,8 @@ bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect1.points(pts1); rect2.points(pts2); + int ret = INTERSECT_FULL; + // Line vector // A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1] for( int i = 0; i < 4; i++ ) @@ -91,7 +93,7 @@ bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& float t2 = (vx1*y21 - vy1*x21) / det; // This takes care of parallel lines - if( !std::isnormal(t1) || !std::isnormal(t2) ) + if( !std::isfinite(t1) || !std::isfinite(t2) ) { continue; } @@ -106,6 +108,11 @@ bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& } } + if( !intersection.empty() ) + { + ret = INTERSECT_PARTIAL; + } + // Check for vertices from rect1 inside recct2 for( int i = 0; i < 4; i++ ) { @@ -203,23 +210,12 @@ bool rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& if( intersection.empty() ) { - return false; + return INTERSECT_NONE ; } - //intersectingRegion.create(intersection.size(), 2, CV_32F); - - // Mat ret = intersectingRegion.getMat(); - Mat(intersection).copyTo(intersectingRegion); -// size_t step = !m.isContinuous() ? m.step[0] : sizeof(Point2f); - -// for( size_t i = 0; i < intersection.size(); i++ ) -// { -// *(Point2f*)(m.data + i*step) = intersection[i]; -// } - - return true; + return ret; } } From cb16f733b9a8ce7ccc7da479abe71a17db2e6fbf Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Sun, 28 Jul 2013 15:31:58 +1000 Subject: [PATCH 04/12] Added C interface --- .../include/opencv2/imgproc/imgproc_c.h | 4 ++++ modules/imgproc/src/intersection.cpp | 21 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h index 4e2dc7142a..6417a00f38 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h +++ b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h @@ -615,6 +615,10 @@ CVAPI(CvSeq*) cvHoughCircles( CvArr* image, void* circle_storage, CVAPI(void) cvFitLine( const CvArr* points, int dist_type, double param, double reps, double aeps, float* line ); +/* Finds the intersecting region made by two rotated rectangles. + If there is an intersection the vertices of the region are returned. */ +CVAPI(int) cvRotatedRectangleIntersection( const CvBox2D* rect1, const CvBox2D* rect2, CvPoint2D32f intersectingRegion[8], int* pointCount ); + #ifdef __cplusplus } #endif diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index 7870f78299..f0f00e2cd1 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -49,7 +49,7 @@ namespace cv int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ) { - const float samePointEps = 0.00001; // used to test if two points are the same, due to numerical error + const float samePointEps = 0.00001; // used to test if two points are the same Point2f vec1[4], vec2[4]; Point2f pts1[4], pts2[4]; @@ -213,9 +213,28 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r return INTERSECT_NONE ; } + // If this check fails then it means we're getting dupes, increase samePointEps + CV_Assert( intersection.size() <= 8 ); + Mat(intersection).copyTo(intersectingRegion); return ret; } +} // end namespace + +int cvRotatedRectangleIntersection( const CvBox2D* rect1, const CvBox2D* rect2, CvPoint2D32f intersectingRegion[8], int* pointCount ) +{ + std::vector pts; + + int ret = cv::rotatedRectangleIntersection( *rect1, *rect2, pts ); + + for( size_t i=0; i < pts.size(); i++ ) + { + intersectingRegion[i] = pts[i]; + } + + *pointCount = (int)pts.size(); + + return ret; } From 00f63fa8f7569fc03cc425ed838ad20f03006d2a Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Wed, 31 Jul 2013 23:08:02 +1000 Subject: [PATCH 05/12] Finished test code, added image to the doc --- ...uctural_analysis_and_shape_descriptors.rst | 30 +++++++++++++++++++ modules/imgproc/src/intersection.cpp | 28 +++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst index 136d3e3df4..763b45bc8a 100644 --- a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst +++ b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst @@ -657,3 +657,33 @@ See below a sample output of the function where each image pixel is tested again .. [Suzuki85] Suzuki, S. and Abe, K., *Topological Structural Analysis of Digitized Binary Images by Border Following*. CVGIP 30 1, pp 32-46 (1985) .. [TehChin89] Teh, C.H. and Chin, R.T., *On the Detection of Dominant Points on Digital Curve*. PAMI 11 8, pp 859-872 (1989) + + + +rotatedRectangleIntersection +------------------------------- +Finds out if there is any intersection between two rotated rectangles. If there is then the vertices of the interesecting region are returned as well. + +.. ocv:function:: int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ) +.. ocv:pyfunction:: cv2.rotatedRectangleIntersection( rect1, rect2 ) -> retval, intersectingRegion +.. ocv:cfunction:: int cvRotatedRectangleIntersection( const CvBox2D* rect1, const CvBox2D* rect2, CvPoint2D32f intersectingRegion[8], int* pointCount ) + + :param rect1: First rectangle + + :param rect2: Second rectangle + + :param intersectingRegion: The output array of the verticies of the intersecting region. It returns at most 8 vertices. Stored as ``std::vector`` or ``cv::Mat`` as Mx1 of type CV_32FC2. + + :param pointCount: The number of vertices. + +The following values are returned by the function: + + * INTERSECT_NONE=0 - No intersection + + * INTERSECT_PARTIAL=1 - There is a partial intersection + + * INTERSECT_FULL=2 - One of the rectangle is fully enclosed in the other + +Below are some examples of intersection configurations. The hatched pattern indicates the intersecting region and the red vertices are returned by the function. + +.. image:: pics/intersection.png diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index f0f00e2cd1..f63f8230c3 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -61,6 +61,34 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r int ret = INTERSECT_FULL; + // Specical case of rect1 == rect2 + { + bool same = true; + + for( int i = 0; i < 4; i++ ) + { + if( fabs(pts1[i].x - pts2[i].x) > samePointEps || (fabs(pts1[i].y - pts2[i].y) > samePointEps) ) + { + same = false; + break; + } + } + + if(same) + { + intersection.resize(4); + + for( int i = 0; i < 4; i++ ) + { + intersection[i] = pts1[i]; + } + + Mat(intersection).copyTo(intersectingRegion); + + return INTERSECT_FULL; + } + } + // Line vector // A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1] for( int i = 0; i < 4; i++ ) From c0bbdde8bafef5c92a3b4a53571c7f29e90eb8c5 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Wed, 31 Jul 2013 23:43:25 +1000 Subject: [PATCH 06/12] Added test cpp --- modules/imgproc/test/test_intersection.cpp | 499 +++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 modules/imgproc/test/test_intersection.cpp diff --git a/modules/imgproc/test/test_intersection.cpp b/modules/imgproc/test/test_intersection.cpp new file mode 100644 index 0000000000..3a53d6f0c8 --- /dev/null +++ b/modules/imgproc/test/test_intersection.cpp @@ -0,0 +1,499 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Nghia Ho, nghiaho12@yahoo.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of OpenCV Foundation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the OpenCV Foundation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +#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 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(); +} + +void CV_RotatedRectangleIntersectionTest::test1() +{ + // no intersection + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 12.0f; + + rect2.center.x = 10; + rect2.center.y = 10; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 34.0f; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_NONE); + CV_Assert(vertices.empty()); +} + +void CV_RotatedRectangleIntersectionTest::test2() +{ + // partial intersection, rectangles translated + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 1; + rect2.center.y = 1; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(0.0f, 0.0f); + possibleVertices[1] = Point2f(1.0f, 1.0f); + possibleVertices[2] = Point2f(0.0f, 1.0f); + possibleVertices[3] = Point2f(1.0f, 0.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test3() +{ + // partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 1; + rect2.center.y = 1; + rect2.size.width = sqrt(2.0f); + rect2.size.height = 20; + rect2.angle = 45.0f; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 3); + + vector possibleVertices(3); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(0.0f, 1.0f); + possibleVertices[2] = Point2f(1.0f, 0.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test4() +{ + // full intersection, rectangles of same size directly on top of each other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_FULL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(-1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + possibleVertices[2] = Point2f(-1.0f, -1.0f); + possibleVertices[3] = Point2f(1.0f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test5() +{ + // partial intersection, rectangle on top rotated 45 degrees + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 45.0f; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 8); + + vector possibleVertices(8); + + possibleVertices[0] = Point2f(-1.0f, -0.414214f); + possibleVertices[1] = Point2f(-1.0f, 0.414214f); + possibleVertices[2] = Point2f(-0.414214f, -1.0f); + possibleVertices[3] = Point2f(0.414214f, -1.0f); + possibleVertices[4] = Point2f(1.0f, -0.414214f); + possibleVertices[5] = Point2f(1.0f, 0.414214f); + possibleVertices[6] = Point2f(0.414214f, 1.0f); + possibleVertices[7] = Point2f(-0.414214f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test6() +{ + // 6 - partial intersection, rectangle on top of different size + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 10; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + possibleVertices[2] = Point2f(-1.0f, -1.0f); + possibleVertices[3] = Point2f(-1.0f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test7() +{ + // full intersection, rectangle fully enclosed in the other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 12.34; + rect1.size.height = 56.78; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_FULL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + possibleVertices[2] = Point2f(-1.0f, -1.0f); + possibleVertices[3] = Point2f(-1.0f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test8() +{ + // full intersection, rectangle fully enclosed in the other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 2; + rect2.center.y = 2; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 1); + + double dx = vertices[0].x - 1; + double dy = vertices[0].y - 1; + double r = sqrt(dx*dx + dy*dy); + + CV_Assert(r < ACCURACY); +} + +void CV_RotatedRectangleIntersectionTest::test9() +{ + // full intersection, rectangle fully enclosed in the other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 2; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 123.45; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 2); + + vector possibleVertices(2); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +TEST (Imgproc_RotatedRectangleIntersection, accuracy) { CV_RotatedRectangleIntersectionTest test; test.safe_run(); } From bc3dd2452f41900f03348486b83c4d917c8f3964 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Wed, 31 Jul 2013 23:43:25 +1000 Subject: [PATCH 07/12] Added test cpp --- modules/imgproc/doc/pics/intersection.png | Bin 0 -> 32696 bytes modules/imgproc/test/test_intersection.cpp | 499 +++++++++++++++++++++ 2 files changed, 499 insertions(+) create mode 100644 modules/imgproc/doc/pics/intersection.png create mode 100644 modules/imgproc/test/test_intersection.cpp diff --git a/modules/imgproc/doc/pics/intersection.png b/modules/imgproc/doc/pics/intersection.png new file mode 100644 index 0000000000000000000000000000000000000000..4d1367c55e4dcebcd798d331b4a1ad9d97709694 GIT binary patch literal 32696 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

9Pq^EOkj^*9E(elf0)ONnyv&r%C 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 literal 0 HcmV?d00001 diff --git a/modules/imgproc/test/test_intersection.cpp b/modules/imgproc/test/test_intersection.cpp new file mode 100644 index 0000000000..3a53d6f0c8 --- /dev/null +++ b/modules/imgproc/test/test_intersection.cpp @@ -0,0 +1,499 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Nghia Ho, nghiaho12@yahoo.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of OpenCV Foundation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the OpenCV Foundation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +#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 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(); +} + +void CV_RotatedRectangleIntersectionTest::test1() +{ + // no intersection + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 12.0f; + + rect2.center.x = 10; + rect2.center.y = 10; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 34.0f; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_NONE); + CV_Assert(vertices.empty()); +} + +void CV_RotatedRectangleIntersectionTest::test2() +{ + // partial intersection, rectangles translated + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 1; + rect2.center.y = 1; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(0.0f, 0.0f); + possibleVertices[1] = Point2f(1.0f, 1.0f); + possibleVertices[2] = Point2f(0.0f, 1.0f); + possibleVertices[3] = Point2f(1.0f, 0.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test3() +{ + // partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 1; + rect2.center.y = 1; + rect2.size.width = sqrt(2.0f); + rect2.size.height = 20; + rect2.angle = 45.0f; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 3); + + vector possibleVertices(3); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(0.0f, 1.0f); + possibleVertices[2] = Point2f(1.0f, 0.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test4() +{ + // full intersection, rectangles of same size directly on top of each other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_FULL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(-1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + possibleVertices[2] = Point2f(-1.0f, -1.0f); + possibleVertices[3] = Point2f(1.0f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test5() +{ + // partial intersection, rectangle on top rotated 45 degrees + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 45.0f; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 8); + + vector possibleVertices(8); + + possibleVertices[0] = Point2f(-1.0f, -0.414214f); + possibleVertices[1] = Point2f(-1.0f, 0.414214f); + possibleVertices[2] = Point2f(-0.414214f, -1.0f); + possibleVertices[3] = Point2f(0.414214f, -1.0f); + possibleVertices[4] = Point2f(1.0f, -0.414214f); + possibleVertices[5] = Point2f(1.0f, 0.414214f); + possibleVertices[6] = Point2f(0.414214f, 1.0f); + possibleVertices[7] = Point2f(-0.414214f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test6() +{ + // 6 - partial intersection, rectangle on top of different size + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 10; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + possibleVertices[2] = Point2f(-1.0f, -1.0f); + possibleVertices[3] = Point2f(-1.0f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test7() +{ + // full intersection, rectangle fully enclosed in the other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 12.34; + rect1.size.height = 56.78; + rect1.angle = 0; + + rect2.center.x = 0; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_FULL); + CV_Assert(vertices.size() == 4); + + vector possibleVertices(4); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + possibleVertices[2] = Point2f(-1.0f, -1.0f); + possibleVertices[3] = Point2f(-1.0f, 1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +void CV_RotatedRectangleIntersectionTest::test8() +{ + // full intersection, rectangle fully enclosed in the other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 2; + rect2.center.y = 2; + rect2.size.width = 2; + rect2.size.height = 2; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 1); + + double dx = vertices[0].x - 1; + double dy = vertices[0].y - 1; + double r = sqrt(dx*dx + dy*dy); + + CV_Assert(r < ACCURACY); +} + +void CV_RotatedRectangleIntersectionTest::test9() +{ + // full intersection, rectangle fully enclosed in the other + + RotatedRect rect1, rect2; + + rect1.center.x = 0; + rect1.center.y = 0; + rect1.size.width = 2; + rect1.size.height = 2; + rect1.angle = 0; + + rect2.center.x = 2; + rect2.center.y = 0; + rect2.size.width = 2; + rect2.size.height = 123.45; + rect2.angle = 0; + + vector vertices; + + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + + CV_Assert(ret == INTERSECT_PARTIAL); + CV_Assert(vertices.size() == 2); + + vector possibleVertices(2); + + possibleVertices[0] = Point2f(1.0f, 1.0f); + possibleVertices[1] = Point2f(1.0f, -1.0f); + + for( size_t i = 0; i < vertices.size(); i++ ) + { + double bestR = DBL_MAX; + + for( size_t j = 0; j < possibleVertices.size(); j++ ) + { + double dx = vertices[i].x - possibleVertices[j].x; + double dy = vertices[i].y - possibleVertices[j].y; + double r = sqrt(dx*dx + dy*dy); + + bestR = std::min(bestR, r); + } + + CV_Assert(bestR < ACCURACY); + } +} + +TEST (Imgproc_RotatedRectangleIntersection, accuracy) { CV_RotatedRectangleIntersectionTest test; test.safe_run(); } From 18e7aa5d195b210a74628c10f10b0841471e59b2 Mon Sep 17 00:00:00 2001 From: Nghia Date: Wed, 7 Aug 2013 14:57:12 +1000 Subject: [PATCH 08/12] Update intersection.cpp Added #ifdef _WIN32 for finite number testing --- modules/imgproc/src/intersection.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index f63f8230c3..817fc8a368 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -121,7 +121,11 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r float t2 = (vx1*y21 - vy1*x21) / det; // This takes care of parallel lines +#ifdef _WIN32 + if( !_finite(t1) || ! _finite(t2) ) +#else if( !std::isfinite(t1) || !std::isfinite(t2) ) +#endif { continue; } From 95ee02ba8e7d35c578fbce2d4e3d7b19d0b2eca3 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Thu, 8 Aug 2013 01:33:21 +1000 Subject: [PATCH 09/12] Added extra comment to test buildbot --- modules/imgproc/src/intersection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index 817fc8a368..6e409c1680 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -121,6 +121,7 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r float t2 = (vx1*y21 - vy1*x21) / det; // This takes care of parallel lines + // MSVC does not have std::infinite? #ifdef _WIN32 if( !_finite(t1) || ! _finite(t2) ) #else From 79965c9ee9eade95623a028820c7c1a899919ba2 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Tue, 13 Aug 2013 21:26:21 +1000 Subject: [PATCH 10/12] Replaced isfinite() with cvIsInf and cvIsNan --- modules/imgproc/src/intersection.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/imgproc/src/intersection.cpp b/modules/imgproc/src/intersection.cpp index 6e409c1680..d1b80b15c7 100644 --- a/modules/imgproc/src/intersection.cpp +++ b/modules/imgproc/src/intersection.cpp @@ -121,12 +121,7 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r float t2 = (vx1*y21 - vy1*x21) / det; // This takes care of parallel lines - // MSVC does not have std::infinite? -#ifdef _WIN32 - if( !_finite(t1) || ! _finite(t2) ) -#else - if( !std::isfinite(t1) || !std::isfinite(t2) ) -#endif + if( cvIsInf(t1) || cvIsInf(t2) || cvIsNaN(t1) || cvIsNaN(t2) ) { continue; } From 357e5d55f64f4f7439cc907e3b0030a59ef0b58a Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Thu, 26 Sep 2013 19:14:59 +1000 Subject: [PATCH 11/12] Removed trailing whitespaces --- .../imgproc/doc/structural_analysis_and_shape_descriptors.rst | 4 ++-- modules/imgproc/test/test_intersection.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst index d5532fc185..f74671244f 100644 --- a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst +++ b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst @@ -685,7 +685,7 @@ rotatedRectangleIntersection Finds out if there is any intersection between two rotated rectangles. If there is then the vertices of the interesecting region are returned as well. .. ocv:function:: int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion ) -.. ocv:pyfunction:: cv2.rotatedRectangleIntersection( rect1, rect2 ) -> retval, intersectingRegion +.. ocv:pyfunction:: cv2.rotatedRectangleIntersection( rect1, rect2 ) -> retval, intersectingRegion :param rect1: First rectangle @@ -699,7 +699,7 @@ The following values are returned by the function: * INTERSECT_NONE=0 - No intersection - * INTERSECT_PARTIAL=1 - There is a partial intersection + * INTERSECT_PARTIAL=1 - There is a partial intersection * INTERSECT_FULL=2 - One of the rectangle is fully enclosed in the other diff --git a/modules/imgproc/test/test_intersection.cpp b/modules/imgproc/test/test_intersection.cpp index 3a53d6f0c8..fa7f348db8 100644 --- a/modules/imgproc/test/test_intersection.cpp +++ b/modules/imgproc/test/test_intersection.cpp @@ -115,7 +115,7 @@ void CV_RotatedRectangleIntersectionTest::test1() vector vertices; - int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); CV_Assert(ret == INTERSECT_NONE); CV_Assert(vertices.empty()); From 61f5176badbf25443e1a26127a70836c9004af4d Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Tue, 1 Oct 2013 21:03:54 +1000 Subject: [PATCH 12/12] Removed trailing whitespace --- modules/imgproc/test/test_intersection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/test/test_intersection.cpp b/modules/imgproc/test/test_intersection.cpp index fa7f348db8..95e4562eb6 100644 --- a/modules/imgproc/test/test_intersection.cpp +++ b/modules/imgproc/test/test_intersection.cpp @@ -115,7 +115,7 @@ void CV_RotatedRectangleIntersectionTest::test1() vector vertices; - int ret = rotatedRectangleIntersection(rect1, rect2, vertices); + int ret = rotatedRectangleIntersection(rect1, rect2, vertices); CV_Assert(ret == INTERSECT_NONE); CV_Assert(vertices.empty());