Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
visp-calibrate-camera.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Camera calibration with chessboard or circle calibration grid.
32 */
33
38#include <iostream>
39
40#include <visp3/core/vpConfig.h>
41
42#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_HIGHGUI) && defined(HAVE_OPENCV_IMGPROC) && defined(VISP_HAVE_PUGIXML) \
43 && (((VISP_HAVE_OPENCV_VERSION < 0x050000) && defined(HAVE_OPENCV_CALIB3D)) || ((VISP_HAVE_OPENCV_VERSION >= 0x050000) && defined(HAVE_OPENCV_3D)))
44
45#include <map>
46
47#if defined(HAVE_OPENCV_CALIB3D)
48#include <opencv2/calib3d/calib3d.hpp>
49#elif defined(HAVE_OPENCV_CALIB)
50#include <opencv2/calib.hpp>
51#endif
52
53#if defined(HAVE_OPENCV_CONTRIB)
54#include <opencv2/contrib/contrib.hpp> // Needed on Ubuntu 16.04 with OpenCV 2.4.9.1
55#endif
56
57#include <opencv2/core/core.hpp>
58#include <opencv2/highgui/highgui.hpp>
59#include <opencv2/imgproc/imgproc.hpp>
60
61#include <visp3/vision/vpCalibration.h>
62
63#include <visp3/core/vpImageTools.h>
64#include <visp3/core/vpIoTools.h>
65#include <visp3/core/vpMeterPixelConversion.h>
66#include <visp3/core/vpPixelMeterConversion.h>
67#include <visp3/core/vpPoint.h>
68#include <visp3/core/vpXmlParserCamera.h>
69#include <visp3/gui/vpDisplayFactory.h>
70#include <visp3/io/vpVideoReader.h>
71
72#include "calibration-helper.hpp"
73
74using namespace calib_helper;
75
76void usage(const char *argv[], int error)
77{
78 std::cout << "Synopsis" << std::endl
79 << " " << argv[0]
80 << " <configuration file>.cfg"
81 << " [--init-from-xml <camera-init.xml>]"
82 << " [--camera-name <name>]"
83 << " [--aspect-ratio <ratio>]"
84 << " [--output <file.xml>]"
85 << " [--help, -h]" << std::endl
86 << std::endl;
87 std::cout << "Description" << std::endl
88 << " <configuration file>.cfg" << std::endl
89 << " Configuration file. See example in \"default-chessboard.cfg\" or in \"default-circles.cfg\"." << std::endl
90 << " Default: \"default.cfg\"." << std::endl
91 << std::endl
92 << " --init-from-xml <camera-init.xml>" << std::endl
93 << " XML file that contains camera parameters used to initialize the calibration process." << std::endl
94 << std::endl
95 << " --camera-name <name>" << std::endl
96 << " Camera name in the XML file set using \"--init-from-xml\" option." << std::endl
97 << " Default: \"Camera\"." << std::endl
98 << std::endl
99 << " --aspect-ratio <ratio>" << std::endl
100 << " Pixel aspect ratio. To estimate px = py, use \"--aspect-ratio 1\" option. Set to -1" << std::endl
101 << " to unset any constraint for px and py parameters. " << std::endl
102 << " Default: -1." << std::endl
103 << std::endl
104 << " --output <file.xml>" << std::endl
105 << " XML file containing estimated camera parameters." << std::endl
106 << " Default: \"camera.xml\"." << std::endl
107 << std::endl
108 << " --help, -h" << std::endl
109 << " Print this helper message." << std::endl
110 << std::endl;
111 if (error) {
112 std::cout << "Error" << std::endl
113 << " "
114 << "Unsupported parameter " << argv[error] << std::endl;
115 }
116}
117
118int main(int argc, const char *argv[])
119{
120#if defined(ENABLE_VISP_NAMESPACE)
121 using namespace VISP_NAMESPACE_NAME;
122#endif
123
124#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
125 vpDisplay *d = nullptr;
126#endif
127
128 try {
129 if (argc == 1) {
130 usage(argv, 0);
131 return EXIT_SUCCESS;
132 }
133 std::string opt_output_file_name = "camera.xml";
134 Settings s;
135 const std::string opt_inputSettingsFile = argc > 1 ? argv[1] : "default.cfg";
136 std::string opt_init_camera_xml_file;
137 std::string opt_camera_name = "Camera";
138 double opt_aspect_ratio = -1; // Not used
139
140 for (int i = 2; i < argc; i++) {
141 if (std::string(argv[i]) == "--init-from-xml" && i + 1 < argc) {
142 opt_init_camera_xml_file = std::string(argv[++i]);
143 }
144 else if (std::string(argv[i]) == "--camera-name" && i + 1 < argc) {
145 opt_camera_name = std::string(argv[++i]);
146 }
147 else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
148 opt_output_file_name = std::string(argv[++i]);
149 }
150 else if (std::string(argv[i]) == "--aspect-ratio" && i + 1 < argc) {
151 opt_aspect_ratio = std::atof(argv[++i]);
152 }
153 else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
154 usage(argv, 0);
155 return EXIT_SUCCESS;
156 }
157 else {
158 usage(argv, i);
159 return EXIT_FAILURE;
160 }
161 }
162
163 std::cout << "Settings from config file: " << argv[1] << std::endl;
164 if (!s.read(opt_inputSettingsFile)) {
165 std::cout << "Could not open the configuration file: \"" << opt_inputSettingsFile << "\"" << std::endl;
166 usage(argv, 0);
167 return EXIT_FAILURE;
168 }
169
170 if (!s.goodInput) {
171 std::cout << "Invalid input detected. Application stopping. " << std::endl;
172 return EXIT_FAILURE;
173 }
174
175 std::cout << "\nSettings from command line options: " << std::endl;
176 if (!opt_init_camera_xml_file.empty()) {
177 std::cout << "Init parameters: " << opt_init_camera_xml_file << std::endl;
178 }
179 std::cout << "Ouput xml file : " << opt_output_file_name << std::endl;
180 std::cout << "Camera name : " << opt_camera_name << std::endl;
181
182 // Check if output file name exists
183 if (vpIoTools::checkFilename(opt_output_file_name)) {
184 std::cout << "\nOutput file name " << opt_output_file_name << " already exists." << std::endl;
185 std::cout << "Remove this file or change output file name using [--output <file.xml>] command line option."
186 << std::endl;
187 return EXIT_SUCCESS;
188 }
189
190 // Start the calibration code
192 vpVideoReader reader;
193 reader.setFileName(s.input);
194 try {
195 reader.open(I);
196 }
197 catch (const vpException &e) {
198 std::cout << "Catch an exception: " << e.getStringMessage() << std::endl;
199 std::cout << "Check if input images name \"" << s.input << "\" set in " << opt_inputSettingsFile
200 << " config file is correct..." << std::endl;
201 return EXIT_FAILURE;
202 }
203
204#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
205 std::shared_ptr<vpDisplay> d = vpDisplayFactory::createDisplay(I, vpDisplay::SCALE_AUTO);
206#else
208#endif
209
210 vpCameraParameters cam_init;
211 bool init_from_xml = false;
212 if (!opt_init_camera_xml_file.empty()) {
213 if (!vpIoTools::checkFilename(opt_init_camera_xml_file)) {
214 std::cout << "Input camera file \"" << opt_init_camera_xml_file << "\" doesn't exist!" << std::endl;
215 std::cout << "Modify [--init-from-xml <camera-init.xml>] option value" << std::endl;
216#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
217 if (display != nullptr) {
218 delete display;
219 }
220#endif
221 return EXIT_FAILURE;
222 }
223 init_from_xml = true;
224 }
225 if (init_from_xml) {
226 std::cout << "Initialize camera parameters from xml file: " << opt_init_camera_xml_file << std::endl;
228 if (parser.parse(cam_init, opt_init_camera_xml_file, opt_camera_name,
230 std::cout << "Unable to find camera with name \"" << opt_camera_name
231 << "\" in file: " << opt_init_camera_xml_file << std::endl;
232 std::cout << "Modify [--camera-name <name>] option value" << std::endl;
233#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
234 if (display != nullptr) {
235 delete display;
236 }
237#endif
238 return EXIT_FAILURE;
239 }
240 }
241 else {
242 std::cout << "Initialize camera parameters with default values " << std::endl;
243 // Initialize camera parameters
244 double px = cam_init.get_px();
245 double py = cam_init.get_py();
246 // Set (u0,v0) in the middle of the image
247 double u0 = I.getWidth() / 2;
248 double v0 = I.getHeight() / 2;
249 cam_init.initPersProjWithoutDistortion(px, py, u0, v0);
250 }
251
252 std::cout << "Camera parameters used for initialization:\n" << cam_init << std::endl;
253
254 std::vector<vpPoint> model;
255 std::vector<vpCalibration> calibrator;
256
257 for (int i = 0; i < s.boardSize.height; i++) {
258 for (int j = 0; j < s.boardSize.width; j++) {
259 model.push_back(vpPoint(j * s.squareSize, i * s.squareSize, 0));
260 }
261 }
262
263 std::vector<CalibInfo> calib_info;
264 std::multimap<double, vpCameraParameters, std::less<double> > map_cam_sorted; // Sorted by residual
265
266 map_cam_sorted.insert(std::make_pair(1000, cam_init));
267
268 do {
269 reader.acquire(I);
270 long frame_index = reader.getFrameIndex();
271 std::string filename = vpIoTools::formatString(s.input, frame_index);
272 std::string frame_name = vpIoTools::getName(filename);
275
276 cv::Mat cvI;
277 std::vector<cv::Point2f> pointBuf;
279
280 std::cout << "Process frame: " << frame_name << std::flush;
281 bool found = extractCalibrationPoints(s, cvI, pointBuf);
282
283 std::cout << ", grid detection status: " << found;
284 if (!found)
285 std::cout << ", image rejected" << std::endl;
286 else
287 std::cout << ", image used as input data" << std::endl;
288
289 if (found) { // If image processing done with success
290 vpDisplay::setTitle(I, frame_name);
291
292 std::vector<vpImagePoint> data;
293 for (unsigned int i = 0; i < pointBuf.size(); i++) {
294 vpImagePoint ip(pointBuf[i].y, pointBuf[i].x);
295 data.push_back(ip);
297 }
298
299 // Calibration on a single mono image
300 std::vector<vpPoint> calib_points;
301 vpCalibration calib;
302 calib.setLambda(0.5);
303 calib.setAspectRatio(opt_aspect_ratio);
304 for (unsigned int i = 0; i < model.size(); i++) {
305 calib.addPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ(), data[i]);
306 calib_points.push_back(vpPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ()));
307 calib_points.back().set_x(data[i].get_u());
308 calib_points.back().set_y(data[i].get_v());
309 }
310
312 bool calib_status = false;
313 std::multimap<double, vpCameraParameters>::const_iterator it_cam;
314 for (it_cam = map_cam_sorted.begin(); it_cam != map_cam_sorted.end(); ++it_cam) {
315 vpCameraParameters cam = it_cam->second;
316 if (calib.computeCalibration(vpCalibration::CALIB_VIRTUAL_VS, cMo, cam, false) == EXIT_SUCCESS) {
317 calibrator.push_back(calib);
318 // Add calibration info
319 calib_info.push_back(CalibInfo(I, calib_points, data, frame_name));
320 calib_status = true;
321 double residual = calib.getResidual();
322 map_cam_sorted.insert(std::make_pair(residual, cam));
323 break;
324 }
325 }
326 if (!calib_status) {
327 std::cout << "frame: " << frame_name << ", unable to calibrate from single image, image rejected"
328 << std::endl;
329 found = false;
330 }
331 }
332
333 if (found)
335 "Image processing succeed", vpColor::green);
336 else
338 "Image processing failed", vpColor::green);
339
340 if (s.tempo > 10.f) {
342 "A click to process the next image", vpColor::green);
345 }
346 else {
348 vpTime::wait(s.tempo * 1000);
349 }
350 } while (!reader.end());
351
352 // Now we consider the multi image calibration
353 // Calibrate by a non linear method based on virtual visual servoing
354 if (calibrator.empty()) {
355 std::cerr << "Unable to calibrate. Image processing failed !" << std::endl;
356#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
357 if (display != nullptr) {
358 delete display;
359 }
360#endif
361 return EXIT_FAILURE;
362 }
363
364 // Display calibration pattern occupancy
365 drawCalibrationOccupancy(I, calib_info, s.boardSize.width);
366
367 cv::Mat1b img(I.getHeight(), I.getWidth());
369 cv::Mat3b imgColor(I.getHeight(), I.getWidth());
370 cv::applyColorMap(img, imgColor, cv::COLORMAP_JET);
371
372 // Draw calibration board corners
373 for (size_t idx1 = 0; idx1 < calib_info.size(); idx1++) {
374 const CalibInfo &calib = calib_info[idx1];
375
376 for (size_t idx2 = 0; idx2 < calib.m_imPts.size(); idx2++) {
377 const vpImagePoint &imPt = calib.m_imPts[idx2];
378 cv::rectangle(imgColor,
379 cv::Rect(static_cast<int>(imPt.get_u() - 2), static_cast<int>(imPt.get_v() - 2),
381 cv::Scalar(0, 0, 0), -1);
382 }
383 }
384
385 vpImage<vpRGBa> I_color;
386 vpImageConvert::convert(imgColor, I_color);
387 d->close(I);
388 d->init(I_color, 0, 0, "Calibration pattern occupancy");
389
390 vpDisplay::display(I_color);
392 15 * vpDisplay::getDownScalingFactor(I_color), "Calibration pattern occupancy in the image",
394
395 if (s.tempo > 10.f) {
396 vpDisplay::displayText(I_color, I_color.getHeight() - 20 * vpDisplay::getDownScalingFactor(I_color),
397 15 * vpDisplay::getDownScalingFactor(I_color), "Click to continue...", vpColor::red);
398 vpDisplay::flush(I_color);
399 vpDisplay::getClick(I_color);
400 }
401 else {
402 vpDisplay::flush(I_color);
403 vpTime::wait(s.tempo * 1000);
404 }
405
406 d->close(I_color);
407
408 std::stringstream ss_additional_info;
409 ss_additional_info << "<date>" << vpTime::getDateTime() << "</date>";
410 ss_additional_info << "<nb_calibration_images>" << calibrator.size() << "</nb_calibration_images>";
411 ss_additional_info << "<calibration_pattern_type>";
412
413 switch (s.calibrationPattern) {
414 case Settings::CHESSBOARD:
415 ss_additional_info << "Chessboard";
416 break;
417
418 case Settings::CIRCLES_GRID:
419 ss_additional_info << "Circles grid";
420 break;
421
422 case Settings::UNDEFINED:
423 default:
424 ss_additional_info << "Undefined";
425 break;
426 }
427 ss_additional_info << "</calibration_pattern_type>";
428 ss_additional_info << "<board_size>" << s.boardSize.width << "x" << s.boardSize.height << "</board_size>";
429 ss_additional_info << "<square_size>" << s.squareSize << "</square_size>";
430
431 double error;
432 // Initialize with camera parameter that has the lowest residual
433 vpCameraParameters cam = map_cam_sorted.begin()->second;
434 std::cout << "\nCalibration without distortion in progress on " << calibrator.size() << " images..." << std::endl;
436 EXIT_SUCCESS) {
437 std::cout << cam << std::endl;
438
439 d->init(I);
440 vpDisplay::setTitle(I, "Without distortion results");
441 ss_additional_info << "<reprojection_error><without_distortion>";
442 for (size_t i = 0; i < calibrator.size(); i++) {
443 double reproj_error = sqrt(calibrator[i].getResidual() / calibrator[i].get_npt());
444
445 const CalibInfo &calib = calib_info[i];
446 std::cout << "Image " << calib.m_frame_name << " reprojection error: " << reproj_error << std::endl;
447
448 ss_additional_info << "<image>" << reproj_error << "</image>";
449 I = calib.m_img;
451
452 std::ostringstream ss;
453 ss << "Reprojection error: " << reproj_error;
455 calib.m_frame_name, vpColor::red);
457 ss.str(), vpColor::red);
459 "Extracted points", vpColor::red);
461 "Projected points", vpColor::green);
462
463 for (size_t idx = 0; idx < calib.m_points.size(); idx++) {
465
466 vpPoint pt = calib.m_points[idx];
467 pt.project(calibrator[i].cMo);
468 vpImagePoint imPt;
469 vpMeterPixelConversion::convertPoint(cam, pt.get_x(), pt.get_y(), imPt);
471 }
472
473 if (s.tempo > 10.f) {
475 15 * vpDisplay::getDownScalingFactor(I), "Click to continue...", vpColor::red);
478 }
479 else {
481 vpTime::wait(s.tempo * 1000);
482 }
483 }
484
485 std::cout << "\nGlobal reprojection error: " << error << std::endl;
486 ss_additional_info << "<global_reprojection_error>" << error << "</global_reprojection_error>";
487
488 ss_additional_info << "</without_distortion>";
490
491 if (xml.save(cam, opt_output_file_name.c_str(), opt_camera_name, I.getWidth(), I.getHeight()) ==
493 std::cout << "Camera parameters without distortion successfully saved in \"" << opt_output_file_name << "\""
494 << std::endl;
495 else {
496 std::cout << "Failed to save the camera parameters without distortion in \"" << opt_output_file_name << "\""
497 << std::endl;
498 std::cout << "A file with the same name exists. Remove it to be able "
499 "to save the parameters..."
500 << std::endl;
501 }
502 }
503 else {
504 std::cout << "Calibration without distortion failed." << std::endl;
505#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
506 if (display != nullptr) {
507 delete display;
508 }
509#endif
510 return EXIT_FAILURE;
511 }
512 vpCameraParameters cam_without_dist = cam;
513 std::vector<vpCalibration> calibrator_without_dist = calibrator;
514
515 std::cout << "\n\nCalibration with distortion in progress on " << calibrator.size() << " images..." << std::endl;
517 EXIT_SUCCESS) {
518 std::cout << cam << std::endl;
519 vpDisplay::setTitle(I, "With distortion results");
520
521 ss_additional_info << "<with_distortion>";
522 for (size_t i = 0; i < calibrator.size(); i++) {
523 double reproj_error = sqrt(calibrator[i].getResidual_dist() / calibrator[i].get_npt());
524
525 const CalibInfo &calib = calib_info[i];
526 std::cout << "Image " << calib.m_frame_name << " reprojection error: " << reproj_error << std::endl;
527 ss_additional_info << "<image>" << reproj_error << "</image>";
528 I = calib.m_img;
530
531 std::ostringstream ss;
532 ss << "Reprojection error: " << reproj_error;
534 calib.m_frame_name, vpColor::red);
536 ss.str(), vpColor::red);
538 "Extracted points", vpColor::red);
540 "Projected points", vpColor::green);
541
542 for (size_t idx = 0; idx < calib.m_points.size(); idx++) {
544
545 vpPoint pt = calib.m_points[idx];
546 pt.project(calibrator[i].cMo_dist);
547 vpImagePoint imPt;
548 vpMeterPixelConversion::convertPoint(cam, pt.get_x(), pt.get_y(), imPt);
550 }
551
552 if (s.tempo > 10.f) {
554 15 * vpDisplay::getDownScalingFactor(I), "Click to continue...", vpColor::red);
557 }
558 else {
560 vpTime::wait(s.tempo * 1000);
561 }
562 }
563 std::cout << "\nGlobal reprojection error: " << error << std::endl;
564 ss_additional_info << "<global_reprojection_error>" << error << "</global_reprojection_error>";
565
566 ss_additional_info << "</with_distortion></reprojection_error>";
567
568 vpImage<unsigned char> I_undist;
569 vpImage<unsigned char> I_dist_undist(I.getHeight(), 2 * I.getWidth());
570 d->close(I);
571 d->init(I_dist_undist, 0, 0, "Straight lines have to be straight - distorted image / undistorted image");
572
573 for (size_t idx = 0; idx < calib_info.size(); idx++) {
574 std::cout << "\nThis tool computes the line fitting error (mean distance error) on image points extracted from "
575 "the raw distorted image."
576 << std::endl;
577
578 I = calib_info[idx].m_img;
579 vpImageTools::undistort(I, cam, I_undist);
580
581 I_dist_undist.insert(I, vpImagePoint(0, 0));
582 I_dist_undist.insert(I_undist, vpImagePoint(0, I.getWidth()));
583 vpDisplay::display(I_dist_undist);
584
585 vpDisplay::displayText(I_dist_undist, 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
586 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
587 calib_info[idx].m_frame_name + std::string(" distorted"), vpColor::red);
588 vpDisplay::displayText(I_dist_undist, 30 * vpDisplay::getDownScalingFactor(I_dist_undist),
589 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
590 "Draw lines from first / last points.", vpColor::red);
591 std::vector<vpImagePoint> grid_points = calib_info[idx].m_imPts;
592 for (int i = 0; i < s.boardSize.height; i++) {
593 std::vector<vpImagePoint> current_line(grid_points.begin() + i * s.boardSize.width,
594 grid_points.begin() + (i + 1) * s.boardSize.width);
595
596 std::vector<vpImagePoint> current_line_undist = undistort(cam, current_line);
597 double a = 0, b = 0, c = 0;
598 double line_fitting_error = vpMath::lineFitting(current_line, a, b, c);
599 double line_fitting_error_undist = vpMath::lineFitting(current_line_undist, a, b, c);
600 std::cout << calib_info[idx].m_frame_name << " line " << i + 1
601 << " fitting error on distorted points: " << line_fitting_error
602 << " ; on undistorted points: " << line_fitting_error_undist << std::endl;
603
604 vpImagePoint ip1 = current_line.front();
605 vpImagePoint ip2 = current_line.back();
606 vpDisplay::displayLine(I_dist_undist, ip1, ip2, vpColor::red);
607 }
608
609 std::cout << "\nThis tool computes the line fitting error (mean distance error) on image points extracted from "
610 "the undistorted image"
611 << " (vpImageTools::undistort())." << std::endl;
612 cv::Mat cvI;
613 std::vector<cv::Point2f> pointBuf;
614 vpImageConvert::convert(I_undist, cvI);
615
616 bool found = extractCalibrationPoints(s, cvI, pointBuf);
617 if (found) {
618 std::vector<vpImagePoint> grid_points;
619 for (unsigned int i = 0; i < pointBuf.size(); i++) {
620 vpImagePoint ip(pointBuf[i].y, pointBuf[i].x);
621 grid_points.push_back(ip);
622 }
623
624 vpDisplay::displayText(I_dist_undist, 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
625 I.getWidth() + 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
626 calib_info[idx].m_frame_name + std::string(" undistorted"), vpColor::red);
627 for (int i = 0; i < s.boardSize.height; i++) {
628 std::vector<vpImagePoint> current_line(grid_points.begin() + i * s.boardSize.width,
629 grid_points.begin() + (i + 1) * s.boardSize.width);
630
631 double a = 0, b = 0, c = 0;
632 double line_fitting_error = vpMath::lineFitting(current_line, a, b, c);
633 std::cout << calib_info[idx].m_frame_name << " undistorted image, line " << i + 1
634 << " fitting error: " << line_fitting_error << std::endl;
635
636 vpImagePoint ip1 = current_line.front() + vpImagePoint(0, I.getWidth());
637 vpImagePoint ip2 = current_line.back() + vpImagePoint(0, I.getWidth());
638 vpDisplay::displayLine(I_dist_undist, ip1, ip2, vpColor::red);
639 }
640 }
641 else {
642 std::string msg("Unable to detect grid on undistorted image");
643 std::cout << msg << std::endl;
644 std::cout << "Check that the grid is not too close to the image edges" << std::endl;
645 vpDisplay::displayText(I_dist_undist, 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
646 15 * vpDisplay::getDownScalingFactor(I_dist_undist),
647 calib_info[idx].m_frame_name + std::string(" undistorted"), vpColor::red);
648 vpDisplay::displayText(I_dist_undist, 30 * vpDisplay::getDownScalingFactor(I_dist_undist),
649 15 * vpDisplay::getDownScalingFactor(I_dist_undist), msg, vpColor::red);
650 }
651
652 if (s.tempo > 10.f) {
654 I_dist_undist, I_dist_undist.getHeight() - 20 * vpDisplay::getDownScalingFactor(I_dist_undist),
655 15 * vpDisplay::getDownScalingFactor(I_dist_undist), "Click to continue...", vpColor::red);
656 vpDisplay::flush(I_dist_undist);
657 vpDisplay::getClick(I_dist_undist);
658 }
659 else {
660 vpDisplay::flush(I_dist_undist);
661 vpTime::wait(s.tempo * 1000);
662 }
663 }
664
665 std::cout << std::endl;
667
668 // Camera poses
669 ss_additional_info << "<camera_poses>";
670 for (size_t i = 0; i < calibrator.size(); i++) {
671 vpPoseVector pose(calibrator[i].cMo);
672 ss_additional_info << "<cMo>" << pose.t() << "</cMo>";
673 }
674 for (size_t i = 0; i < calibrator.size(); i++) {
675 vpPoseVector pose(calibrator[i].cMo_dist);
676 ss_additional_info << "<cMo_dist>" << pose.t() << "</cMo_dist>";
677 }
678 ss_additional_info << "</camera_poses>";
679
680 if (xml.save(cam, opt_output_file_name.c_str(), opt_camera_name, I.getWidth(), I.getHeight(),
681 ss_additional_info.str()) == vpXmlParserCamera::SEQUENCE_OK)
682 std::cout << "Camera parameters without distortion successfully saved in \"" << opt_output_file_name << "\""
683 << std::endl;
684 else {
685 std::cout << "Failed to save the camera parameters without distortion in \"" << opt_output_file_name << "\""
686 << std::endl;
687 std::cout << "A file with the same name exists. Remove it to be able "
688 "to save the parameters..."
689 << std::endl;
690 }
691 std::cout << std::endl;
692 std::cout << "Estimated pose using vpPoseVector format: [tx ty tz tux tuy tuz] with translation in meter and "
693 "rotation in rad"
694 << std::endl;
695 for (unsigned int i = 0; i < calibrator.size(); i++)
696 std::cout << "Estimated pose on input data extracted from " << calib_info[i].m_frame_name << ": "
697 << vpPoseVector(calibrator[i].cMo_dist).t() << std::endl;
698 }
699 else {
700 std::cout << "Calibration with distortion failed." << std::endl;
701#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
702 if (display != nullptr) {
703 delete display;
704 }
705#endif
706 return EXIT_FAILURE;
707 }
708
709 std::cout << "\nCamera calibration succeeded. Results are saved in " << "\"" << opt_output_file_name << "\"" << std::endl;
710#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
711 if (display != nullptr) {
712 delete display;
713 }
714#endif
715 return EXIT_SUCCESS;
716 }
717 catch (const vpException &e) {
718 std::cout << "Catch an exception: " << e << std::endl;
719#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
720 if (display != nullptr) {
721 delete display;
722 }
723#endif
724 return EXIT_FAILURE;
725 }
726}
727#else
728int main()
729{
730#if !defined(HAVE_OPENCV_IMGPROC)
731 std::cout << "This example requires OpenCV imgproc module." << std::endl;
732#endif
733#if !defined(HAVE_OPENCV_HIGHGUI)
734 std::cout << "This example requires OpenCV highgui module." << std::endl;
735#endif
736#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION < 0x050000) && !defined(HAVE_OPENCV_CALIB3D)
737 std::cout << "This example requires OpenCV calib3d module." << std::endl;
738#endif
739#if defined(VISP_HAVE_OPENCV) && (VISP_HAVE_OPENCV_VERSION >= 0x050000) && !defined(HAVE_OPENCV_3D)
740 std::cout << "This example requires OpenCV 3d module." << std::endl;
741#endif
742#if !defined(VISP_HAVE_PUGIXML)
743 std::cout << "pugixml built-in 3rdparty is requested to run the calibration." << std::endl;
744#endif
745 return EXIT_SUCCESS;
746}
747#endif
Tools for perspective camera intrinsic parameters calibration.
static void setLambda(const double &lambda)
int computeCalibration(vpCalibrationMethodType method, vpHomogeneousMatrix &cMo_est, vpCameraParameters &cam_est, bool verbose=false)
double getResidual(void) const
int addPoint(double X, double Y, double Z, vpImagePoint &ip)
static int computeCalibrationMulti(vpCalibrationMethodType method, std::vector< vpCalibration > &table_cal, vpCameraParameters &cam_est, double &globalReprojectionError, bool verbose=false)
void setAspectRatio(double aspect_ratio)
Generic class defining intrinsic camera parameters.
void initPersProjWithoutDistortion(double px, double py, double u0, double v0)
@ perspectiveProjWithoutDistortion
Perspective projection without distortion model.
static const vpColor red
Definition vpColor.h:198
static const vpColor green
Definition vpColor.h:201
Class that defines generic functionalities for display.
Definition vpDisplay.h:171
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
static void setTitle(const vpImage< unsigned char > &I, const std::string &windowtitle)
static void flush(const vpImage< unsigned char > &I)
unsigned int getDownScalingFactor()
Definition vpDisplay.h:218
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
error that can be emitted by ViSP classes.
Definition vpException.h:60
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
double get_u() const
double get_v() const
static void undistort(const vpImage< Type > &I, const vpCameraParameters &cam, vpImage< Type > &newI, unsigned int nThreads=2)
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getHeight() const
Definition vpImage.h:181
static bool checkFilename(const std::string &filename)
static std::string formatString(const std::string &name, unsigned int val)
static std::string getName(const std::string &pathname)
static double lineFitting(const std::vector< vpImagePoint > &imPts, double &a, double &b, double &c)
Definition vpMath.cpp:411
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
Definition vpPoint.h:79
double get_y() const
Get the point y coordinate in the image plane.
Definition vpPoint.cpp:429
double get_x() const
Get the point x coordinate in the image plane.
Definition vpPoint.cpp:427
Implementation of a pose vector and operations on poses.
vpRowVector t() const
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
void open(vpImage< vpRGBa > &I) VP_OVERRIDE
void setFileName(const std::string &filename)
long getFrameIndex() const
void acquire(vpImage< vpRGBa > &I) VP_OVERRIDE
XML parser to load and save intrinsic camera parameters.
int save(const vpCameraParameters &cam, const std::string &filename, const std::string &camera_name, unsigned int image_width=0, unsigned int image_height=0, const std::string &additionalInfo="", bool verbose=true)
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.
VISP_EXPORT int wait(double t0, double t)
VISP_EXPORT std::string getDateTime(const std::string &format="%Y/%m/%d %H:%M:%S")