From a174a6c0b5aee7f29feb23a9f8d991a329cc86ed Mon Sep 17 00:00:00 2001 From: Vladimir Ponomarev Date: Wed, 26 Apr 2023 12:13:16 +0300 Subject: [PATCH] Merge pull request #23486 from vovka643:4.x_calibration_with_aruco MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add charuco pattern into calibration.cpp #23486 Added charuco pattern into calibration.cpp. Added charuco pattern with predefined aruco dictionary and with dictionary from file. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [х] I agree to contribute to the project under Apache 2 License. - [х] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [х] The PR is proposed to the proper branch - [х] There is a reference to the original bug report and related work - [х] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [х] The feature is well documented and sample code can be built with the project CMake --- samples/cpp/calibration.cpp | 103 ++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/samples/cpp/calibration.cpp b/samples/cpp/calibration.cpp index 89b0564e74..f8134368b2 100644 --- a/samples/cpp/calibration.cpp +++ b/samples/cpp/calibration.cpp @@ -5,6 +5,7 @@ #include "opencv2/imgcodecs.hpp" #include "opencv2/videoio.hpp" #include "opencv2/highgui.hpp" +#include #include #include @@ -48,15 +49,25 @@ static void help(char** argv) { printf( "This is a camera calibration sample.\n" "Usage: %s\n" - " -w= # the number of inner corners per one of board dimension\n" - " -h= # the number of inner corners per another board dimension\n" - " [-pt=] # the type of pattern: chessboard or circles' grid\n" + " -w= # the calibration board horizontal size in inner corners " + "for chessboard and in squares or circles for others like ChArUco or circles grid\n" + " -h= # the calibration board verical size in inner corners " + "for chessboard and in squares or circles for others like ChArUco or circles grid\n" + " [-pt=] # the type of pattern: chessboard, charuco, circles, acircles\n" " [-n=] # the number of frames to use for calibration\n" " # (if not specified, it will be set to the number\n" " # of board views actually available)\n" " [-d=] # a minimum delay in ms between subsequent attempts to capture a next view\n" " # (used only for video capturing)\n" " [-s=] # square size in some user-defined units (1 by default)\n" + " [-ms=] # marker size in some user-defined units (0.5 by default)\n" + " [-ad=] # Aruco dictionary name for ChArUco board. " + "Available ArUco dictionaries: DICT_4X4_50, DICT_4X4_100, DICT_4X4_250, " + "DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, " + "DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50, " + "DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL, " + "DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10, DICT_APRILTAG_36h11\n" + " [-adf=] # Custom aruco dictionary file for ChArUco board\n" " [-o=] # the output filename for intrinsic [and extrinsic] parameters\n" " [-op] # write detected feature points\n" " [-oe] # write extrinsic parameters\n" @@ -90,7 +101,7 @@ static void help(char** argv) } enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }; -enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID }; +enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID, CHARUCOBOARD}; static double computeReprojectionErrors( const vector >& objectPoints, @@ -139,6 +150,12 @@ static void calcChessboardCorners(Size boardSize, float squareSize, vector > imagePoints, vector > objectPoints(1); calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType); - objectPoints[0][boardSize.width - 1].x = objectPoints[0][0].x + grid_width; + int offset = patternType != CHARUCOBOARD ? boardSize.width - 1: boardSize.width - 2; + objectPoints[0][offset].x = objectPoints[0][0].x + grid_width; newObjPoints = objectPoints[0]; objectPoints.resize(imagePoints.size(),objectPoints[0]); @@ -350,10 +368,12 @@ static bool runAndSave(const string& outputFilename, int main( int argc, char** argv ) { Size boardSize, imageSize; - float squareSize, aspectRatio = 1; + float squareSize, markerSize, aspectRatio = 1; Mat cameraMatrix, distCoeffs; string outputFilename; string inputFilename = ""; + int arucoDict; + string dictFilename; int i, nframes; bool writeExtrinsics, writePoints; @@ -367,12 +387,12 @@ int main( int argc, char** argv ) clock_t prevTimestamp = 0; int mode = DETECTION; int cameraId = 0; - vector > imagePoints; + vector> imagePoints; vector imageList; Pattern pattern = CHESSBOARD; cv::CommandLineParser parser(argc, argv, - "{help ||}{w||}{h||}{pt|chessboard|}{n|10|}{d|1000|}{s|1|}{o|out_camera_data.yml|}" + "{help ||}{w||}{h||}{pt|chessboard|}{n|10|}{d|1000|}{s|1|}{ms|0.5|}{ad|DICT_4X4_50|}{adf|None|}{o|out_camera_data.yml|}" "{op||}{oe||}{zt||}{a||}{p||}{v||}{V||}{su||}" "{oo||}{ws|11|}{dt||}" "{fx||}{fy||}{cx||}{cy||}" @@ -394,10 +414,43 @@ int main( int argc, char** argv ) pattern = ASYMMETRIC_CIRCLES_GRID; else if( val == "chessboard" ) pattern = CHESSBOARD; + else if( val == "charuco" ) + pattern = CHARUCOBOARD; else return fprintf( stderr, "Invalid pattern type: must be chessboard or circles\n" ), -1; } + squareSize = parser.get("s"); + markerSize = parser.get("ms"); + + string arucoDictName = parser.get("ad"); + if (arucoDictName == "DICT_4X4_50") { arucoDict = cv::aruco::DICT_4X4_50; } + else if (arucoDictName == "DICT_4X4_100") { arucoDict = cv::aruco::DICT_4X4_100; } + else if (arucoDictName == "DICT_4X4_250") { arucoDict = cv::aruco::DICT_4X4_250; } + else if (arucoDictName == "DICT_4X4_1000") { arucoDict = cv::aruco::DICT_4X4_1000; } + else if (arucoDictName == "DICT_5X5_50") { arucoDict = cv::aruco::DICT_5X5_50; } + else if (arucoDictName == "DICT_5X5_100") { arucoDict = cv::aruco::DICT_5X5_100; } + else if (arucoDictName == "DICT_5X5_250") { arucoDict = cv::aruco::DICT_5X5_250; } + else if (arucoDictName == "DICT_5X5_1000") { arucoDict = cv::aruco::DICT_5X5_1000; } + else if (arucoDictName == "DICT_6X6_50") { arucoDict = cv::aruco::DICT_6X6_50; } + else if (arucoDictName == "DICT_6X6_100") { arucoDict = cv::aruco::DICT_6X6_100; } + else if (arucoDictName == "DICT_6X6_250") { arucoDict = cv::aruco::DICT_6X6_250; } + else if (arucoDictName == "DICT_6X6_1000") { arucoDict = cv::aruco::DICT_6X6_1000; } + else if (arucoDictName == "DICT_7X7_50") { arucoDict = cv::aruco::DICT_7X7_50; } + else if (arucoDictName == "DICT_7X7_100") { arucoDict = cv::aruco::DICT_7X7_100; } + else if (arucoDictName == "DICT_7X7_250") { arucoDict = cv::aruco::DICT_7X7_250; } + else if (arucoDictName == "DICT_7X7_1000") { arucoDict = cv::aruco::DICT_7X7_1000; } + else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { arucoDict = cv::aruco::DICT_ARUCO_ORIGINAL; } + else if (arucoDictName == "DICT_APRILTAG_16h5") { arucoDict = cv::aruco::DICT_APRILTAG_16h5; } + else if (arucoDictName == "DICT_APRILTAG_25h9") { arucoDict = cv::aruco::DICT_APRILTAG_25h9; } + else if (arucoDictName == "DICT_APRILTAG_36h10") { arucoDict = cv::aruco::DICT_APRILTAG_36h10; } + else if (arucoDictName == "DICT_APRILTAG_36h11") { arucoDict = cv::aruco::DICT_APRILTAG_36h11; } + else { + cout << "Incorrect Aruco dictionary name " << arucoDictName << std::endl; + return 1; + } + + dictFilename = parser.get("adf"); nframes = parser.get("n"); delay = parser.get("d"); writePoints = parser.has("op"); @@ -438,7 +491,8 @@ int main( int argc, char** argv ) { flags |= CALIB_FIX_K3; } - float grid_width = squareSize * (boardSize.width - 1); + + float grid_width = squareSize *(pattern != CHARUCOBOARD ? (boardSize.width - 1): (boardSize.width - 2) ); bool release_object = false; if (parser.has("dt")) { grid_width = parser.get("dt"); @@ -463,6 +517,22 @@ int main( int argc, char** argv ) if ( boardSize.height <= 0 ) return fprintf( stderr, "Invalid board height\n" ), -1; + cv::aruco::Dictionary dictionary; + if (dictFilename == "None") { + std::cout << "Using predefined dictionary with id: " << arucoDict << std::endl; + dictionary = aruco::getPredefinedDictionary(arucoDict); + } + else { + std::cout << "Using custom dictionary from file: " << dictFilename << std::endl; + cv::FileStorage dict_file(dictFilename, cv::FileStorage::Mode::READ); + cv::FileNode fn(dict_file.root()); + dictionary.readDictionary(fn); + } + + cv::aruco::CharucoBoard ch_board(boardSize, squareSize, markerSize, dictionary); + std::vector markerIds; + cv::aruco::CharucoDetector ch_detector(ch_board); + if( !inputFilename.empty() ) { if( !videofile && readStringList(samples::findFile(inputFilename), imageList) ) @@ -474,7 +544,7 @@ int main( int argc, char** argv ) capture.open(cameraId); if( !capture.isOpened() && imageList.empty() ) - return fprintf( stderr, "Could not initialize video (%d) capture\n",cameraId ), -2; + return fprintf( stderr, "Could not initialize video (%d) capture\n", cameraId ), -2; if( !imageList.empty() ) nframes = (int)imageList.size(); @@ -529,6 +599,12 @@ int main( int argc, char** argv ) case ASYMMETRIC_CIRCLES_GRID: found = findCirclesGrid( view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID ); break; + case CHARUCOBOARD: + { + ch_detector.detectBoard(view, pointbuf, markerIds); + found = pointbuf.size() == (size_t)(boardSize.width-1)*(boardSize.height-1); + break; + } default: return fprintf( stderr, "Unknown pattern type\n" ), -1; } @@ -546,7 +622,12 @@ int main( int argc, char** argv ) } if(found) - drawChessboardCorners( view, boardSize, Mat(pointbuf), found ); + { + if(pattern != CHARUCOBOARD) + drawChessboardCorners( view, boardSize, Mat(pointbuf), found ); + else + drawChessboardCorners( view, Size(boardSize.width-1, boardSize.height-1), Mat(pointbuf), found ); + } string msg = mode == CAPTURING ? "100/100" : mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";