diff --git a/modules/objdetect/src/aruco/charuco_detector.cpp b/modules/objdetect/src/aruco/charuco_detector.cpp index b6d3b819641e0a24261e637424a7377f1287b28f..55a7f8d888459e49468eb8d413e945dc5c76fd07 100644 --- a/modules/objdetect/src/aruco/charuco_detector.cpp +++ b/modules/objdetect/src/aruco/charuco_detector.cpp @@ -23,6 +23,54 @@ struct CharucoDetector::CharucoDetectorImpl { arucoDetector(_arucoDetector) {} + bool checkBoard(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray charucoCorners, InputArray charucoIds) { + vector mCorners; + markerCorners.getMatVector(mCorners); + Mat mIds = markerIds.getMat(); + + Mat chCorners = charucoCorners.getMat(); + Mat chIds = charucoIds.getMat(); + + vector > nearestMarkerIdx = board.getNearestMarkerIdx(); + vector distance(board.getNearestMarkerIdx().size(), Point2f(0.f, std::numeric_limits::max())); + // distance[i].x: max distance from the i-th charuco corner to charuco corner-forming markers. + // The two charuco corner-forming markers of i-th charuco corner are defined in getNearestMarkerIdx()[i] + // distance[i].y: min distance from the charuco corner to other markers. + for (size_t i = 0ull; i < chIds.total(); i++) { + int chId = chIds.ptr(0)[i]; + Point2f charucoCorner(chCorners.ptr(0)[i]); + for (size_t j = 0ull; j < mIds.total(); j++) { + int idMaker = mIds.ptr(0)[j]; + Point2f centerMarker((mCorners[j].ptr(0)[0] + mCorners[j].ptr(0)[1] + + mCorners[j].ptr(0)[2] + mCorners[j].ptr(0)[3]) / 4.f); + float dist = sqrt(normL2Sqr(centerMarker - charucoCorner)); + // check distance from the charuco corner to charuco corner-forming markers + if (nearestMarkerIdx[chId][0] == idMaker || nearestMarkerIdx[chId][1] == idMaker) { + int nearestCornerId = nearestMarkerIdx[chId][0] == idMaker ? board.getNearestMarkerCorners()[chId][0] : board.getNearestMarkerCorners()[chId][1]; + Point2f nearestCorner = mCorners[j].ptr(0)[nearestCornerId]; + float distToNearest = sqrt(normL2Sqr(nearestCorner - charucoCorner)); + distance[chId].x = max(distance[chId].x, distToNearest); + // check that nearestCorner is nearest point + { + Point2f mid1 = (mCorners[j].ptr(0)[(nearestCornerId + 1) % 4]+nearestCorner)*0.5f; + Point2f mid2 = (mCorners[j].ptr(0)[(nearestCornerId + 3) % 4]+nearestCorner)*0.5f; + float tmpDist = min(sqrt(normL2Sqr(mid1 - charucoCorner)), sqrt(normL2Sqr(mid2 - charucoCorner))); + if (tmpDist < distToNearest) + return false; + } + } + // check distance from the charuco corner to other markers + else + distance[chId].y = min(distance[chId].y, dist); + } + // if distance from the charuco corner to charuco corner-forming markers more then distance from the charuco corner to other markers, + // then a false board is found. + if (distance[chId].x > 0.f && distance[chId].y < std::numeric_limits::max() && distance[chId].x > distance[chId].y) + return false; + } + return true; + } + /** Calculate the maximum window sizes for corner refinement for each charuco corner based on the distance * to their closest markers */ vector getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, InputArray markerIds, @@ -246,6 +294,31 @@ struct CharucoDetector::CharucoDetectorImpl { Mat(filteredCharucoIds).copyTo(_filteredCharucoIds); return (int)_filteredCharucoIds.total(); } + + void detectBoard(InputArray image, OutputArray charucoCorners, OutputArray charucoIds, + InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) { + CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.total() == markerIds.total())); + vector> tmpMarkerCorners; + vector tmpMarkerIds; + InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners; + InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds; + + if (markerCorners.empty() && markerIds.empty()) { + vector > rejectedMarkers; + arucoDetector.detectMarkers(image, _markerCorners, _markerIds, rejectedMarkers); + if (charucoParameters.tryRefineMarkers) + arucoDetector.refineDetectedMarkers(image, board, _markerCorners, _markerIds, rejectedMarkers); + } + // if camera parameters are avaible, use approximated calibration + if(!charucoParameters.cameraMatrix.empty()) + interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, image, charucoCorners, charucoIds); + // else use local homography + else + interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, image, charucoCorners, charucoIds); + // to return a charuco corner, its closest aruco markers should have been detected + filterCornersWithoutMinMarkers(charucoCorners, charucoIds, _markerIds, charucoCorners, charucoIds); +} + }; CharucoDetector::CharucoDetector(const CharucoBoard &board, const CharucoParameters &charucoParams, @@ -288,30 +361,11 @@ void CharucoDetector::setRefineParameters(const RefineParameters& refineParamete void CharucoDetector::detectBoard(InputArray image, OutputArray charucoCorners, OutputArray charucoIds, InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) const { - CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.total() == markerIds.total())); - vector> tmpMarkerCorners; - vector tmpMarkerIds; - InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners; - InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds; - - if (markerCorners.empty() && markerIds.empty()) { - vector > rejectedMarkers; - charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds, rejectedMarkers); - if (charucoDetectorImpl->charucoParameters.tryRefineMarkers) - charucoDetectorImpl->arucoDetector.refineDetectedMarkers(image, charucoDetectorImpl->board, _markerCorners, - _markerIds, rejectedMarkers); + charucoDetectorImpl->detectBoard(image, charucoCorners, charucoIds, markerCorners, markerIds); + if (charucoDetectorImpl->checkBoard(markerCorners, markerIds, charucoCorners, charucoIds) == false) { + charucoCorners.release(); + charucoIds.release(); } - // if camera parameters are avaible, use approximated calibration - if(!charucoDetectorImpl->charucoParameters.cameraMatrix.empty()) - charucoDetectorImpl->interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, image, charucoCorners, - charucoIds); - // else use local homography - else - charucoDetectorImpl->interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, image, charucoCorners, - charucoIds); - // to return a charuco corner, its closest aruco markers should have been detected - charucoDetectorImpl->filterCornersWithoutMinMarkers(charucoCorners, charucoIds, _markerIds, charucoCorners, - charucoIds); } void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds, @@ -416,7 +470,7 @@ void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diam // interpolate the charuco corners of the diamond vector currentMarkerCorners; Mat aux; - detectBoard(grey, currentMarkerCorners, aux, currentMarker, currentMarkerId); + charucoDetectorImpl->detectBoard(grey, currentMarkerCorners, aux, currentMarker, currentMarkerId); // if everything is ok, save the diamond if(currentMarkerCorners.size() > 0ull) { diff --git a/modules/objdetect/test/test_charucodetection.cpp b/modules/objdetect/test/test_charucodetection.cpp index 3a459e11fcf3eb0d663cc1d7edeb314838742fcf..9e561bc40a5409189388f43c3b0d63fc364f93e5 100644 --- a/modules/objdetect/test/test_charucodetection.cpp +++ b/modules/objdetect/test/test_charucodetection.cpp @@ -689,4 +689,32 @@ TEST(Charuco, testmatchImagePoints) } } +typedef testing::TestWithParam CharucoBoard; +INSTANTIATE_TEST_CASE_P(/**/, CharucoBoard, testing::Values(Size(3, 2), Size(3, 2), Size(6, 2), Size(2, 6), + Size(3, 4), Size(4, 3), Size(7, 3), Size(3, 7))); +TEST_P(CharucoBoard, testWrongSizeDetection) +{ + cv::Size boardSize = GetParam(); + ASSERT_FALSE(boardSize.width == boardSize.height); + aruco::CharucoBoard board(boardSize, 1.f, 0.5f, aruco::getPredefinedDictionary(aruco::DICT_4X4_50)); + + vector detectedCharucoIds, detectedArucoIds; + vector detectedCharucoCorners; + vector> detectedArucoCorners; + Mat boardImage; + board.generateImage(boardSize*40, boardImage); + + swap(boardSize.width, boardSize.height); + aruco::CharucoDetector detector(aruco::CharucoBoard(boardSize, 1.f, 0.5f, aruco::getPredefinedDictionary(aruco::DICT_4X4_50))); + // try detect board with wrong size + detector.detectBoard(boardImage, detectedCharucoCorners, detectedCharucoIds, detectedArucoCorners, detectedArucoIds); + + // aruco markers must be found + ASSERT_EQ(detectedArucoIds.size(), board.getIds().size()); + ASSERT_EQ(detectedArucoCorners.size(), board.getIds().size()); + // charuco corners should not be found in board with wrong size + ASSERT_TRUE(detectedCharucoCorners.empty()); + ASSERT_TRUE(detectedCharucoIds.empty()); +} + }} // namespace