diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp index 1f41474405..e8300c82bf 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_board.hpp @@ -166,11 +166,11 @@ public: */ CV_WRAP std::vector getChessboardCorners() const; - /** @brief get CharucoBoard::nearestMarkerIdx + /** @brief get CharucoBoard::nearestMarkerIdx, for each charuco corner, nearest marker index in ids array */ CV_PROP std::vector > getNearestMarkerIdx() const; - /** @brief get CharucoBoard::nearestMarkerCorners + /** @brief get CharucoBoard::nearestMarkerCorners, for each charuco corner, nearest marker corner id of each marker */ CV_PROP std::vector > getNearestMarkerCorners() const; diff --git a/modules/objdetect/src/aruco/aruco_board.cpp b/modules/objdetect/src/aruco/aruco_board.cpp index 2ad6c5757b..cf45a96450 100644 --- a/modules/objdetect/src/aruco/aruco_board.cpp +++ b/modules/objdetect/src/aruco/aruco_board.cpp @@ -319,8 +319,9 @@ struct CharucoBoardImpl : Board::Impl { // vector of chessboard 3D corners precalculated std::vector chessboardCorners; - // for each charuco corner, nearest marker id and nearest marker corner id of each marker + // for each charuco corner, nearest marker index in ids array std::vector > nearestMarkerIdx; + // for each charuco corner, nearest marker corner id of each marker std::vector > nearestMarkerCorners; void createCharucoBoard(); diff --git a/modules/objdetect/src/aruco/charuco_detector.cpp b/modules/objdetect/src/aruco/charuco_detector.cpp index 55a7f8d888..97b8c53f71 100644 --- a/modules/objdetect/src/aruco/charuco_detector.cpp +++ b/modules/objdetect/src/aruco/charuco_detector.cpp @@ -5,6 +5,7 @@ #include "../precomp.hpp" #include +#include #include "opencv2/objdetect/charuco_detector.hpp" #include "aruco_utils.hpp" @@ -26,12 +27,13 @@ struct CharucoDetector::CharucoDetectorImpl { bool checkBoard(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray charucoCorners, InputArray charucoIds) { vector mCorners; markerCorners.getMatVector(mCorners); - Mat mIds = markerIds.getMat(); + const Mat& mIds = markerIds.getMat(); - Mat chCorners = charucoCorners.getMat(); - Mat chIds = charucoIds.getMat(); + const Mat& chCorners = charucoCorners.getMat(); + const Mat& chIds = charucoIds.getMat(); + const vector& boardIds = board.getIds(); - vector > nearestMarkerIdx = board.getNearestMarkerIdx(); + const 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] @@ -41,13 +43,19 @@ struct CharucoDetector::CharucoDetectorImpl { Point2f charucoCorner(chCorners.ptr(0)[i]); for (size_t j = 0ull; j < mIds.total(); j++) { int idMaker = mIds.ptr(0)[j]; + // skip the check if the marker is not in the current board. + if (find(boardIds.begin(), boardIds.end(), idMaker) == boardIds.end()) + continue; 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]; + // nearestMarkerIdx contains for each charuco corner, nearest marker index in ids array + const int nearestMarkerId1 = boardIds[nearestMarkerIdx[chId][0]]; + const int nearestMarkerId2 = boardIds[nearestMarkerIdx[chId][1]]; + if (nearestMarkerId1 == idMaker || nearestMarkerId2 == idMaker) { + int nearestCornerId = nearestMarkerId1 == idMaker ? board.getNearestMarkerCorners()[chId][0] : board.getNearestMarkerCorners()[chId][1]; Point2f nearestCorner = mCorners[j].ptr(0)[nearestCornerId]; + // distToNearest: distance from the charuco corner to charuco corner-forming markers float distToNearest = sqrt(normL2Sqr(nearestCorner - charucoCorner)); distance[chId].x = max(distance[chId].x, distToNearest); // check that nearestCorner is nearest point @@ -363,6 +371,7 @@ void CharucoDetector::detectBoard(InputArray image, OutputArray charucoCorners, InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) const { charucoDetectorImpl->detectBoard(image, charucoCorners, charucoIds, markerCorners, markerIds); if (charucoDetectorImpl->checkBoard(markerCorners, markerIds, charucoCorners, charucoIds) == false) { + CV_LOG_DEBUG(NULL, "ChArUco board is built incorrectly"); charucoCorners.release(); charucoIds.release(); } diff --git a/modules/objdetect/test/test_charucodetection.cpp b/modules/objdetect/test/test_charucodetection.cpp index 9e561bc40a..256e1942d7 100644 --- a/modules/objdetect/test/test_charucodetection.cpp +++ b/modules/objdetect/test/test_charucodetection.cpp @@ -717,4 +717,58 @@ TEST_P(CharucoBoard, testWrongSizeDetection) ASSERT_TRUE(detectedCharucoIds.empty()); } +TEST(Charuco, testSeveralBoardsWithCustomIds) +{ + Size res{500, 500}; + Mat K = (Mat_(3,3) << + 0.5*res.width, 0, 0.5*res.width, + 0, 0.5*res.height, 0.5*res.height, + 0, 0, 1); + + Mat expected_corners = (Mat_(9,2) << + 200, 200, + 250, 200, + 300, 200, + 200, 250, + 250, 250, + 300, 250, + 200, 300, + 250, 300, + 300, 300 + ); + + + aruco::Dictionary dict = cv::aruco::getPredefinedDictionary(aruco::DICT_4X4_50); + vector ids1 = {0, 1, 33, 3, 4, 5, 6, 8}, ids2 = {7, 9, 44, 11, 12, 13, 14, 15}; + aruco::CharucoBoard board1(Size(4, 4), 1.f, .8f, dict, ids1), board2(Size(4, 4), 1.f, .8f, dict, ids2); + + // generate ChArUco board + Mat gray; + { + Mat gray1, gray2; + board1.generateImage(Size(res.width, res.height), gray1, 150); + board2.generateImage(Size(res.width, res.height), gray2, 150); + hconcat(gray1, gray2, gray); + } + + aruco::CharucoParameters charucoParameters; + charucoParameters.cameraMatrix = K; + aruco::CharucoDetector detector1(board1, charucoParameters), detector2(board2, charucoParameters); + + vector ids; + vector corners; + Mat c_ids1, c_ids2, c_corners1, c_corners2; + + detector1.detectBoard(gray, c_corners1, c_ids1, corners, ids); + detector2.detectBoard(gray, c_corners2, c_ids2, corners, ids); + + ASSERT_EQ(ids.size(), size_t(16)); + ASSERT_EQ(c_corners1.rows, expected_corners.rows); + EXPECT_NEAR(0, cvtest::norm(expected_corners, c_corners1.reshape(1), NORM_INF), 3e-1); + + ASSERT_EQ(c_corners2.rows, expected_corners.rows); + expected_corners.col(0) += 500; + EXPECT_NEAR(0, cvtest::norm(expected_corners, c_corners2.reshape(1), NORM_INF), 3e-1); +} + }} // namespace