37 #include <visp3/core/vpConfig.h>
39 #if (VISP_HAVE_OPENCV_VERSION >= 0x030000) && defined(HAVE_OPENCV_CALIB3D) && defined(HAVE_OPENCV_HIGHGUI) && \
40 defined(HAVE_OPENCV_IMGPROC) && defined(VISP_HAVE_PUGIXML)
44 #include <opencv2/calib3d/calib3d.hpp>
45 #include <opencv2/core/core.hpp>
46 #include <opencv2/highgui/highgui.hpp>
47 #include <opencv2/imgproc/imgproc.hpp>
49 #include <visp3/vision/vpCalibration.h>
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>
64 #include "calibration-helper.hpp"
66 using namespace calib_helper;
68 void usage(
const char *argv[],
int error)
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
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
79 <<
" --init-from-xml <camera-init.xml> XML file that contains camera parameters" << std::endl
80 <<
" used to initialize the calibration process." << 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
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
90 <<
" --output <file.xml> XML file containing estimated camera parameters." << std::endl
91 <<
" Default: \"camera.xml\"." << std::endl
93 <<
" --help, -h Print this helper message." << std::endl
96 std::cout <<
"Error" << std::endl
98 <<
"Unsupported parameter " << argv[error] << std::endl;
102 int main(
int argc,
const char *argv[])
109 std::string opt_output_file_name =
"camera.xml";
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;
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]);
121 else if (std::string(argv[i]) ==
"--camera-name" && i + 1 < argc) {
122 opt_camera_name = std::string(argv[i + 1]);
125 else if (std::string(argv[i]) ==
"--output" && i + 1 < argc) {
126 opt_output_file_name = std::string(argv[i + 1]);
129 else if (std::string(argv[i]) ==
"--aspect-ratio" && i + 1 < argc) {
130 opt_aspect_ratio = std::atof(argv[i + 1]);
133 else if (std::string(argv[i]) ==
"--help" || std::string(argv[i]) ==
"-h") {
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;
151 std::cout <<
"Invalid input detected. Application stopping. " << std::endl;
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;
159 std::cout <<
"Ouput xml file : " << opt_output_file_name << std::endl;
160 std::cout <<
"Camera name : " << opt_camera_name << std::endl;
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."
179 std::cout <<
"Check if input images name \"" << s.input <<
"\" set in " << opt_inputSettingsFile
180 <<
" config file is correct..." << std::endl;
184 #if defined(VISP_HAVE_X11)
186 #elif defined(VISP_HAVE_GDI)
188 #elif defined(VISP_HAVE_GTK)
190 #elif defined(HAVE_OPENCV_HIGHGUI)
195 bool init_from_xml =
false;
196 if (!opt_init_camera_xml_file.empty()) {
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;
202 init_from_xml =
true;
205 std::cout <<
"Initialize camera parameters from xml file: " << opt_init_camera_xml_file << std::endl;
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;
216 std::cout <<
"Initialize camera parameters with default values " << std::endl;
218 double px = cam_init.
get_px();
219 double py = cam_init.
get_py();
226 std::cout <<
"Camera parameters used for initialization:\n" << cam_init << std::endl;
228 std::vector<vpPoint> model;
229 std::vector<vpCalibration> calibrator;
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));
237 std::vector<CalibInfo> calib_info;
238 std::multimap<double, vpCameraParameters, std::less<double> > map_cam_sorted;
240 map_cam_sorted.insert(std::make_pair(1000, cam_init));
245 char filename[FILENAME_MAX];
246 snprintf(filename, FILENAME_MAX, s.input.c_str(), frame_index);
252 std::vector<cv::Point2f> pointBuf;
255 std::cout <<
"Process frame: " << frame_name << std::flush;
256 bool found = extractCalibrationPoints(s, cvI, pointBuf);
258 std::cout <<
", grid detection status: " << found;
260 std::cout <<
", image rejected" << std::endl;
262 std::cout <<
", image used as input data" << std::endl;
267 std::vector<vpImagePoint> data;
268 for (
unsigned int i = 0; i < pointBuf.size(); i++) {
275 std::vector<vpPoint> calib_points;
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());
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) {
292 calibrator.push_back(calib);
294 calib_info.push_back(CalibInfo(I, calib_points, data, frame_name));
297 map_cam_sorted.insert(std::make_pair(residual, cam));
302 std::cout <<
"frame: " << frame_name <<
", unable to calibrate from single image, image rejected"
315 if (s.tempo > 10.f) {
325 }
while (!reader.
end());
329 if (calibrator.empty()) {
330 std::cerr <<
"Unable to calibrate. Image processing failed !" << std::endl;
335 drawCalibrationOccupancy(I, calib_info, s.boardSize.width);
340 cv::applyColorMap(img, imgColor, cv::COLORMAP_JET);
343 for (
size_t idx1 = 0; idx1 < calib_info.size(); idx1++) {
344 const CalibInfo &calib = calib_info[idx1];
346 for (
size_t idx2 = 0; idx2 < calib.m_imPts.size(); 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);
358 d.
init(I_color, 0, 0,
"Calibration pattern occupancy");
365 if (s.tempo > 10.f) {
379 std::stringstream ss_additional_info;
381 ss_additional_info <<
"<nb_calibration_images>" << calibrator.size() <<
"</nb_calibration_images>";
382 ss_additional_info <<
"<calibration_pattern_type>";
384 switch (s.calibrationPattern) {
385 case Settings::CHESSBOARD:
386 ss_additional_info <<
"Chessboard";
389 case Settings::CIRCLES_GRID:
390 ss_additional_info <<
"Circles grid";
393 case Settings::UNDEFINED:
395 ss_additional_info <<
"Undefined";
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>";
405 std::cout <<
"\nCalibration without distortion in progress on " << calibrator.size() <<
" images..." << std::endl;
408 std::cout << cam << std::endl;
411 for (
size_t i = 0; i < calibrator.size(); i++) {
412 double reproj_error = sqrt(calibrator[i].getResidual() / calibrator[i].get_npt());
414 const CalibInfo &calib = calib_info[i];
415 std::cout <<
"Image " << calib.m_frame_name <<
" reprojection error: " << reproj_error << std::endl;
419 std::ostringstream ss;
420 ss <<
"Reprojection error: " << reproj_error;
430 for (
size_t idx = 0; idx < calib.m_points.size(); idx++) {
433 vpPoint pt = calib.m_points[idx];
440 if (s.tempo > 10.f) {
452 std::cout <<
"\nGlobal reprojection error: " << error << std::endl;
453 ss_additional_info <<
"<global_reprojection_error><without_distortion>" << error <<
"</without_distortion>";
459 std::cout <<
"Camera parameters without distortion successfully saved in \"" << opt_output_file_name <<
"\""
462 std::cout <<
"Failed to save the camera parameters without distortion in \"" << opt_output_file_name <<
"\""
464 std::cout <<
"A file with the same name exists. Remove it to be able "
465 "to save the parameters..."
470 std::cout <<
"Calibration without distortion failed." << std::endl;
474 std::vector<vpCalibration> calibrator_without_dist = calibrator;
476 std::cout <<
"\n\nCalibration with distortion in progress on " << calibrator.size() <<
" images..." << std::endl;
479 std::cout << cam << std::endl;
482 for (
size_t i = 0; i < calibrator.size(); i++) {
483 double reproj_error = sqrt(calibrator[i].getResidual_dist() / calibrator[i].get_npt());
485 const CalibInfo &calib = calib_info[i];
486 std::cout <<
"Image " << calib.m_frame_name <<
" reprojection error: " << reproj_error << std::endl;
490 std::ostringstream ss;
491 ss <<
"Reprojection error: " << reproj_error;
501 for (
size_t idx = 0; idx < calib.m_points.size(); idx++) {
504 vpPoint pt = calib.m_points[idx];
505 pt.
project(calibrator[i].cMo_dist);
511 if (s.tempo > 10.f) {
523 std::cout <<
"\nGlobal reprojection error: " << error << std::endl;
524 ss_additional_info <<
"<with_distortion>" << error <<
"</with_distortion></global_reprojection_error>";
529 d.
init(I_dist_undist, 0, 0,
"Straight lines have to be straight - distorted image / undistorted image");
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."
536 I = calib_info[idx].m_img;
545 calib_info[idx].m_frame_name + std::string(
" distorted"),
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);
554 std::vector<vpImagePoint> current_line_undist = undistort(cam, current_line);
555 double a = 0, b = 0, c = 0;
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;
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;
571 std::vector<cv::Point2f> pointBuf;
574 bool found = extractCalibrationPoints(s, cvI, pointBuf);
576 std::vector<vpImagePoint> grid_points;
577 for (
unsigned int i = 0; i < pointBuf.size(); i++) {
579 grid_points.push_back(ip);
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);
589 double a = 0, b = 0, c = 0;
591 std::cout << calib_info[idx].m_frame_name <<
" undistorted image, line " << i + 1
592 <<
" fitting error: " << line_fitting_error << std::endl;
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;
605 calib_info[idx].m_frame_name + std::string(
" undistorted"),
vpColor::red);
610 if (s.tempo > 10.f) {
623 std::cout << std::endl;
627 ss_additional_info <<
"<camera_poses>";
628 for (
size_t i = 0; i < calibrator.size(); i++) {
630 ss_additional_info <<
"<cMo>" << pose.t() <<
"</cMo>";
632 for (
size_t i = 0; i < calibrator.size(); i++) {
634 ss_additional_info <<
"<cMo_dist>" << pose.t() <<
"</cMo_dist>";
636 ss_additional_info <<
"</camera_poses>";
640 std::cout <<
"Camera parameters without distortion successfully saved in \"" << opt_output_file_name <<
"\""
643 std::cout <<
"Failed to save the camera parameters without distortion in \"" << opt_output_file_name <<
"\""
645 std::cout <<
"A file with the same name exists. Remove it to be able "
646 "to save the parameters..."
649 std::cout << std::endl;
650 std::cout <<
"Estimated pose using vpPoseVector format: [tx ty tz tux tuy tuz] with translation in meter and "
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 <<
": "
658 std::cout <<
"Calibration with distortion failed." << std::endl;
662 std::cout <<
"\nCamera calibration succeeded. Results are savec in " <<
"\"" << opt_output_file_name <<
"\"" << std::endl;
666 std::cout <<
"Catch an exception: " << e << std::endl;
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;
678 #if !defined(VISP_HAVE_PUGIXML)
679 std::cout <<
"pugixml built-in 3rdparty is requested to run the calibration." << std::endl;
Tools for perspective camera 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 green
Display for windows using GDI (available on any windows 32 platform).
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
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...
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)
unsigned int getDownScalingFactor()
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.
const std::string & getStringMessage() const
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 ...
unsigned int getWidth() const
unsigned int getHeight() const
static double lineFitting(const std::vector< vpImagePoint > &imPts, double &a, double &b, double &c)
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 ...
double get_y() const
Get the point y coordinate in the image plane.
double get_x() const
Get the point x coordinate in the image plane.
Implementation of a pose vector and operations on poses.
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")