From 30bf4a5e34251498ba9da548b00e7c833ebf7e3a Mon Sep 17 00:00:00 2001 From: fegorsch Date: Mon, 15 Oct 2018 18:13:03 +0200 Subject: [PATCH 1/2] CircleGridClusterFinder: map circle pattern width and height correctly During the cluster-based detection of circle grids, the detected circle pattern has to be mapped to 3D-points. When doing this the width (i.e. more circles) and height (i.e. less circles) of the pattern need to be identified in image coordinates. Until now this was done by assuming that the shorter side in image coordinates (length in pixels) corresponds to the height in 3D. This assumption does not hold if we look at the pattern from a perspective where the projection of the width is shorter than the projection of the height. This in turn lead to misdetections in although the circle pattern was clearly visible. Instead count how many circles have been detected along two edges of the projected quadrangle and use the one with more circles as width and the one with less as height. --- modules/calib3d/src/circlesgrid.cpp | 37 +++++++++++++++++++++++++---- modules/calib3d/src/circlesgrid.hpp | 2 +- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/modules/calib3d/src/circlesgrid.cpp b/modules/calib3d/src/circlesgrid.cpp index 81693941de..c27604873c 100644 --- a/modules/calib3d/src/circlesgrid.cpp +++ b/modules/calib3d/src/circlesgrid.cpp @@ -178,7 +178,7 @@ void CirclesGridClusterFinder::findGrid(const std::vector &points, if(outsideCorners.size() != outsideCornersCount) return; } - getSortedCorners(hull2f, corners, outsideCorners, sortedCorners); + getSortedCorners(hull2f, patternPoints, corners, outsideCorners, sortedCorners); if(sortedCorners.size() != cornersCount) return; @@ -295,7 +295,18 @@ void CirclesGridClusterFinder::findOutsideCorners(const std::vector #endif } -void CirclesGridClusterFinder::getSortedCorners(const std::vector &hull2f, const std::vector &corners, const std::vector &outsideCorners, std::vector &sortedCorners) +namespace { +double pointLineDistance(const cv::Point2f &p, const cv::Vec4f &line) +{ + Vec3f pa( line[0], line[1], 1 ); + Vec3f pb( line[2], line[3], 1 ); + Vec3f l = pa.cross(pb); + return std::abs((p.x * l[0] + p.y * l[1] + l[2])) * 1.0 / + std::sqrt(double(l[0] * l[0] + l[1] * l[1])); +} +} + +void CirclesGridClusterFinder::getSortedCorners(const std::vector &hull2f, const std::vector &patternPoints, const std::vector &corners, const std::vector &outsideCorners, std::vector &sortedCorners) { Point2f firstCorner; if(isAsymmetricGrid) @@ -341,10 +352,26 @@ void CirclesGridClusterFinder::getSortedCorners(const std::vector & if(!isAsymmetricGrid) { - double dist1 = norm(sortedCorners[0] - sortedCorners[1]); - double dist2 = norm(sortedCorners[1] - sortedCorners[2]); + double dist01 = norm(sortedCorners[0] - sortedCorners[1]); + double dist12 = norm(sortedCorners[1] - sortedCorners[2]); + // Use half the average distance between circles on the shorter side as threshold for determining whether a point lies on an edge. + double thresh = min(dist01, dist12) / min(patternSize.width, patternSize.height) / 2; - if((dist1 > dist2 && patternSize.height > patternSize.width) || (dist1 < dist2 && patternSize.height < patternSize.width)) + size_t circleCount01 = 0; + size_t circleCount12 = 0; + Vec4f line01( sortedCorners[0].x, sortedCorners[0].y, sortedCorners[1].x, sortedCorners[1].y ); + Vec4f line12( sortedCorners[1].x, sortedCorners[1].y, sortedCorners[2].x, sortedCorners[2].y ); + // Count the circles along both edges. + for (size_t i = 0; i < patternPoints.size(); i++) + { + if (pointLineDistance(patternPoints[i], line01) < thresh) + circleCount01++; + if (pointLineDistance(patternPoints[i], line12) < thresh) + circleCount12++; + } + + // Ensure that the edge from sortedCorners[0] to sortedCorners[1] is the one with more circles (i.e. it is interpreted as the pattern's width). + if ((circleCount01 > circleCount12 && patternSize.height > patternSize.width) || (circleCount01 < circleCount12 && patternSize.height < patternSize.width)) { for(size_t i=0; i &hull2f, std::vector &corners); void findOutsideCorners(const std::vector &corners, std::vector &outsideCorners); - void getSortedCorners(const std::vector &hull2f, const std::vector &corners, const std::vector &outsideCorners, std::vector &sortedCorners); + void getSortedCorners(const std::vector &hull2f, const std::vector &patternPoints, const std::vector &corners, const std::vector &outsideCorners, std::vector &sortedCorners); void rectifyPatternPoints(const std::vector &patternPoints, const std::vector &sortedCorners, std::vector &rectifiedPatternPoints); void parsePatternPoints(const std::vector &patternPoints, const std::vector &rectifiedPatternPoints, std::vector ¢ers); From ce00d38bd918e229ce5967a824e493a57d883111 Mon Sep 17 00:00:00 2001 From: fegorsch Date: Fri, 2 Nov 2018 15:13:46 +0100 Subject: [PATCH 2/2] Add test for symmetric circles with clustering --- modules/calib3d/test/test_chesscorners.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/calib3d/test/test_chesscorners.cpp b/modules/calib3d/test/test_chesscorners.cpp index e55d069de0..73e91e1942 100644 --- a/modules/calib3d/test/test_chesscorners.cpp +++ b/modules/calib3d/test/test_chesscorners.cpp @@ -468,5 +468,24 @@ TEST(Calib3d_AsymmetricCirclesPatternDetector, accuracy) { CV_ChessboardDetector TEST(Calib3d_AsymmetricCirclesPatternDetectorWithClustering, accuracy) { CV_ChessboardDetectorTest test( ASYMMETRIC_CIRCLES_GRID, CALIB_CB_CLUSTERING ); test.safe_run(); } #endif +TEST(Calib3d_CirclesPatternDetectorWithClustering, accuracy) +{ + cv::String dataDir = string(TS::ptr()->get_data_path()) + "cv/cameracalibration/circles/"; + + cv::Mat expected; + FileStorage fs(dataDir + "circles_corners15.dat", FileStorage::READ); + fs["corners"] >> expected; + fs.release(); + + cv::Mat image = cv::imread(dataDir + "circles15.png"); + + std::vector centers; + cv::findCirclesGrid(image, Size(10, 8), centers, CALIB_CB_SYMMETRIC_GRID | CALIB_CB_CLUSTERING); + ASSERT_EQ(expected.total(), centers.size()); + + double error = calcError(centers, expected); + ASSERT_LE(error, precise_success_error_level); +} + }} // namespace /* End of file. */