From 175cd03ff2237eca57089f14f866fe17186130b4 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 11 Dec 2020 07:00:25 +0000 Subject: [PATCH] calib3d: fix findCirclesGrid hang - detect case with infinite loop and raise NoConv exception - handle such exception - add support for case with missing `blobDetector` (image contains Point2f array of candidates) - add regression test - undone rectification for "failed" detections too - drop redirectError() usage --- modules/calib3d/include/opencv2/calib3d.hpp | 1 + modules/calib3d/src/calibinit.cpp | 109 ++++++++++---------- modules/calib3d/src/circlesgrid.cpp | 6 +- modules/calib3d/test/test_chesscorners.cpp | 54 ++++++++++ 4 files changed, 110 insertions(+), 60 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 04b5e58e23..e6cad17ae1 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -1453,6 +1453,7 @@ struct CV_EXPORTS_W_SIMPLE CirclesGridFinderParameters2 : public CirclesGridFind - **CALIB_CB_CLUSTERING** uses a special algorithm for grid detection. It is more robust to perspective distortions but much more sensitive to background clutter. @param blobDetector feature detector that finds blobs like dark circles on light background. + If `blobDetector` is NULL then `image` represents Point2f array of candidates. @param parameters struct for finding circles in a grid pattern. The function attempts to determine whether the input image contains a grid of circles. If it is, the diff --git a/modules/calib3d/src/calibinit.cpp b/modules/calib3d/src/calibinit.cpp index 8b857a347d..1332d51ba6 100644 --- a/modules/calib3d/src/calibinit.cpp +++ b/modules/calib3d/src/calibinit.cpp @@ -2178,13 +2178,6 @@ void drawChessboardCorners( InputOutputArray image, Size patternSize, } } -static int quiet_error(int /*status*/, const char* /*func_name*/, - const char* /*err_msg*/, const char* /*file_name*/, - int /*line*/, void* /*userdata*/) -{ - return 0; -} - bool findCirclesGrid(InputArray image, Size patternSize, OutputArray centers, int flags, const Ptr &blobDetector, @@ -2205,15 +2198,22 @@ bool findCirclesGrid2(InputArray _image, Size patternSize, bool isSymmetricGrid = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false; CV_Assert(isAsymmetricGrid ^ isSymmetricGrid); - Mat image = _image.getMat(); std::vector centers; - std::vector keypoints; - blobDetector->detect(image, keypoints); std::vector points; - for (size_t i = 0; i < keypoints.size(); i++) + if (blobDetector) { - points.push_back (keypoints[i].pt); + std::vector keypoints; + blobDetector->detect(_image, keypoints); + for (size_t i = 0; i < keypoints.size(); i++) + { + points.push_back(keypoints[i].pt); + } + } + else + { + CV_CheckTypeEQ(_image.type(), CV_32FC2, "blobDetector must be provided or image must contains Point2f array (std::vector) with candidates"); + _image.copyTo(points); } if(flags & CALIB_CB_ASYMMETRIC_GRID) @@ -2229,64 +2229,59 @@ bool findCirclesGrid2(InputArray _image, Size patternSize, return !centers.empty(); } + bool isValid = false; const int attempts = 2; const size_t minHomographyPoints = 4; Mat H; for (int i = 0; i < attempts; i++) { - centers.clear(); - CirclesGridFinder boxFinder(patternSize, points, parameters); - bool isFound = false; -#define BE_QUIET 1 -#if BE_QUIET - void* oldCbkData; - ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData); // FIXIT not thread safe -#endif - try - { - isFound = boxFinder.findHoles(); - } - catch (const cv::Exception &) - { - - } -#if BE_QUIET - redirectError(oldCbk, oldCbkData); -#endif - if (isFound) - { - switch(parameters.gridType) + centers.clear(); + CirclesGridFinder boxFinder(patternSize, points, parameters); + try { - case CirclesGridFinderParameters::SYMMETRIC_GRID: - boxFinder.getHoles(centers); - break; - case CirclesGridFinderParameters::ASYMMETRIC_GRID: - boxFinder.getAsymmetricHoles(centers); - break; - default: - CV_Error(Error::StsBadArg, "Unknown pattern type"); + bool isFound = boxFinder.findHoles(); + if (isFound) + { + switch(parameters.gridType) + { + case CirclesGridFinderParameters::SYMMETRIC_GRID: + boxFinder.getHoles(centers); + break; + case CirclesGridFinderParameters::ASYMMETRIC_GRID: + boxFinder.getAsymmetricHoles(centers); + break; + default: + CV_Error(Error::StsBadArg, "Unknown pattern type"); + } + + isValid = true; + break; // done, return result + } + } + catch (const cv::Exception& e) + { + CV_UNUSED(e); + CV_LOG_DEBUG(NULL, "findCirclesGrid2: attempt=" << i << ": " << e.what()); + // nothing, next attempt } - if (i != 0) + boxFinder.getHoles(centers); + if (i != attempts - 1) { - Mat orgPointsMat; - transform(centers, orgPointsMat, H.inv()); - convertPointsFromHomogeneous(orgPointsMat, centers); + if (centers.size() < minHomographyPoints) + break; + H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points); } - Mat(centers).copyTo(_centers); - return true; - } + } - boxFinder.getHoles(centers); - if (i != attempts - 1) - { - if (centers.size() < minHomographyPoints) - break; - H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points); - } + if (!H.empty()) // undone rectification + { + Mat orgPointsMat; + transform(centers, orgPointsMat, H.inv()); + convertPointsFromHomogeneous(orgPointsMat, centers); } Mat(centers).copyTo(_centers); - return false; + return isValid; } bool findCirclesGrid(InputArray _image, Size patternSize, diff --git a/modules/calib3d/src/circlesgrid.cpp b/modules/calib3d/src/circlesgrid.cpp index d86d22b205..96279baef2 100644 --- a/modules/calib3d/src/circlesgrid.cpp +++ b/modules/calib3d/src/circlesgrid.cpp @@ -1622,7 +1622,7 @@ size_t CirclesGridFinder::getFirstCorner(std::vector &largeCornerIndices, int cornerIdx = 0; bool waitOutsider = true; - for(;;) + for (size_t i = 0; i < cornersCount * 2; ++i) { if (waitOutsider) { @@ -1632,11 +1632,11 @@ size_t CirclesGridFinder::getFirstCorner(std::vector &largeCornerIndices, else { if (isInsider[(cornerIdx + 1) % cornersCount]) - break; + return cornerIdx; } cornerIdx = (cornerIdx + 1) % cornersCount; } - return cornerIdx; + CV_Error(Error::StsNoConv, "isInsider array has the same values"); } diff --git a/modules/calib3d/test/test_chesscorners.cpp b/modules/calib3d/test/test_chesscorners.cpp index 73e91e1942..f2cf8a8116 100644 --- a/modules/calib3d/test/test_chesscorners.cpp +++ b/modules/calib3d/test/test_chesscorners.cpp @@ -487,5 +487,59 @@ TEST(Calib3d_CirclesPatternDetectorWithClustering, accuracy) ASSERT_LE(error, precise_success_error_level); } +TEST(Calib3d_AsymmetricCirclesPatternDetector, regression_18713) +{ + float pts_[][2] = { + { 166.5, 107 }, { 146, 236 }, { 147, 92 }, { 184, 162 }, { 150, 185.5 }, + { 215, 105 }, { 270.5, 186 }, { 159, 142 }, { 6, 205.5 }, { 32, 148.5 }, + { 126, 163.5 }, { 181, 208.5 }, { 240.5, 62 }, { 84.5, 76.5 }, { 190, 120.5 }, + { 10, 189 }, { 266, 104 }, { 307.5, 207.5 }, { 97, 184 }, { 116.5, 210 }, + { 114, 139 }, { 84.5, 233 }, { 269.5, 139 }, { 136, 126.5 }, { 120, 107.5 }, + { 129.5, 65.5 }, { 212.5, 140.5 }, { 204.5, 60.5 }, { 207.5, 241 }, { 61.5, 94.5 }, + { 186.5, 61.5 }, { 220, 63 }, { 239, 120.5 }, { 212, 186 }, { 284, 87.5 }, + { 62, 114.5 }, { 283, 61.5 }, { 238.5, 88.5 }, { 243, 159 }, { 245, 208 }, + { 298.5, 158.5 }, { 57, 129 }, { 156.5, 63.5 }, { 192, 90.5 }, { 281, 235.5 }, + { 172, 62.5 }, { 291.5, 119.5 }, { 90, 127 }, { 68.5, 166.5 }, { 108.5, 83.5 }, + { 22, 176 } + }; + Mat candidates(51, 1, CV_32FC2, (void*)pts_); + Size patternSize(4, 9); + + std::vector< Point2f > result; + bool res = false; + + // issue reports about hangs + EXPECT_NO_THROW(res = findCirclesGrid(candidates, patternSize, result, CALIB_CB_ASYMMETRIC_GRID, Ptr()/*blobDetector=NULL*/)); + EXPECT_FALSE(res); + + if (cvtest::debugLevel > 0) + { + std::cout << Mat(candidates) << std::endl; + std::cout << Mat(result) << std::endl; + Mat img(Size(400, 300), CV_8UC3, Scalar::all(0)); + + std::vector< Point2f > centers; + candidates.copyTo(centers); + + for (size_t i = 0; i < centers.size(); i++) + { + const Point2f& pt = centers[i]; + //printf("{ %g, %g }, \n", pt.x, pt.y); + circle(img, pt, 5, Scalar(0, 255, 0)); + } + for (size_t i = 0; i < result.size(); i++) + { + const Point2f& pt = result[i]; + circle(img, pt, 10, Scalar(0, 0, 255)); + } + imwrite("test_18713.png", img); + if (cvtest::debugLevel >= 10) + { + imshow("result", img); + waitKey(); + } + } +} + }} // namespace /* End of file. */