40 #include <visp3/core/vpConfig.h> 42 #if VISP_HAVE_OPENCV_VERSION >= 0x020300 46 #include <opencv2/calib3d/calib3d.hpp> 47 #include <opencv2/core/core.hpp> 48 #include <opencv2/highgui/highgui.hpp> 49 #include <opencv2/imgproc/imgproc.hpp> 51 #include <visp3/vision/vpCalibration.h> 53 #include <visp3/core/vpImageTools.h> 54 #include <visp3/core/vpIoTools.h> 55 #include <visp3/core/vpMeterPixelConversion.h> 56 #include <visp3/core/vpPixelMeterConversion.h> 57 #include <visp3/core/vpPoint.h> 58 #include <visp3/core/vpXmlParserCamera.h> 59 #include <visp3/gui/vpDisplayD3D.h> 60 #include <visp3/gui/vpDisplayGDI.h> 61 #include <visp3/gui/vpDisplayGTK.h> 62 #include <visp3/gui/vpDisplayOpenCV.h> 63 #include <visp3/gui/vpDisplayX.h> 64 #include <visp3/io/vpVideoReader.h> 66 #include "calibration-helper.hpp" 70 void usage(
const char *argv[],
int error)
72 std::cout <<
"Synopsis" << std::endl
73 <<
" " << argv[0] <<
" <configuration file>.cfg [--init-from-xml <camera-init.xml>]" 74 <<
" [--camera-name <name>] [--aspect-ratio <ratio>] [--output <file.xml>] [--help] [-h]" << std::endl << std::endl;
75 std::cout <<
"Description" << std::endl
76 <<
" <configuration file>.cfg Configuration file. See example in" << std::endl
77 <<
" \"default-chessboard.cfg\" or in \"default-circles.cfg\"." << std::endl
78 <<
" Default: \"default.cfg\"." << std::endl << 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 << std::endl
81 <<
" --camera-name <name> Camera name in the XML file set using \"--init-from-xml\" option." << std::endl
82 <<
" Default: \"Camera\"." << std::endl << std::endl
83 <<
" --aspect-ratio <ratio> Pixel aspect ratio. " << std::endl
84 <<
" To estimate px = py, use \"--aspect-ratio 1\" option. Set to -1" << std::endl
85 <<
" to unset any constraint for px and py parameters. " << std::endl
86 <<
" Default: -1." << std::endl << std::endl
87 <<
" --output <file.xml> XML file containing estimated camera parameters." << std::endl
88 <<
" Default: \"camera.xml\"." << std::endl << std::endl
89 <<
" --help, -h Print this helper message." << std::endl << std::endl;
91 std::cout <<
"Error" << std::endl
92 <<
" " <<
"Unsupported parameter " << argv[error] << std::endl;
96 int main(
int argc,
const char *argv[])
103 std::string opt_output_file_name =
"camera.xml";
105 const std::string opt_inputSettingsFile = argc > 1 ? argv[1] :
"default.cfg";
106 std::string opt_init_camera_xml_file;
107 std::string opt_camera_name =
"Camera";
108 double opt_aspect_ratio = -1;
110 for (
int i = 2; i < argc; i++) {
111 if (std::string(argv[i]) ==
"--init-from-xml" && i+1 < argc) {
112 opt_init_camera_xml_file = std::string(argv[i + 1]);
115 else if (std::string(argv[i]) ==
"--camera-name" && i+1 < argc) {
116 opt_camera_name = std::string(argv[i + 1]);
119 else if (std::string(argv[i]) ==
"--output" && i+1 < argc) {
120 opt_output_file_name = std::string(argv[i + 1]);
123 else if (std::string(argv[i]) ==
"--aspect-ratio" && i+1 < argc) {
124 opt_aspect_ratio = std::atof(argv[i + 1]);
127 else if (std::string(argv[i]) ==
"--help" || std::string(argv[i]) ==
"-h") {
137 std::cout <<
"Settings from config file: " << argv[1] << std::endl;
138 if (!s.read(opt_inputSettingsFile)) {
139 std::cout <<
"Could not open the configuration file: \"" << opt_inputSettingsFile <<
"\"" << std::endl;
145 std::cout <<
"Invalid input detected. Application stopping. " << std::endl;
149 std::cout <<
"\nSettings from command line options: " << std::endl;
150 if (!opt_init_camera_xml_file.empty()) {
151 std::cout <<
"Init parameters: " << opt_init_camera_xml_file << std::endl;
153 std::cout <<
"Ouput xml file : " << opt_output_file_name << std::endl;
154 std::cout <<
"Camera name : " << opt_camera_name << std::endl;
158 std::cout <<
"\nOutput file name " << opt_output_file_name <<
" already exists." << std::endl;
159 std::cout <<
"Remove this file or change output file name using [--output <file.xml>] command line option." 172 std::cout <<
"Check if input images name \"" << s.input <<
"\" set in " << opt_inputSettingsFile
173 <<
" config file is correct..." << std::endl;
179 #elif defined VISP_HAVE_GDI 181 #elif defined VISP_HAVE_GTK 183 #elif defined VISP_HAVE_OPENCV 188 bool init_from_xml =
false;
189 if (!opt_init_camera_xml_file.empty()) {
191 std::cout <<
"Input camera file \"" << opt_init_camera_xml_file <<
"\" doesn't exist!" << std::endl;
192 std::cout <<
"Modify [--init-from-xml <camera-init.xml>] option value" << std::endl;
195 init_from_xml =
true;
198 std::cout <<
"Initialize camera parameters from xml file: " << opt_init_camera_xml_file << std::endl;
200 if (parser.
parse(cam_init, opt_init_camera_xml_file, opt_camera_name,
202 std::cout <<
"Unable to find camera with name \"" << opt_camera_name
203 <<
"\" in file: " << opt_init_camera_xml_file << std::endl;
204 std::cout <<
"Modify [--camera-name <name>] option value" << std::endl;
208 std::cout <<
"Initialize camera parameters with default values " << std::endl;
210 double px = cam_init.
get_px();
211 double py = cam_init.
get_py();
218 std::cout <<
"Camera parameters used for initialization:\n" << cam_init << std::endl;
220 std::vector<vpPoint> model;
221 std::vector<vpCalibration> calibrator;
223 for (
int i = 0; i < s.boardSize.height; i++) {
224 for (
int j = 0; j < s.boardSize.width; j++) {
225 model.push_back(
vpPoint(j * s.squareSize, i * s.squareSize, 0));
229 std::vector<CalibInfo> calib_info;
230 std::multimap<double, vpCameraParameters, std::less<double> > map_cam_sorted;
232 map_cam_sorted.insert(std::make_pair(1000, cam_init));
237 char filename[FILENAME_MAX];
238 sprintf(filename, s.input.c_str(), frame_index);
244 std::vector<cv::Point2f> pointBuf;
247 std::cout <<
"Process frame: " << frame_name << std::flush;
248 bool found = extractCalibrationPoints(s, cvI, pointBuf);
250 std::cout <<
", grid detection status: " << found;
252 std::cout <<
", image rejected" << std::endl;
254 std::cout <<
", image used as input data" << std::endl;
259 std::vector<vpImagePoint> data;
260 for (
unsigned int i = 0; i < pointBuf.size(); i++) {
267 std::vector<vpPoint> calib_points;
271 for (
unsigned int i = 0; i < model.size(); i++) {
272 calib.
addPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ(), data[i]);
273 calib_points.push_back(
vpPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ()));
274 calib_points.back().set_x(data[i].get_u());
275 calib_points.back().set_y(data[i].get_v());
279 bool calib_status =
false;
280 std::multimap<double, vpCameraParameters>::const_iterator it_cam;
281 for (it_cam = map_cam_sorted.begin(); it_cam != map_cam_sorted.end(); ++it_cam) {
284 calibrator.push_back(calib);
286 calib_info.push_back(CalibInfo(I, calib_points, data, frame_name));
289 map_cam_sorted.insert(std::make_pair(residual, cam));
294 std::cout <<
"frame: " << frame_name <<
", unable to calibrate from single image, image rejected" 307 if (s.tempo > 10.f) {
316 }
while (!reader.
end());
320 if (calibrator.empty()) {
321 std::cerr <<
"Unable to calibrate. Image processing failed !" << std::endl;
326 drawCalibrationOccupancy(I, calib_info, s.boardSize.width);
330 "Calibration pattern occupancy in the image",
vpColor::red);
336 std::stringstream ss_additional_info;
338 ss_additional_info <<
"<nb_calibration_images>" << calibrator.size() <<
"</nb_calibration_images>";
339 ss_additional_info <<
"<calibration_pattern_type>";
341 switch (s.calibrationPattern) {
342 case Settings::CHESSBOARD:
343 ss_additional_info <<
"Chessboard";
346 case Settings::CIRCLES_GRID:
347 ss_additional_info <<
"Circles grid";
350 case Settings::UNDEFINED:
352 ss_additional_info <<
"Undefined";
355 ss_additional_info <<
"</calibration_pattern_type>";
356 ss_additional_info <<
"<board_size>" << s.boardSize.width <<
"x" << s.boardSize.height <<
"</board_size>";
357 ss_additional_info <<
"<square_size>" << s.squareSize <<
"</square_size>";
362 std::cout <<
"\nCalibration without distortion in progress on " << calibrator.size() <<
" images..." << std::endl;
365 std::cout << cam << std::endl;
368 for (
size_t i = 0; i < calibrator.size(); i++) {
369 double reproj_error = sqrt(calibrator[i].getResidual() / calibrator[i].get_npt());
371 const CalibInfo &calib = calib_info[i];
372 std::cout <<
"Image " << calib.m_frame_name <<
" reprojection error: " << reproj_error << std::endl;
376 std::ostringstream ss;
377 ss <<
"Reprojection error: " << reproj_error;
387 for (
size_t idx = 0; idx < calib.m_points.size(); idx++) {
390 vpPoint pt = calib.m_points[idx];
403 std::cout <<
"\nGlobal reprojection error: " << error << std::endl;
404 ss_additional_info <<
"<global_reprojection_error><without_distortion>" << error <<
"</without_distortion>";
410 std::cout <<
"Camera parameters without distortion successfully saved in \"" << opt_output_file_name <<
"\"" 413 std::cout <<
"Failed to save the camera parameters without distortion in \"" << opt_output_file_name <<
"\"" 415 std::cout <<
"A file with the same name exists. Remove it to be able " 416 "to save the parameters..." 420 std::cout <<
"Calibration without distortion failed." << std::endl;
424 std::vector<vpCalibration> calibrator_without_dist = calibrator;
426 std::cout <<
"\n\nCalibration with distortion in progress on " << calibrator.size() <<
" images..." << std::endl;
429 std::cout << cam << std::endl;
432 for (
size_t i = 0; i < calibrator.size(); i++) {
433 double reproj_error = sqrt(calibrator[i].getResidual_dist() / calibrator[i].get_npt());
435 const CalibInfo &calib = calib_info[i];
436 std::cout <<
"Image " << calib.m_frame_name <<
" reprojection error: " << reproj_error << std::endl;
440 std::ostringstream ss;
441 ss <<
"Reprojection error: " << reproj_error;
451 for (
size_t idx = 0; idx < calib.m_points.size(); idx++) {
454 vpPoint pt = calib.m_points[idx];
455 pt.
project(calibrator[i].cMo_dist);
467 std::cout <<
"\nGlobal reprojection error: " << error << std::endl;
468 ss_additional_info <<
"<with_distortion>" << error <<
"</with_distortion></global_reprojection_error>";
474 #elif defined VISP_HAVE_GDI 477 #elif defined VISP_HAVE_GTK 480 #elif defined VISP_HAVE_OPENCV 487 for (
size_t idx = 0; idx < calib_info.size(); idx++) {
488 std::cout <<
"\nThis tool computes the line fitting error (mean distance error) on image points extracted from " 489 "the raw distorted image." 492 I = calib_info[idx].m_img;
502 std::vector<vpImagePoint> grid_points = calib_info[idx].m_imPts;
503 for (
int i = 0; i < s.boardSize.height; i++) {
504 std::vector<vpImagePoint> current_line(grid_points.begin() + i * s.boardSize.width,
505 grid_points.begin() + (i + 1) * s.boardSize.width);
507 std::vector<vpImagePoint> current_line_undist = undistort(cam, current_line);
508 double a = 0, b = 0, c = 0;
511 std::cout << calib_info[idx].m_frame_name <<
" line " << i + 1
512 <<
" fitting error on distorted points: " << line_fitting_error
513 <<
" ; on undistorted points: " << line_fitting_error_undist << std::endl;
520 std::cout <<
"\nThis tool computes the line fitting error (mean distance error) on image points extracted from " 521 "the undistorted image" 522 <<
" (vpImageTools::undistort())." << std::endl;
524 std::vector<cv::Point2f> pointBuf;
527 bool found = extractCalibrationPoints(s, cvI, pointBuf);
529 std::vector<vpImagePoint> grid_points;
530 for (
unsigned int i = 0; i < pointBuf.size(); i++) {
532 grid_points.push_back(ip);
537 calib_info[idx].m_frame_name + std::string(
" undistorted"),
vpColor::red);
541 for (
int i = 0; i < s.boardSize.height; i++) {
542 std::vector<vpImagePoint> current_line(grid_points.begin() + i * s.boardSize.width,
543 grid_points.begin() + (i + 1) * s.boardSize.width);
545 double a = 0, b = 0, c = 0;
547 std::cout << calib_info[idx].m_frame_name <<
" undistorted image, line " << i + 1
548 <<
" fitting error: " << line_fitting_error << std::endl;
555 std::string msg(
"Unable to detect grid on undistorted image");
556 std::cout << msg << std::endl;
557 std::cout <<
"Check that the grid is not too close to the image edges" << std::endl;
560 calib_info[idx].m_frame_name + std::string(
" undistorted"),
vpColor::red);
572 std::cout << std::endl;
576 ss_additional_info <<
"<camera_poses>";
577 for (
size_t i = 0; i < calibrator.size(); i++) {
579 ss_additional_info <<
"<cMo>" << pose.t() <<
"</cMo>";
581 for (
size_t i = 0; i < calibrator.size(); i++) {
583 ss_additional_info <<
"<cMo_dist>" << pose.t() <<
"</cMo_dist>";
585 ss_additional_info <<
"</camera_poses>";
589 std::cout <<
"Camera parameters without distortion successfully saved in \"" << opt_output_file_name <<
"\"" 592 std::cout <<
"Failed to save the camera parameters without distortion in \"" << opt_output_file_name <<
"\"" 594 std::cout <<
"A file with the same name exists. Remove it to be able " 595 "to save the parameters..." 598 std::cout << std::endl;
599 std::cout <<
"Estimated pose using vpPoseVector format: [tx ty tz tux tuy tuz] with translation in meter and " 602 for (
unsigned int i = 0; i < calibrator.size(); i++)
603 std::cout <<
"Estimated pose on input data extracted from " << calib_info[i].m_frame_name <<
": " 606 std::cout <<
"Calibration with distortion failed." << std::endl;
612 std::cout <<
"Catch an exception: " << e << std::endl;
619 std::cout <<
"OpenCV 2.3.0 or higher is requested to run the calibration." << std::endl;
620 std::cout <<
"Tip:" << std::endl;
621 std::cout <<
"- Install OpenCV, configure again ViSP using cmake and build again this example" << std::endl;
VISP_EXPORT int wait(double t0, double t)
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static double lineFitting(const std::vector< vpImagePoint > &imPts, double &a, double &b, double &c)
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
double getResidual(void) const
get the residual in pixels
int computeCalibration(vpCalibrationMethodType method, vpHomogeneousMatrix &cMo_est, vpCameraParameters &cam_est, bool verbose=false)
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
Display for windows using GDI (available on any windows 32 platform).
void setAspectRatio(double aspect_ratio)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
error that can be emited by ViSP classes.
int addPoint(double X, double Y, double Z, vpImagePoint &ip)
Tools for perspective camera calibration.
XML parser to load and save intrinsic camera parameters.
static const vpColor green
static void flush(const vpImage< unsigned char > &I)
VISP_EXPORT std::string getDateTime(const std::string &format="%Y/%m/%d %H:%M:%S")
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
static void setLambda(const double &lambda)
set the gain for the virtual visual servoing algorithm
void open(vpImage< vpRGBa > &I)
static void display(const vpImage< unsigned char > &I)
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
Generic class defining intrinsic camera parameters.
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
void acquire(vpImage< vpRGBa > &I)
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)
void initPersProjWithoutDistortion(double px, double py, double u0, double v0)
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
long getFrameIndex() const
unsigned int getHeight() const
double get_x() const
Get the point x coordinate in the image plane.
unsigned int getDownScalingFactor()
Implementation of a pose vector and operations on poses.
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="")
double get_y() const
Get the point y coordinate in the image plane.
const std::string & getStringMessage() const
Send a reference (constant) related the error message (can be empty).
void setFileName(const std::string &filename)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
static int computeCalibrationMulti(vpCalibrationMethodType method, std::vector< vpCalibration > &table_cal, vpCameraParameters &cam, double &globalReprojectionError, bool verbose=false)
static void setTitle(const vpImage< unsigned char > &I, const std::string &windowtitle)
unsigned int getWidth() const
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)