From f7e52076b649f65d666b390de5fc260105dcd444 Mon Sep 17 00:00:00 2001 From: vovka643 Date: Wed, 22 Feb 2023 00:02:02 +0300 Subject: [PATCH] Define charuco board parameters with command line --- apps/interactive-calibration/calibCommon.hpp | 7 +- .../frameProcessor.cpp | 81 ++++++++++--------- .../frameProcessor.hpp | 3 +- apps/interactive-calibration/main.cpp | 7 ++ .../parametersController.cpp | 56 ++++++++++--- 5 files changed, 103 insertions(+), 51 deletions(-) diff --git a/apps/interactive-calibration/calibCommon.hpp b/apps/interactive-calibration/calibCommon.hpp index 1a3879e632..73cb4ab9fc 100644 --- a/apps/interactive-calibration/calibCommon.hpp +++ b/apps/interactive-calibration/calibCommon.hpp @@ -21,7 +21,7 @@ namespace calib enum InputType { Video, Pictures }; enum InputVideoSource { Camera, File }; - enum TemplateType { AcirclesGrid, Chessboard, chAruco, DoubleAcirclesGrid, CirclesGrid }; + enum TemplateType { AcirclesGrid, Chessboard, ChArUco, DoubleAcirclesGrid, CirclesGrid }; static const std::string mainWindowName = "Calibration"; static const std::string gridWindowName = "Board locations"; @@ -79,8 +79,11 @@ namespace calib InputType captureMethod; InputVideoSource source; TemplateType board; - cv::Size boardSize; + cv::Size inputBoardSize; + cv::Size boardSizeInnerCorners; // board size in inner corners for chessboard + cv::Size boardSizeUnits; // board size in squares, circles, etc. int charucoDictName; + std::string charucoDictFile; int calibrationStep; float charucoSquareLength, charucoMarkerSize; float captureDelay; diff --git a/apps/interactive-calibration/frameProcessor.cpp b/apps/interactive-calibration/frameProcessor.cpp index a3c17ded5e..f43ee9094a 100644 --- a/apps/interactive-calibration/frameProcessor.cpp +++ b/apps/interactive-calibration/frameProcessor.cpp @@ -58,14 +58,14 @@ FrameProcessor::~FrameProcessor() bool CalibProcessor::detectAndParseChessboard(const cv::Mat &frame) { int chessBoardFlags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE | cv::CALIB_CB_FAST_CHECK; - bool isTemplateFound = cv::findChessboardCorners(frame, mBoardSize, mCurrentImagePoints, chessBoardFlags); + bool isTemplateFound = cv::findChessboardCorners(frame, mBoardSizeInnerCorners, mCurrentImagePoints, chessBoardFlags); if (isTemplateFound) { cv::Mat viewGray; cv::cvtColor(frame, viewGray, cv::COLOR_BGR2GRAY); cv::cornerSubPix(viewGray, mCurrentImagePoints, cv::Size(11,11), cv::Size(-1,-1), cv::TermCriteria( cv::TermCriteria::EPS+cv::TermCriteria::COUNT, 30, 0.1 )); - cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isTemplateFound); + cv::drawChessboardCorners(frame, mBoardSizeInnerCorners, cv::Mat(mCurrentImagePoints), isTemplateFound); mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]); } return isTemplateFound; @@ -101,20 +101,20 @@ bool CalibProcessor::detectAndParseChAruco(const cv::Mat &frame) bool CalibProcessor::detectAndParseCircles(const cv::Mat &frame) { - bool isTemplateFound = findCirclesGrid(frame, mBoardSize, mCurrentImagePoints, cv::CALIB_CB_SYMMETRIC_GRID, mBlobDetectorPtr); + bool isTemplateFound = findCirclesGrid(frame, mBoardSizeUnits, mCurrentImagePoints, cv::CALIB_CB_SYMMETRIC_GRID, mBlobDetectorPtr); if(isTemplateFound) { mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]); - cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isTemplateFound); + cv::drawChessboardCorners(frame, mBoardSizeUnits, cv::Mat(mCurrentImagePoints), isTemplateFound); } return isTemplateFound; } bool CalibProcessor::detectAndParseACircles(const cv::Mat &frame) { - bool isTemplateFound = findCirclesGrid(frame, mBoardSize, mCurrentImagePoints, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr); + bool isTemplateFound = findCirclesGrid(frame, mBoardSizeUnits, mCurrentImagePoints, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr); if(isTemplateFound) { mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]); - cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isTemplateFound); + cv::drawChessboardCorners(frame, mBoardSizeUnits, cv::Mat(mCurrentImagePoints), isTemplateFound); } return isTemplateFound; } @@ -125,18 +125,18 @@ bool CalibProcessor::detectAndParseDualACircles(const cv::Mat &frame) cv::Mat invertedView; cv::bitwise_not(frame, invertedView); - bool isWhiteGridFound = cv::findCirclesGrid(frame, mBoardSize, mCurrentImagePoints, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr); + bool isWhiteGridFound = cv::findCirclesGrid(frame, mBoardSizeUnits, mCurrentImagePoints, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr); if(!isWhiteGridFound) return false; - bool isBlackGridFound = cv::findCirclesGrid(invertedView, mBoardSize, blackPointbuf, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr); + bool isBlackGridFound = cv::findCirclesGrid(invertedView, mBoardSizeUnits, blackPointbuf, cv::CALIB_CB_ASYMMETRIC_GRID, mBlobDetectorPtr); if(!isBlackGridFound) { mCurrentImagePoints.clear(); return false; } - cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(mCurrentImagePoints), isWhiteGridFound); - cv::drawChessboardCorners(frame, mBoardSize, cv::Mat(blackPointbuf), isBlackGridFound); + cv::drawChessboardCorners(frame, mBoardSizeUnits, cv::Mat(mCurrentImagePoints), isWhiteGridFound); + cv::drawChessboardCorners(frame, mBoardSizeUnits, cv::Mat(blackPointbuf), isBlackGridFound); mCurrentImagePoints.insert(mCurrentImagePoints.end(), blackPointbuf.begin(), blackPointbuf.end()); mTemplateLocations.insert(mTemplateLocations.begin(), mCurrentImagePoints[0]); @@ -151,14 +151,14 @@ void CalibProcessor::saveFrameData() switch(mBoardType) { case Chessboard: - objectPoints.reserve(mBoardSize.height*mBoardSize.width); - for( int i = 0; i < mBoardSize.height; ++i ) - for( int j = 0; j < mBoardSize.width; ++j ) + objectPoints.reserve(mBoardSizeInnerCorners.height*mBoardSizeInnerCorners.width); + for( int i = 0; i < mBoardSizeInnerCorners.height; ++i ) + for( int j = 0; j < mBoardSizeInnerCorners.width; ++j ) objectPoints.push_back(cv::Point3f(j*mSquareSize, i*mSquareSize, 0)); mCalibData->imagePoints.push_back(mCurrentImagePoints); mCalibData->objectPoints.push_back(objectPoints); break; - case chAruco: + case ChArUco: mCalibData->allCharucoCorners.push_back(mCurrentCharucoCorners); mCalibData->allCharucoIds.push_back(mCurrentCharucoIds); @@ -168,38 +168,38 @@ void CalibProcessor::saveFrameData() mCalibData->objectPoints.push_back(objectPoints); break; case CirclesGrid: - objectPoints.reserve(mBoardSize.height*mBoardSize.width); - for( int i = 0; i < mBoardSize.height; i++ ) - for( int j = 0; j < mBoardSize.width; j++ ) + objectPoints.reserve(mBoardSizeUnits.height*mBoardSizeUnits.width); + for( int i = 0; i < mBoardSizeUnits.height; i++ ) + for( int j = 0; j < mBoardSizeUnits.width; j++ ) objectPoints.push_back(cv::Point3f(j*mSquareSize, i*mSquareSize, 0)); mCalibData->imagePoints.push_back(mCurrentImagePoints); mCalibData->objectPoints.push_back(objectPoints); break; case AcirclesGrid: - objectPoints.reserve(mBoardSize.height*mBoardSize.width); - for( int i = 0; i < mBoardSize.height; i++ ) - for( int j = 0; j < mBoardSize.width; j++ ) + objectPoints.reserve(mBoardSizeUnits.height*mBoardSizeUnits.width); + for( int i = 0; i < mBoardSizeUnits.height; i++ ) + for( int j = 0; j < mBoardSizeUnits.width; j++ ) objectPoints.push_back(cv::Point3f((2*j + i % 2)*mSquareSize, i*mSquareSize, 0)); mCalibData->imagePoints.push_back(mCurrentImagePoints); mCalibData->objectPoints.push_back(objectPoints); break; case DoubleAcirclesGrid: { - float gridCenterX = (2*((float)mBoardSize.width - 1) + 1)*mSquareSize + mTemplDist / 2; - float gridCenterY = (mBoardSize.height - 1)*mSquareSize / 2; - objectPoints.reserve(2*mBoardSize.height*mBoardSize.width); + float gridCenterX = (2*((float)mBoardSizeUnits.width - 1) + 1)*mSquareSize + mTemplDist / 2; + float gridCenterY = (mBoardSizeUnits.height - 1)*mSquareSize / 2; + objectPoints.reserve(2*mBoardSizeUnits.height*mBoardSizeUnits.width); //white part - for( int i = 0; i < mBoardSize.height; i++ ) - for( int j = 0; j < mBoardSize.width; j++ ) + for( int i = 0; i < mBoardSizeUnits.height; i++ ) + for( int j = 0; j < mBoardSizeUnits.width; j++ ) objectPoints.push_back( cv::Point3f(-float((2*j + i % 2)*mSquareSize + mTemplDist + - (2*(mBoardSize.width - 1) + 1)*mSquareSize - gridCenterX), + (2*(mBoardSizeUnits.width - 1) + 1)*mSquareSize - gridCenterX), -float(i*mSquareSize) - gridCenterY, 0)); //black part - for( int i = 0; i < mBoardSize.height; i++ ) - for( int j = 0; j < mBoardSize.width; j++ ) + for( int i = 0; i < mBoardSizeUnits.height; i++ ) + for( int j = 0; j < mBoardSizeUnits.width; j++ ) objectPoints.push_back(cv::Point3f(-float((2*j + i % 2)*mSquareSize - gridCenterX), -float(i*mSquareSize) - gridCenterY, 0)); @@ -261,7 +261,8 @@ bool CalibProcessor::checkLastFrame() } CalibProcessor::CalibProcessor(cv::Ptr data, captureParameters &capParams) : - mCalibData(data), mBoardType(capParams.board), mBoardSize(capParams.boardSize) + mCalibData(data), mBoardType(capParams.board), mBoardSizeUnits(capParams.boardSizeUnits), + mBoardSizeInnerCorners(capParams.boardSizeInnerCorners) { mCapuredFrames = 0; mNeededFramesNum = capParams.calibrationStep; @@ -277,10 +278,18 @@ CalibProcessor::CalibProcessor(cv::Ptr data, captureParameters switch(mBoardType) { - case chAruco: - mArucoDictionary = cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(capParams.charucoDictName)); - mCharucoBoard = cv::makePtr(cv::Size(mBoardSize.width + 1, mBoardSize.height + 1), capParams.charucoSquareLength, - capParams.charucoMarkerSize, mArucoDictionary); + case ChArUco: + if (capParams.charucoDictFile != "None") { + std::string filename = capParams.charucoDictFile; + cv::FileStorage dict_file(filename, cv::FileStorage::Mode::READ); + cv::FileNode fn(dict_file.root()); + mArucoDictionary.readDictionary(fn); + } + else { + mArucoDictionary = cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(capParams.charucoDictName)); + } + mCharucoBoard = cv::makePtr(cv::Size(mBoardSizeUnits.width, mBoardSizeUnits.height), capParams.charucoSquareLength, + capParams.charucoMarkerSize, mArucoDictionary); detector = cv::makePtr(cv::aruco::CharucoDetector(*mCharucoBoard, charucoParameters)); break; case CirclesGrid: @@ -311,7 +320,7 @@ cv::Mat CalibProcessor::processFrame(const cv::Mat &frame) case Chessboard: isTemplateFound = detectAndParseChessboard(frameCopy); break; - case chAruco: + case ChArUco: isTemplateFound = detectAndParseChAruco(frameCopy); break; case CirclesGrid: @@ -391,7 +400,7 @@ void ShowProcessor::drawBoard(cv::Mat &img, cv::InputArray points) void ShowProcessor::drawGridPoints(const cv::Mat &frame) { - if(mBoardType != chAruco) + if(mBoardType != ChArUco) for(std::vector >::iterator it = mCalibdata->imagePoints.begin(); it != mCalibdata->imagePoints.end(); ++it) for(std::vector::iterator pointIt = (*it).begin(); pointIt != (*it).end(); ++pointIt) cv::circle(frame, *pointIt, POINT_SIZE, cv::Scalar(0, 255, 0), 1, cv::LINE_AA); @@ -513,7 +522,7 @@ void ShowProcessor::updateBoardsView() if(mVisMode == Window) { cv::Size originSize = mCalibdata->imageSize; cv::Mat altGridView = cv::Mat::zeros((int)(originSize.height*mGridViewScale), (int)(originSize.width*mGridViewScale), CV_8UC3); - if(mBoardType != chAruco) + if(mBoardType != ChArUco) for(std::vector >::iterator it = mCalibdata->imagePoints.begin(); it != mCalibdata->imagePoints.end(); ++it) if(mBoardType != DoubleAcirclesGrid) drawBoard(altGridView, *it); diff --git a/apps/interactive-calibration/frameProcessor.hpp b/apps/interactive-calibration/frameProcessor.hpp index 8ecdca789e..1294048076 100644 --- a/apps/interactive-calibration/frameProcessor.hpp +++ b/apps/interactive-calibration/frameProcessor.hpp @@ -30,7 +30,8 @@ class CalibProcessor : public FrameProcessor protected: cv::Ptr mCalibData; TemplateType mBoardType; - cv::Size mBoardSize; + cv::Size mBoardSizeUnits; + cv::Size mBoardSizeInnerCorners; std::vector mTemplateLocations; std::vector mCurrentImagePoints; cv::Mat mCurrentCharucoCorners; diff --git a/apps/interactive-calibration/main.cpp b/apps/interactive-calibration/main.cpp index 4d665a62c5..92ea2a93ae 100644 --- a/apps/interactive-calibration/main.cpp +++ b/apps/interactive-calibration/main.cpp @@ -32,6 +32,12 @@ const std::string keys = "{dst | 295 | Distance between white and black parts of daulCircles template}" "{w | | Width of template (in corners or circles)}" "{h | | Height of template (in corners or circles)}" + "{ad | DICT_4X4_50 | Name of predefined ArUco dictionary. 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 }" + "{fad | None | name of file with ArUco dictionary}" "{of | cameraParameters.xml | Output file name}" "{ft | true | Auto tuning of calibration flags}" "{vis | grid | Captured boards visualisation (grid, window)}" @@ -117,6 +123,7 @@ int main(int argc, char** argv) dataController->setParametersFileName(parser.get("of")); cv::Ptr capProcessor, showProcessor; + capProcessor = cv::Ptr(new CalibProcessor(globalData, capParams)); showProcessor = cv::Ptr(new ShowProcessor(globalData, controller, capParams.board)); diff --git a/apps/interactive-calibration/parametersController.cpp b/apps/interactive-calibration/parametersController.cpp index eab183ab69..4ce81f352a 100644 --- a/apps/interactive-calibration/parametersController.cpp +++ b/apps/interactive-calibration/parametersController.cpp @@ -3,6 +3,7 @@ // of this distribution and at http://opencv.org/license.html. #include "parametersController.hpp" +#include #include @@ -36,7 +37,6 @@ bool calib::parametersController::loadFromFile(const std::string &inputFileName) return true; } - readFromNode(reader["charuco_dict"], mCapParams.charucoDictName); if (readFromNode(reader["charuco_square_lenght"], mCapParams.charucoSquareLength)) { std::cout << "DEPRECATION: Parameter 'charuco_square_lenght' has been deprecated (typo). Use 'charuco_square_length' instead." << std::endl; } @@ -52,7 +52,6 @@ bool calib::parametersController::loadFromFile(const std::string &inputFileName) readFromNode(reader["frame_filter_conv_param"], mInternalParameters.filterAlpha); bool retValue = - checkAssertion(mCapParams.charucoDictName >= 0, "Dict name must be >= 0") && checkAssertion(mCapParams.charucoMarkerSize > 0, "Marker size must be positive") && checkAssertion(mCapParams.charucoSquareLength > 0, "Square size must be positive") && checkAssertion(mCapParams.minFramesNum > 1, "Minimal number of frames for calibration < 1") && @@ -109,27 +108,53 @@ bool calib::parametersController::loadFromParser(cv::CommandLineParser &parser) std::string templateType = parser.get("t"); - if(templateType.find("symcircles", 0) == 0) { mCapParams.board = CirclesGrid; - mCapParams.boardSize = cv::Size(4, 11); + mCapParams.boardSizeUnits = cv::Size(4, 11); } else if(templateType.find("circles", 0) == 0) { mCapParams.board = AcirclesGrid; - mCapParams.boardSize = cv::Size(4, 11); + mCapParams.boardSizeUnits = cv::Size(4, 11); } else if(templateType.find("chessboard", 0) == 0) { mCapParams.board = Chessboard; - mCapParams.boardSize = cv::Size(7, 7); + mCapParams.boardSizeUnits = cv::Size(7, 7); } else if(templateType.find("dualcircles", 0) == 0) { mCapParams.board = DoubleAcirclesGrid; - mCapParams.boardSize = cv::Size(4, 11); + mCapParams.boardSizeUnits = cv::Size(4, 11); } else if(templateType.find("charuco", 0) == 0) { - mCapParams.board = chAruco; - mCapParams.boardSize = cv::Size(5, 7); - mCapParams.charucoDictName = 0; + mCapParams.board = ChArUco; + mCapParams.boardSizeUnits = cv::Size(5, 7); + mCapParams.charucoDictFile = parser.get("fad"); + std::string arucoDictName = parser.get("ad"); + + if (arucoDictName == "DICT_4X4_50") { mCapParams.charucoDictName = cv::aruco::DICT_4X4_50; } + else if (arucoDictName == "DICT_4X4_100") { mCapParams.charucoDictName = cv::aruco::DICT_4X4_100; } + else if (arucoDictName == "DICT_4X4_250") { mCapParams.charucoDictName = cv::aruco::DICT_4X4_250; } + else if (arucoDictName == "DICT_4X4_1000") { mCapParams.charucoDictName = cv::aruco::DICT_4X4_1000; } + else if (arucoDictName == "DICT_5X5_50") { mCapParams.charucoDictName = cv::aruco::DICT_5X5_50; } + else if (arucoDictName == "DICT_5X5_100") { mCapParams.charucoDictName = cv::aruco::DICT_5X5_100; } + else if (arucoDictName == "DICT_5X5_250") { mCapParams.charucoDictName = cv::aruco::DICT_5X5_250; } + else if (arucoDictName == "DICT_5X5_1000") { mCapParams.charucoDictName = cv::aruco::DICT_5X5_1000; } + else if (arucoDictName == "DICT_6X6_50") { mCapParams.charucoDictName = cv::aruco::DICT_6X6_50; } + else if (arucoDictName == "DICT_6X6_100") { mCapParams.charucoDictName = cv::aruco::DICT_6X6_100; } + else if (arucoDictName == "DICT_6X6_250") { mCapParams.charucoDictName = cv::aruco::DICT_6X6_250; } + else if (arucoDictName == "DICT_6X6_1000") { mCapParams.charucoDictName = cv::aruco::DICT_6X6_1000; } + else if (arucoDictName == "DICT_7X7_50") { mCapParams.charucoDictName = cv::aruco::DICT_7X7_50; } + else if (arucoDictName == "DICT_7X7_100") { mCapParams.charucoDictName = cv::aruco::DICT_7X7_100; } + else if (arucoDictName == "DICT_7X7_250") { mCapParams.charucoDictName = cv::aruco::DICT_7X7_250; } + else if (arucoDictName == "DICT_7X7_1000") { mCapParams.charucoDictName = cv::aruco::DICT_7X7_1000; } + else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { mCapParams.charucoDictName = cv::aruco::DICT_ARUCO_ORIGINAL; } + else if (arucoDictName == "DICT_APRILTAG_16h5") { mCapParams.charucoDictName = cv::aruco::DICT_APRILTAG_16h5; } + else if (arucoDictName == "DICT_APRILTAG_25h9") { mCapParams.charucoDictName = cv::aruco::DICT_APRILTAG_25h9; } + else if (arucoDictName == "DICT_APRILTAG_36h10") { mCapParams.charucoDictName = cv::aruco::DICT_APRILTAG_36h10; } + else if (arucoDictName == "DICT_APRILTAG_36h11") { mCapParams.charucoDictName = cv::aruco::DICT_APRILTAG_36h11; } + else { + std::cout << "incorrect name of aruco dictionary \n"; + return false; + } mCapParams.charucoSquareLength = 200; mCapParams.charucoMarkerSize = 100; } @@ -139,8 +164,15 @@ bool calib::parametersController::loadFromParser(cv::CommandLineParser &parser) } if(parser.has("w") && parser.has("h")) { - mCapParams.boardSize = cv::Size(parser.get("w"), parser.get("h")); - if(!checkAssertion(mCapParams.boardSize.width > 0 || mCapParams.boardSize.height > 0, + mCapParams.inputBoardSize = cv::Size(parser.get("w"), parser.get("h")); + //only for chessboard pattern board size given in inner corners + if (templateType != "chessboard") { + mCapParams.boardSizeUnits = mCapParams.inputBoardSize; + } + else { + mCapParams.boardSizeInnerCorners = mCapParams.inputBoardSize; + } + if(!checkAssertion(mCapParams.inputBoardSize.width > 0 || mCapParams.inputBoardSize.height > 0, "Board size must be positive")) return false; }