Merge pull request #23486 from vovka643:4.x_calibration_with_aruco

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
This commit is contained in:
Vladimir Ponomarev 2023-04-26 12:13:16 +03:00 committed by GitHub
parent d4f29a6361
commit a174a6c0b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,6 +5,7 @@
#include "opencv2/imgcodecs.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include <opencv2/objdetect/charuco_detector.hpp>
#include <cctype>
#include <stdio.h>
@ -48,15 +49,25 @@ static void help(char** argv)
{
printf( "This is a camera calibration sample.\n"
"Usage: %s\n"
" -w=<board_width> # the number of inner corners per one of board dimension\n"
" -h=<board_height> # the number of inner corners per another board dimension\n"
" [-pt=<pattern>] # the type of pattern: chessboard or circles' grid\n"
" -w=<board_width> # the calibration board horizontal size in inner corners "
"for chessboard and in squares or circles for others like ChArUco or circles grid\n"
" -h=<board_height> # the calibration board verical size in inner corners "
"for chessboard and in squares or circles for others like ChArUco or circles grid\n"
" [-pt=<pattern>] # the type of pattern: chessboard, charuco, circles, acircles\n"
" [-n=<number_of_frames>] # 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=<delay>] # a minimum delay in ms between subsequent attempts to capture a next view\n"
" # (used only for video capturing)\n"
" [-s=<squareSize>] # square size in some user-defined units (1 by default)\n"
" [-ms=<markerSize>] # marker size in some user-defined units (0.5 by default)\n"
" [-ad=<arucoDict>] # 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=<dictFilename>] # Custom aruco dictionary file for ChArUco board\n"
" [-o=<out_camera_params>] # 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<vector<Point3f> >& objectPoints,
@ -139,6 +150,12 @@ static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point
float(i*squareSize), 0));
break;
case CHARUCOBOARD:
for( int i = 0; i < boardSize.height-1; i++ )
for( int j = 0; j < boardSize.width-1; j++ )
corners.push_back(Point3f(float(j*squareSize),
float(i*squareSize), 0));
break;
default:
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
}
@ -161,7 +178,8 @@ static bool runCalibration( vector<vector<Point2f> > imagePoints,
vector<vector<Point3f> > 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<vector<Point2f> > imagePoints;
vector<vector<Point2f>> imagePoints;
vector<string> 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<float>("s");
markerSize = parser.get<float>("ms");
string arucoDictName = parser.get<string>("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<std::string>("adf");
nframes = parser.get<int>("n");
delay = parser.get<int>("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<float>("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<int> 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";