1// This file is part of OpenCV project.
2// It is subject to the license terms in the LICENSE file found in the top-level directory
3// of this distribution and at http://opencv.org/license.html.
4
5#include <opencv2/core.hpp>
6#include <opencv2/calib3d.hpp>
7#include <opencv2/cvconfig.h>
8#include <opencv2/highgui.hpp>
9
10
11#include <string>
12#include <vector>
13#include <stdexcept>
14#include <algorithm>
15#include <iostream>
16
17#include "calibCommon.hpp"
18#include "calibPipeline.hpp"
19#include "frameProcessor.hpp"
20#include "calibController.hpp"
21#include "parametersController.hpp"
22#include "rotationConverters.hpp"
23
24using namespace calib;
25
26const std::string keys =
27 "{v | | Input from video file }"
28 "{ci | 0 | Default camera id }"
29 "{flip | false | Vertical flip of input frames }"
30 "{t | circles | Template for calibration (circles, chessboard, dualCircles, charuco, symcircles) }"
31 "{sz | 16.3 | Distance between two nearest centers of circles or squares on calibration board}"
32 "{dst | 295 | Distance between white and black parts of daulCircles template}"
33 "{w | | Width of template (in corners or circles)}"
34 "{h | | Height of template (in corners or circles)}"
35 "{ad | DICT_4X4_50 | Name of predefined ArUco dictionary. Available ArUco dictionaries: "
36 "DICT_4X4_50, DICT_4X4_100, DICT_4X4_250, DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, "
37 "DICT_5X5_1000, DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50, DICT_7X7_100, "
38 "DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL, DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, "
39 "DICT_APRILTAG_36h10, DICT_APRILTAG_36h11 }"
40 "{fad | None | name of file with ArUco dictionary}"
41 "{of | cameraParameters.xml | Output file name}"
42 "{ft | true | Auto tuning of calibration flags}"
43 "{vis | grid | Captured boards visualisation (grid, window)}"
44 "{d | 0.8 | Min delay between captures}"
45 "{pf | defaultConfig.xml| Advanced application parameters}"
46 "{save_frames | false | Save frames that contribute to final calibration}"
47 "{zoom | 1 | Zoom factor applied to the preview image}"
48 "{force_reopen | false | Forcefully reopen camera in case of errors}"
49 "{help | | Print help}";
50
51bool calib::showOverlayMessage(const std::string& message)
52{
53#ifdef HAVE_QT
54 cv::displayOverlay(mainWindowName, message, OVERLAY_DELAY);
55 return true;
56#else
57 std::cout << message << std::endl;
58 return false;
59#endif
60}
61
62static void deleteButton(int, void* data)
63{
64 (static_cast<cv::Ptr<calibDataController>*>(data))->get()->deleteLastFrame();
65 calib::showOverlayMessage(message: "Last frame deleted");
66}
67
68static void deleteAllButton(int, void* data)
69{
70 (static_cast<cv::Ptr<calibDataController>*>(data))->get()->deleteAllData();
71 calib::showOverlayMessage(message: "All frames deleted");
72}
73
74static void saveCurrentParamsButton(int, void* data)
75{
76 if((static_cast<cv::Ptr<calibDataController>*>(data))->get()->saveCurrentCameraParameters())
77 calib::showOverlayMessage(message: "Calibration parameters saved");
78}
79
80#ifdef HAVE_QT
81static void switchVisualizationModeButton(int, void* data)
82{
83 ShowProcessor* processor = static_cast<ShowProcessor*>(((cv::Ptr<FrameProcessor>*)data)->get());
84 processor->switchVisualizationMode();
85}
86
87static void undistortButton(int state, void* data)
88{
89 ShowProcessor* processor = static_cast<ShowProcessor*>(((cv::Ptr<FrameProcessor>*)data)->get());
90 processor->setUndistort(static_cast<bool>(state));
91 calib::showOverlayMessage(std::string("Undistort is ") +
92 (static_cast<bool>(state) ? std::string("on") : std::string("off")));
93}
94#endif //HAVE_QT
95
96int main(int argc, char** argv)
97{
98 cv::CommandLineParser parser(argc, argv, keys);
99 if(parser.has(name: "help")) {
100 parser.printMessage();
101 return 0;
102 }
103 std::cout << consoleHelp << std::endl;
104 parametersController paramsController;
105
106 if(!paramsController.loadFromParser(parser))
107 return 0;
108
109 captureParameters capParams = paramsController.getCaptureParameters();
110 internalParameters intParams = paramsController.getInternalParameters();
111
112 cv::TermCriteria solverTermCrit = cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS,
113 intParams.solverMaxIters, intParams.solverEps);
114 cv::Ptr<calibrationData> globalData(new calibrationData);
115 if(!parser.has(name: "v")) globalData->imageSize = capParams.cameraResolution;
116
117 int calibrationFlags = 0;
118 if(intParams.fastSolving) calibrationFlags |= cv::CALIB_USE_QR;
119 cv::Ptr<calibController> controller(new calibController(globalData, calibrationFlags,
120 parser.get<bool>(name: "ft"), capParams.minFramesNum));
121 cv::Ptr<calibDataController> dataController(new calibDataController(globalData, capParams.maxFramesNum,
122 intParams.filterAlpha));
123 dataController->setParametersFileName(parser.get<std::string>(name: "of"));
124
125 cv::Ptr<FrameProcessor> capProcessor, showProcessor;
126
127 capProcessor = cv::Ptr<FrameProcessor>(new CalibProcessor(globalData, capParams));
128 showProcessor = cv::Ptr<FrameProcessor>(new ShowProcessor(globalData, controller, capParams.board));
129
130 if(parser.get<std::string>(name: "vis").find(s: "window") == 0) {
131 static_cast<ShowProcessor*>(showProcessor.get())->setVisualizationMode(Window);
132 cv::namedWindow(winname: gridWindowName);
133 cv::moveWindow(winname: gridWindowName, x: 1280, y: 500);
134 }
135
136 cv::Ptr<CalibPipeline> pipeline(new CalibPipeline(capParams));
137 std::vector<cv::Ptr<FrameProcessor> > processors;
138 processors.push_back(x: capProcessor);
139 processors.push_back(x: showProcessor);
140
141 cv::namedWindow(winname: mainWindowName);
142 cv::moveWindow(winname: mainWindowName, x: 10, y: 10);
143#ifdef HAVE_QT
144 cv::createButton("Delete last frame", deleteButton, &dataController,
145 cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
146 cv::createButton("Delete all frames", deleteAllButton, &dataController,
147 cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
148 cv::createButton("Undistort", undistortButton, &showProcessor,
149 cv::QT_CHECKBOX | cv::QT_NEW_BUTTONBAR, false);
150 cv::createButton("Save current parameters", saveCurrentParamsButton, &dataController,
151 cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
152 cv::createButton("Switch visualisation mode", switchVisualizationModeButton, &showProcessor,
153 cv::QT_PUSH_BUTTON | cv::QT_NEW_BUTTONBAR);
154#endif //HAVE_QT
155 try {
156 bool pipelineFinished = false;
157 while(!pipelineFinished)
158 {
159 PipelineExitStatus exitStatus = pipeline->start(processors);
160 if (exitStatus == Finished) {
161 if(controller->getCommonCalibrationState())
162 saveCurrentParamsButton(0, data: &dataController);
163 pipelineFinished = true;
164 continue;
165 }
166 else if (exitStatus == Calibrate) {
167
168 dataController->rememberCurrentParameters();
169 globalData->imageSize = pipeline->getImageSize();
170 calibrationFlags = controller->getNewFlags();
171
172 globalData->totalAvgErr =
173 cv::calibrateCamera(objectPoints: globalData->objectPoints, imagePoints: globalData->imagePoints,
174 imageSize: globalData->imageSize, cameraMatrix: globalData->cameraMatrix,
175 distCoeffs: globalData->distCoeffs, rvecs: cv::noArray(), tvecs: cv::noArray(),
176 stdDeviationsIntrinsics: globalData->stdDeviations, stdDeviationsExtrinsics: cv::noArray(), perViewErrors: globalData->perViewErrors,
177 flags: calibrationFlags, criteria: solverTermCrit);
178 dataController->updateUndistortMap();
179 dataController->printParametersToConsole(output&: std::cout);
180 controller->updateState();
181 for(int j = 0; j < capParams.calibrationStep; j++)
182 dataController->filterFrames();
183 static_cast<ShowProcessor*>(showProcessor.get())->updateBoardsView();
184 }
185 else if (exitStatus == DeleteLastFrame) {
186 deleteButton(0, data: &dataController);
187 static_cast<ShowProcessor*>(showProcessor.get())->updateBoardsView();
188 }
189 else if (exitStatus == DeleteAllFrames) {
190 deleteAllButton(0, data: &dataController);
191 static_cast<ShowProcessor*>(showProcessor.get())->updateBoardsView();
192 }
193 else if (exitStatus == SaveCurrentData) {
194 saveCurrentParamsButton(0, data: &dataController);
195 }
196 else if (exitStatus == SwitchUndistort)
197 static_cast<ShowProcessor*>(showProcessor.get())->switchUndistort();
198 else if (exitStatus == SwitchVisualisation)
199 static_cast<ShowProcessor*>(showProcessor.get())->switchVisualizationMode();
200
201 for (std::vector<cv::Ptr<FrameProcessor> >::iterator it = processors.begin(); it != processors.end(); ++it)
202 (*it)->resetState();
203 }
204 }
205 catch (const std::runtime_error& exp) {
206 std::cout << exp.what() << std::endl;
207 }
208
209 return 0;
210}
211

source code of opencv/apps/interactive-calibration/main.cpp