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