From 103336c76e4c4352193182ed441a40b89a9eb75d Mon Sep 17 00:00:00 2001 From: Deanna Hood Date: Thu, 16 Apr 2015 11:59:05 -0400 Subject: [PATCH 1/3] Fix Bug #3989: correctly identify ellipse with its axes parallel to x-y axes during semi-major axis calculation --- modules/imgproc/src/shapedescr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/shapedescr.cpp b/modules/imgproc/src/shapedescr.cpp index 5e0c432d9..65eddfd1a 100644 --- a/modules/imgproc/src/shapedescr.cpp +++ b/modules/imgproc/src/shapedescr.cpp @@ -447,9 +447,9 @@ cv::RotatedRect cv::fitEllipse( InputArray _points ) // store angle and radii rp[4] = -0.5 * atan2(gfp[2], gfp[1] - gfp[0]); // convert from APP angle usage t = sin(-2.0 * rp[4]); - if( fabs(t) > fabs(gfp[2])*min_eps ) + if( fabs(t) > min_eps ) t = gfp[2]/t; - else + else // ellipse is rotated by an integer multiple of pi/2 t = gfp[1] - gfp[0]; rp[2] = fabs(gfp[0] + gfp[1] - t); if( rp[2] > min_eps ) From 5a552b6d8dc580dd1829eacb0ea9bee4fe4db55a Mon Sep 17 00:00:00 2001 From: Deanna Hood Date: Thu, 16 Apr 2015 19:54:53 -0400 Subject: [PATCH 2/3] Regression test for Bug #3989: check fitEllipse with rotation angles of n*pi/2 --- modules/imgproc/test/test_convhull.cpp | 62 +++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 6b5144f92..e7b2886d3 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1239,7 +1239,6 @@ void CV_FitEllipseTest::run_func() box = (CvBox2D)cv::fitEllipse(cv::cvarrToMat(points)); } - int CV_FitEllipseTest::validate_test_results( int test_case_idx ) { int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); @@ -1354,6 +1353,64 @@ protected: } }; + +// Regression test for incorrect fitEllipse result reported in Bug #3989 +// Check edge cases for rotation angles of ellipse ([-180, 90, 0, 90, 180] degrees) +class CV_FitEllipseParallelTest : public CV_FitEllipseTest +{ +public: + CV_FitEllipseParallelTest(); + ~CV_FitEllipseParallelTest(); +protected: + void generate_point_set( void* points ); + void run_func(void); + Mat pointsMat; +}; + +CV_FitEllipseParallelTest::CV_FitEllipseParallelTest() +{ + min_ellipse_size = 5; +} + +void CV_FitEllipseParallelTest::generate_point_set( void* ) +{ + RNG& rng = ts->get_rng(); + int height = (int)(MAX(high.val[0] - low.val[0], min_ellipse_size)); + int width = (int)(MAX(high.val[1] - low.val[1], min_ellipse_size)); + const int angle = ( (cvtest::randInt(rng) % 5) - 2 ) * 90; + const int dim = max(height, width); + const Point center = Point(dim*2, dim*2); + + if( width > height ) + { + int t; + CV_SWAP( width, height, t ); + } + + Mat image = Mat::zeros(dim*4, dim*4, CV_8UC1); + ellipse(image, center, Size(height, width), angle, + 0, 360, Scalar(255, 0, 0), 1, 8); + + box0.center.x = (float)center.x; + box0.center.y = (float)center.y; + box0.size.width = (float)width*2; + box0.size.height = (float)height*2; + box0.angle = (float)angle; + + vector > contours; + findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); + Mat(contours[0]).convertTo(pointsMat, CV_32F); +} + +void CV_FitEllipseParallelTest::run_func() +{ + box = (CvBox2D)cv::fitEllipse(pointsMat); +} + +CV_FitEllipseParallelTest::~CV_FitEllipseParallelTest(){ + pointsMat.release(); +} + /****************************************************************************************\ * FitLine Test * \****************************************************************************************/ @@ -1377,7 +1434,7 @@ protected: CV_FitLineTest::CV_FitLineTest() { - min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least + min_log_size = 5; // for robust line fitting a dozen of points is needed at least max_log_size = 10; max_noise = 0.05; } @@ -1866,6 +1923,7 @@ TEST(Imgproc_MinTriangle, accuracy) { CV_MinTriangleTest test; test.safe_run(); TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); } TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); } TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); } +TEST(Imgproc_FitEllipse, parallel) { CV_FitEllipseParallelTest test; test.safe_run(); } TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); } TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); } TEST(Imgproc_ContourPerimeterSlice, accuracy) { CV_PerimeterAreaSliceTest test; test.safe_run(); } From 37f77e73977e486ff298735fc764aa614df4cff4 Mon Sep 17 00:00:00 2001 From: Deanna Hood Date: Sat, 18 Apr 2015 12:27:41 -0400 Subject: [PATCH 3/3] Change condition on parallel ellipse case so can only calculate t if necessary --- modules/imgproc/src/shapedescr.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/shapedescr.cpp b/modules/imgproc/src/shapedescr.cpp index 65eddfd1a..205aecb01 100644 --- a/modules/imgproc/src/shapedescr.cpp +++ b/modules/imgproc/src/shapedescr.cpp @@ -446,9 +446,8 @@ cv::RotatedRect cv::fitEllipse( InputArray _points ) // store angle and radii rp[4] = -0.5 * atan2(gfp[2], gfp[1] - gfp[0]); // convert from APP angle usage - t = sin(-2.0 * rp[4]); - if( fabs(t) > min_eps ) - t = gfp[2]/t; + if( fabs(gfp[2]) > min_eps ) + t = gfp[2]/sin(-2.0 * rp[4]); else // ellipse is rotated by an integer multiple of pi/2 t = gfp[1] - gfp[0]; rp[2] = fabs(gfp[0] + gfp[1] - t);