mirror of
https://github.com/opencv/opencv.git
synced 2025-06-10 19:24:07 +08:00
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:
parent
d4f29a6361
commit
a174a6c0b5
@ -5,6 +5,7 @@
|
|||||||
#include "opencv2/imgcodecs.hpp"
|
#include "opencv2/imgcodecs.hpp"
|
||||||
#include "opencv2/videoio.hpp"
|
#include "opencv2/videoio.hpp"
|
||||||
#include "opencv2/highgui.hpp"
|
#include "opencv2/highgui.hpp"
|
||||||
|
#include <opencv2/objdetect/charuco_detector.hpp>
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -48,15 +49,25 @@ static void help(char** argv)
|
|||||||
{
|
{
|
||||||
printf( "This is a camera calibration sample.\n"
|
printf( "This is a camera calibration sample.\n"
|
||||||
"Usage: %s\n"
|
"Usage: %s\n"
|
||||||
" -w=<board_width> # the number of inner corners per one of board dimension\n"
|
" -w=<board_width> # the calibration board horizontal size in inner corners "
|
||||||
" -h=<board_height> # the number of inner corners per another board dimension\n"
|
"for chessboard and in squares or circles for others like ChArUco or circles grid\n"
|
||||||
" [-pt=<pattern>] # the type of pattern: chessboard 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"
|
" [-n=<number_of_frames>] # the number of frames to use for calibration\n"
|
||||||
" # (if not specified, it will be set to the number\n"
|
" # (if not specified, it will be set to the number\n"
|
||||||
" # of board views actually available)\n"
|
" # of board views actually available)\n"
|
||||||
" [-d=<delay>] # a minimum delay in ms between subsequent attempts to capture a next view\n"
|
" [-d=<delay>] # a minimum delay in ms between subsequent attempts to capture a next view\n"
|
||||||
" # (used only for video capturing)\n"
|
" # (used only for video capturing)\n"
|
||||||
" [-s=<squareSize>] # square size in some user-defined units (1 by default)\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"
|
" [-o=<out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters\n"
|
||||||
" [-op] # write detected feature points\n"
|
" [-op] # write detected feature points\n"
|
||||||
" [-oe] # write extrinsic parameters\n"
|
" [-oe] # write extrinsic parameters\n"
|
||||||
@ -90,7 +101,7 @@ static void help(char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
|
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(
|
static double computeReprojectionErrors(
|
||||||
const vector<vector<Point3f> >& objectPoints,
|
const vector<vector<Point3f> >& objectPoints,
|
||||||
@ -139,6 +150,12 @@ static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point
|
|||||||
float(i*squareSize), 0));
|
float(i*squareSize), 0));
|
||||||
break;
|
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:
|
default:
|
||||||
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
|
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
|
||||||
}
|
}
|
||||||
@ -161,7 +178,8 @@ static bool runCalibration( vector<vector<Point2f> > imagePoints,
|
|||||||
|
|
||||||
vector<vector<Point3f> > objectPoints(1);
|
vector<vector<Point3f> > objectPoints(1);
|
||||||
calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType);
|
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];
|
newObjPoints = objectPoints[0];
|
||||||
|
|
||||||
objectPoints.resize(imagePoints.size(),objectPoints[0]);
|
objectPoints.resize(imagePoints.size(),objectPoints[0]);
|
||||||
@ -350,10 +368,12 @@ static bool runAndSave(const string& outputFilename,
|
|||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
Size boardSize, imageSize;
|
Size boardSize, imageSize;
|
||||||
float squareSize, aspectRatio = 1;
|
float squareSize, markerSize, aspectRatio = 1;
|
||||||
Mat cameraMatrix, distCoeffs;
|
Mat cameraMatrix, distCoeffs;
|
||||||
string outputFilename;
|
string outputFilename;
|
||||||
string inputFilename = "";
|
string inputFilename = "";
|
||||||
|
int arucoDict;
|
||||||
|
string dictFilename;
|
||||||
|
|
||||||
int i, nframes;
|
int i, nframes;
|
||||||
bool writeExtrinsics, writePoints;
|
bool writeExtrinsics, writePoints;
|
||||||
@ -367,12 +387,12 @@ int main( int argc, char** argv )
|
|||||||
clock_t prevTimestamp = 0;
|
clock_t prevTimestamp = 0;
|
||||||
int mode = DETECTION;
|
int mode = DETECTION;
|
||||||
int cameraId = 0;
|
int cameraId = 0;
|
||||||
vector<vector<Point2f> > imagePoints;
|
vector<vector<Point2f>> imagePoints;
|
||||||
vector<string> imageList;
|
vector<string> imageList;
|
||||||
Pattern pattern = CHESSBOARD;
|
Pattern pattern = CHESSBOARD;
|
||||||
|
|
||||||
cv::CommandLineParser parser(argc, argv,
|
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||}"
|
"{op||}{oe||}{zt||}{a||}{p||}{v||}{V||}{su||}"
|
||||||
"{oo||}{ws|11|}{dt||}"
|
"{oo||}{ws|11|}{dt||}"
|
||||||
"{fx||}{fy||}{cx||}{cy||}"
|
"{fx||}{fy||}{cx||}{cy||}"
|
||||||
@ -394,10 +414,43 @@ int main( int argc, char** argv )
|
|||||||
pattern = ASYMMETRIC_CIRCLES_GRID;
|
pattern = ASYMMETRIC_CIRCLES_GRID;
|
||||||
else if( val == "chessboard" )
|
else if( val == "chessboard" )
|
||||||
pattern = CHESSBOARD;
|
pattern = CHESSBOARD;
|
||||||
|
else if( val == "charuco" )
|
||||||
|
pattern = CHARUCOBOARD;
|
||||||
else
|
else
|
||||||
return fprintf( stderr, "Invalid pattern type: must be chessboard or circles\n" ), -1;
|
return fprintf( stderr, "Invalid pattern type: must be chessboard or circles\n" ), -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
squareSize = parser.get<float>("s");
|
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");
|
nframes = parser.get<int>("n");
|
||||||
delay = parser.get<int>("d");
|
delay = parser.get<int>("d");
|
||||||
writePoints = parser.has("op");
|
writePoints = parser.has("op");
|
||||||
@ -438,7 +491,8 @@ int main( int argc, char** argv )
|
|||||||
{
|
{
|
||||||
flags |= CALIB_FIX_K3;
|
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;
|
bool release_object = false;
|
||||||
if (parser.has("dt")) {
|
if (parser.has("dt")) {
|
||||||
grid_width = parser.get<float>("dt");
|
grid_width = parser.get<float>("dt");
|
||||||
@ -463,6 +517,22 @@ int main( int argc, char** argv )
|
|||||||
if ( boardSize.height <= 0 )
|
if ( boardSize.height <= 0 )
|
||||||
return fprintf( stderr, "Invalid board height\n" ), -1;
|
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( !inputFilename.empty() )
|
||||||
{
|
{
|
||||||
if( !videofile && readStringList(samples::findFile(inputFilename), imageList) )
|
if( !videofile && readStringList(samples::findFile(inputFilename), imageList) )
|
||||||
@ -474,7 +544,7 @@ int main( int argc, char** argv )
|
|||||||
capture.open(cameraId);
|
capture.open(cameraId);
|
||||||
|
|
||||||
if( !capture.isOpened() && imageList.empty() )
|
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() )
|
if( !imageList.empty() )
|
||||||
nframes = (int)imageList.size();
|
nframes = (int)imageList.size();
|
||||||
@ -529,6 +599,12 @@ int main( int argc, char** argv )
|
|||||||
case ASYMMETRIC_CIRCLES_GRID:
|
case ASYMMETRIC_CIRCLES_GRID:
|
||||||
found = findCirclesGrid( view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID );
|
found = findCirclesGrid( view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID );
|
||||||
break;
|
break;
|
||||||
|
case CHARUCOBOARD:
|
||||||
|
{
|
||||||
|
ch_detector.detectBoard(view, pointbuf, markerIds);
|
||||||
|
found = pointbuf.size() == (size_t)(boardSize.width-1)*(boardSize.height-1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return fprintf( stderr, "Unknown pattern type\n" ), -1;
|
return fprintf( stderr, "Unknown pattern type\n" ), -1;
|
||||||
}
|
}
|
||||||
@ -546,7 +622,12 @@ int main( int argc, char** argv )
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(found)
|
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" :
|
string msg = mode == CAPTURING ? "100/100" :
|
||||||
mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
|
mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
|
||||||
|
Loading…
Reference in New Issue
Block a user