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 | |
24 | using namespace calib; |
25 | |
26 | const 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 | |
51 | bool 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 | |
62 | static void deleteButton(int, void* data) |
63 | { |
64 | (static_cast<cv::Ptr<calibDataController>*>(data))->get()->deleteLastFrame(); |
65 | calib::showOverlayMessage(message: "Last frame deleted" ); |
66 | } |
67 | |
68 | static void deleteAllButton(int, void* data) |
69 | { |
70 | (static_cast<cv::Ptr<calibDataController>*>(data))->get()->deleteAllData(); |
71 | calib::showOverlayMessage(message: "All frames deleted" ); |
72 | } |
73 | |
74 | static 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 |
81 | static void switchVisualizationModeButton(int, void* data) |
82 | { |
83 | ShowProcessor* processor = static_cast<ShowProcessor*>(((cv::Ptr<FrameProcessor>*)data)->get()); |
84 | processor->switchVisualizationMode(); |
85 | } |
86 | |
87 | static 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 | |
96 | int 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 | |