Main MRPT website > C++ reference for MRPT 1.9.9
checkerboard_cam_calib.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "vision-precomp.h" // Precompiled headers
11 
12 #include <mrpt/system/filesystem.h>
14 
16 #include <mrpt/vision/pinhole.h>
18 
19 // Universal include for all versions of OpenCV
20 #include <mrpt/otherlibs/do_opencv_includes.h>
21 
22 using namespace mrpt;
23 using namespace mrpt::vision;
24 using namespace mrpt::utils;
25 using namespace mrpt::math;
26 using namespace mrpt::poses;
27 using namespace std;
28 
29 /* -------------------------------------------------------
30  checkerBoardCameraCalibration
31  ------------------------------------------------------- */
33  TCalibrationImageList& images, unsigned int check_size_x,
34  unsigned int check_size_y, double check_squares_length_X_meters,
35  double check_squares_length_Y_meters, CMatrixDouble33& intrinsicParams,
36  std::vector<double>& distortionParams, bool normalize_image,
37  double* out_MSE, bool skipDrawDetectedImgs,
38  bool useScaramuzzaAlternativeDetector)
39 {
40  // Just a wrapper for the newer version of the function which uses TCamera:
41  TCamera cam;
43  images, check_size_x, check_size_y, check_squares_length_X_meters,
44  check_squares_length_Y_meters, cam, normalize_image, out_MSE,
45  skipDrawDetectedImgs, useScaramuzzaAlternativeDetector);
46 
47  intrinsicParams = cam.intrinsicParams;
48  distortionParams = cam.getDistortionParamsAsVector();
49  return ret;
50 }
51 
52 #if MRPT_HAS_OPENCV
53 // JL says: This was copied here since it seems OpenCV 2.3 had a broken
54 // <opencv2/core/eigen.hpp> header.
55 // It should be removed in the future when 2.3 becomes too old to support.
56 namespace cv
57 {
58 template <typename _Tp, int _rows, int _cols, int _options, int _maxRows,
59  int _maxCols>
61  const Mat& src,
62  Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols>& dst)
63 {
64  CV_DbgAssert(src.rows == _rows && src.cols == _cols);
65  if (!(dst.Flags & Eigen::RowMajorBit))
66  {
67  Mat _dst(
68  src.cols, src.rows, DataType<_Tp>::type, dst.data(),
69  (size_t)(dst.stride() * sizeof(_Tp)));
70  if (src.type() == _dst.type())
71  transpose(src, _dst);
72  else if (src.cols == src.rows)
73  {
74  src.convertTo(_dst, _dst.type());
75  transpose(_dst, _dst);
76  }
77  else
78  Mat(src.t()).convertTo(_dst, _dst.type());
79  CV_DbgAssert(_dst.data == (uchar*)dst.data());
80  }
81  else
82  {
83  Mat _dst(
84  src.rows, src.cols, DataType<_Tp>::type, dst.data(),
85  (size_t)(dst.stride() * sizeof(_Tp)));
86  src.convertTo(_dst, _dst.type());
87  CV_DbgAssert(_dst.data == (uchar*)dst.data());
88  }
89 }
90 }
91 #endif
92 
93 /* -------------------------------------------------------
94  checkerBoardCameraCalibration
95  ------------------------------------------------------- */
97  TCalibrationImageList& images, unsigned int check_size_x,
98  unsigned int check_size_y, double check_squares_length_X_meters,
99  double check_squares_length_Y_meters,
100  mrpt::utils::TCamera& out_camera_params, bool normalize_image,
101  double* out_MSE, bool skipDrawDetectedImgs,
102  bool useScaramuzzaAlternativeDetector)
103 {
104  MRPT_UNUSED_PARAM(skipDrawDetectedImgs);
105 #if MRPT_HAS_OPENCV
106  try
107  {
108  ASSERT_(check_size_x > 2)
109  ASSERT_(check_size_y > 2)
110  ASSERT_(check_squares_length_X_meters > 0)
111  ASSERT_(check_squares_length_Y_meters > 0)
112 
113  if (images.size() < 1)
114  {
115  std::cout << "ERROR: No input images." << std::endl;
116  return false;
117  }
118 
119  const unsigned CORNERS_COUNT = check_size_x * check_size_y;
120  const CvSize check_size = cvSize(check_size_x, check_size_y);
121 
122  // Fill the pattern of expected pattern points only once out of the
123  // loop:
124  vector<cv::Point3f> pattern_obj_points(CORNERS_COUNT);
125  {
126  unsigned int y, k;
127  for (y = 0, k = 0; y < check_size_y; y++)
128  {
129  for (unsigned int x = 0; x < check_size_x; x++, k++)
130  {
131  pattern_obj_points[k].x = -check_squares_length_X_meters *
132  x; // The "-" is for convenience,
133  // so the camera poses appear
134  // with Z>0
135  pattern_obj_points[k].y = check_squares_length_Y_meters * y;
136  pattern_obj_points[k].z = 0;
137  }
138  }
139  }
140 
141  // First: Assure all images are loaded:
142  // -------------------------------------------
144  for (it = images.begin(); it != images.end(); ++it)
145  {
146  TImageCalibData& dat = it->second;
147 
148  dat.projectedPoints_distorted.clear(); // Clear reprojected points.
149  dat.projectedPoints_undistorted.clear();
150 
151  // Skip if images are marked as "externalStorage":
152  if (!dat.img_original.isExternallyStored() &&
153  !mrpt::system::extractFileExtension(it->first).empty())
154  {
155  if (!dat.img_original.loadFromFile(it->first))
157  "Error reading image: %s", it->first.c_str());
158 
159  dat.img_checkboard = dat.img_original;
160  dat.img_rectified = dat.img_original;
161  }
162  }
163 
164  // For each image, find checkerboard corners:
165  // -----------------------------------------------
166  vector<vector<cv::Point3f>>
167  objectPoints; // final container for detected stuff
168  vector<vector<cv::Point2f>>
169  imagePoints; // final container for detected stuff
170 
171  unsigned int valid_detected_imgs = 0;
172  vector<string> pointsIdx2imageFile;
173  cv::Size imgSize(0, 0);
174 
175  unsigned int i;
176  for (i = 0, it = images.begin(); it != images.end(); it++, i++)
177  {
178  TImageCalibData& dat = it->second;
179 
180  // Make grayscale version:
181  const CImage img_gray(
183 
184  if (!i)
185  {
186  imgSize = cv::Size(img_gray.getWidth(), img_gray.getHeight());
187  out_camera_params.ncols = imgSize.width;
188  out_camera_params.nrows = imgSize.height;
189  }
190  else
191  {
192  if (imgSize.height != (int)img_gray.getHeight() ||
193  imgSize.width != (int)img_gray.getWidth())
194  {
195  std::cout << "ERROR: All the images must have the same size"
196  << std::endl;
197  return false;
198  }
199  }
200 
201  // Try with expanded versions of the image if it fails to detect the
202  // checkerboard:
203  unsigned corners_count;
204  bool corners_found = false;
205 
206  corners_count = CORNERS_COUNT;
207 
208  vector<cv::Point2f> this_img_pts(
209  CORNERS_COUNT); // Temporary buffer for points, to be added if
210  // the points pass the checks.
211 
212  dat.detected_corners.clear();
213 
214  // Do detection (this includes the "refine corners" with
215  // cvFindCornerSubPix):
216  vector<TPixelCoordf> detectedCoords;
217  corners_found = mrpt::vision::findChessboardCorners(
218  img_gray, detectedCoords, check_size_x, check_size_y,
219  normalize_image, // normalize_image
220  useScaramuzzaAlternativeDetector);
221 
222  corners_count = detectedCoords.size();
223 
224  // Copy the data into the overall array of coords:
225  ASSERT_(detectedCoords.size() <= CORNERS_COUNT);
226  for (size_t p = 0; p < detectedCoords.size(); p++)
227  {
228  this_img_pts[p].x = detectedCoords[p].x;
229  this_img_pts[p].y = detectedCoords[p].y;
230  }
231 
232  if (corners_found && corners_count != CORNERS_COUNT)
233  corners_found = false;
234 
235  cout << format(
236  "Img %s: %s\n",
237  mrpt::system::extractFileName(it->first).c_str(),
238  corners_found ? "DETECTED" : "NOT DETECTED");
239 
240  if (corners_found)
241  {
242  // save the corners in the data structure:
243  int x, y;
244  unsigned int k;
245  for (y = 0, k = 0; y < check_size.height; y++)
246  for (x = 0; x < check_size.width; x++, k++)
247  dat.detected_corners.push_back(
249  this_img_pts[k].x, this_img_pts[k].y));
250 
251  // Draw the checkerboard in the corresponding image:
252  // ----------------------------------------------------
253  if (!dat.img_original.isExternallyStored())
254  {
255  const int r = 4;
256  CvPoint prev_pt = cvPoint(0, 0);
257  const int line_max = 8;
258  CvScalar line_colors[8];
259 
260  line_colors[0] = CV_RGB(255, 0, 0);
261  line_colors[1] = CV_RGB(255, 128, 0);
262  line_colors[2] = CV_RGB(255, 128, 0);
263  line_colors[3] = CV_RGB(200, 200, 0);
264  line_colors[4] = CV_RGB(0, 255, 0);
265  line_colors[5] = CV_RGB(0, 200, 200);
266  line_colors[6] = CV_RGB(0, 0, 255);
267  line_colors[7] = CV_RGB(255, 0, 255);
268 
269  // Checkboad as color image:
271 
272  void* rgb_img = dat.img_checkboard.getAs<IplImage>();
273 
274  for (y = 0, k = 0; y < check_size.height; y++)
275  {
276  CvScalar color = line_colors[y % line_max];
277  for (x = 0; x < check_size.width; x++, k++)
278  {
279  CvPoint pt;
280  pt.x = cvRound(this_img_pts[k].x);
281  pt.y = cvRound(this_img_pts[k].y);
282 
283  if (k != 0) cvLine(rgb_img, prev_pt, pt, color);
284 
285  cvLine(
286  rgb_img, cvPoint(pt.x - r, pt.y - r),
287  cvPoint(pt.x + r, pt.y + r), color);
288  cvLine(
289  rgb_img, cvPoint(pt.x - r, pt.y + r),
290  cvPoint(pt.x + r, pt.y - r), color);
291  cvCircle(rgb_img, pt, r + 1, color);
292  prev_pt = pt;
293  }
294  }
295  }
296 
297  // Accept this image as good:
298  pointsIdx2imageFile.push_back(it->first);
299  imagePoints.push_back(this_img_pts);
300  objectPoints.push_back(pattern_obj_points);
301 
302  valid_detected_imgs++;
303  }
304 
305  } // end find corners
306 
307  std::cout << valid_detected_imgs << " valid images." << std::endl;
308  if (!valid_detected_imgs)
309  {
310  std::cout << "ERROR: No valid images. Perhaps the checkerboard "
311  "size is incorrect?"
312  << std::endl;
313  return false;
314  }
315 
316  // ---------------------------------------------
317  // Calculate the camera parameters
318  // ---------------------------------------------
319  // Calibrate camera
320  cv::Mat cameraMatrix, distCoeffs(1, 5, CV_64F, cv::Scalar::all(0));
321  vector<cv::Mat> rvecs, tvecs;
322 
323  const double cv_calib_err = cv::calibrateCamera(
324  objectPoints, imagePoints, imgSize, cameraMatrix, distCoeffs, rvecs,
325  tvecs, 0 /*flags*/);
326 
327  // Load matrix:
328  out_camera_params.intrinsicParams =
329  CMatrixDouble33(cameraMatrix.ptr<double>());
330 
331  out_camera_params.dist.assign(0);
332  for (int i = 0; i < 5; i++)
333  out_camera_params.dist[i] = distCoeffs.ptr<double>()[i];
334 
335  // Load camera poses:
336  for (i = 0; i < valid_detected_imgs; i++)
337  {
338  CMatrixDouble44 HM;
339  HM.zeros();
340  HM(3, 3) = 1;
341 
342  {
343  // Convert rotation vectors -> rot matrices:
344  cv::Mat cv_rot;
345  cv::Rodrigues(rvecs[i], cv_rot);
346 
347  Eigen::Matrix3d rot;
348  cv::my_cv2eigen(cv_rot, rot);
349  HM.block<3, 3>(0, 0) = rot;
350  }
351 
352  {
353  Eigen::Matrix<double, 3, 1> trans;
354  cv::my_cv2eigen(tvecs[i], trans);
355  HM.block<3, 1>(0, 3) = trans;
356  }
357 
358  CPose3D p = CPose3D(0, 0, 0) - CPose3D(HM);
359 
360  images[pointsIdx2imageFile[i]].reconstructed_camera_pose = p;
361 
362  std::cout << "Img: "
363  << mrpt::system::extractFileName(pointsIdx2imageFile[i])
364  << ": " << p << std::endl;
365  }
366 
367  {
368  CConfigFileMemory cfg;
369  out_camera_params.saveToConfigFile("CAMERA_PARAMS", cfg);
370  std::cout << cfg.getContent() << std::endl;
371  }
372 
373  // ----------------------------------------
374  // Undistort images:
375  // ----------------------------------------
376  for (it = images.begin(); it != images.end(); ++it)
377  {
378  TImageCalibData& dat = it->second;
379  if (!dat.img_original.isExternallyStored())
381  dat.img_rectified, out_camera_params);
382  } // end undistort
383 
384  // -----------------------------------------------
385  // Reproject points to measure the fit sqr error
386  // -----------------------------------------------
387  double sqrErr = 0;
388 
389  for (i = 0; i < valid_detected_imgs; i++)
390  {
391  TImageCalibData& dat = images[pointsIdx2imageFile[i]];
392  if (dat.detected_corners.size() != CORNERS_COUNT) continue;
393 
394  // Reproject all the points into pixel coordinates:
395  // -----------------------------------------------------
396  vector<TPoint3D> lstPatternPoints(
397  CORNERS_COUNT); // Points as seen from the camera:
398  for (unsigned int p = 0; p < CORNERS_COUNT; p++)
399  lstPatternPoints[p] = TPoint3D(
400  pattern_obj_points[p].x, pattern_obj_points[p].y,
401  pattern_obj_points[p].z);
402 
403  vector<TPixelCoordf>& projectedPoints =
405  vector<TPixelCoordf>& projectedPoints_distorted =
407 
409  lstPatternPoints, // Input points
411  out_camera_params.intrinsicParams, // calib matrix
412  projectedPoints // Output points in pixels
413  );
414 
416  lstPatternPoints, // Input points
418  out_camera_params.intrinsicParams, // calib matrix
419  out_camera_params.getDistortionParamsAsVector(),
420  projectedPoints_distorted // Output points in pixels
421  );
422 
423  ASSERT_(projectedPoints.size() == CORNERS_COUNT);
424  ASSERT_(projectedPoints_distorted.size() == CORNERS_COUNT);
425 
426  for (unsigned int p = 0; p < CORNERS_COUNT; p++)
427  {
428  const double px = projectedPoints[p].x;
429  const double py = projectedPoints[p].y;
430 
431  const double px_d = projectedPoints_distorted[p].x;
432  const double py_d = projectedPoints_distorted[p].y;
433 
434  // Only draw if the img is NOT external:
435  if (!dat.img_original.isExternallyStored())
436  {
437  if (px >= 0 && px < imgSize.width && py >= 0 &&
438  py < imgSize.height)
439  cvCircle(
440  dat.img_rectified.getAs<IplImage>(),
441  cvPoint(px, py), 4, CV_RGB(0, 0, 255));
442  }
443 
444  // Accumulate error:
445  sqrErr +=
446  square(px_d - dat.detected_corners[p].x) +
447  square(py_d - dat.detected_corners[p].y); // Error relative
448  // to the
449  // original
450  // (distorted)
451  // image.
452  }
453  }
454 
455  if (valid_detected_imgs)
456  {
457  sqrErr /= CORNERS_COUNT * valid_detected_imgs;
458  std::cout << "Average err. of reprojection: " << sqrt(sqrErr)
459  << " pixels (OpenCV error=" << cv_calib_err << ")\n";
460  }
461  if (out_MSE) *out_MSE = sqrt(sqrErr);
462 
463  return true;
464  }
465  catch (std::exception& e)
466  {
467  std::cout << e.what() << std::endl;
468  return false;
469  }
470 #else
471  THROW_EXCEPTION("Function not available: MRPT was compiled without OpenCV")
472 #endif
473 }
A pair (x,y) of pixel coordinates (subpixel resolution).
Definition: TPixelCoord.h:21
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
GLdouble GLdouble z
Definition: glext.h:3872
mrpt::utils::CImage img_original
This image will be automatically loaded from the file name passed to checkerBoardCameraCalibration.
A class for storing images as grayscale or RGB bitmaps.
Definition: CImage.h:118
bool findChessboardCorners(const mrpt::utils::CImage &img, std::vector< mrpt::utils::TPixelCoordf > &cornerCoords, unsigned int check_size_x, unsigned int check_size_y, bool normalize_image=true, bool useScaramuzzaMethod=false)
Look for the corners of a chessboard in the image using one of two different methods.
#define THROW_EXCEPTION(msg)
void rectifyImage(CImage &out_img, const mrpt::utils::TCamera &cameraParams) const
Rectify (un-distort) the image according to some camera parameters, and returns an output un-distorte...
Definition: CImage.cpp:2095
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Scalar * iterator
Definition: eigen_plugins.h:26
STL namespace.
void projectPoints_with_distortion(const std::vector< mrpt::math::TPoint3D > &in_points_3D, const mrpt::poses::CPose3D &cameraPose, const mrpt::math::CMatrixDouble33 &intrinsicParams, const std::vector< double > &distortionParams, std::vector< mrpt::utils::TPixelCoordf > &projectedPoints, bool accept_points_behind=false)
Project a set of 3D points into a camera at an arbitrary 6D pose using its calibration matrix and dis...
Definition: pinhole.cpp:50
std::vector< mrpt::utils::TPixelCoordf > detected_corners
At output, the detected corners (x,y) in pixel units.
CMatrixFixedNumeric< double, 3, 3 > CMatrixDouble33
Definition: eigen_frwds.h:55
bool loadFromFile(const std::string &fileName, int isColor=-1)
Load image from a file, whose format is determined from the extension (internally uses OpenCV)...
Definition: CImage.cpp:276
GLuint src
Definition: glext.h:7278
const T * getAs() const
Returns a pointer to a const T* containing the image - the idea is to call like "img.getAs<IplImage>()" so we can avoid here including OpenCV&#39;s headers.
Definition: CImage.h:587
T square(const T x)
Inline function for the square of a number.
Definition: bits.h:55
std::vector< mrpt::utils::TPixelCoordf > projectedPoints_undistorted
At output, like projectedPoints_distorted but for the undistorted image.
GLuint color
Definition: glext.h:8300
This class implements a config file-like interface over a memory-stored string list.
void getContent(std::string &str) const
Return the current contents of the virtual "config file".
Data associated to each image in the calibration process mrpt::vision::checkerBoardCameraCalibration ...
This base provides a set of functions for maths stuff.
Definition: CArrayNumeric.h:19
size_t getWidth() const override
Returns the width of the image in pixels.
Definition: CImage.cpp:869
GLuint dst
Definition: glext.h:7135
mrpt::utils::CImage img_checkboard
At output, this will contain the detected checkerboard overprinted to the image.
#define MRPT_UNUSED_PARAM(a)
Can be used to avoid "not used parameters" warnings from the compiler.
bool checkerBoardCameraCalibration(TCalibrationImageList &images, unsigned int check_size_x, unsigned int check_size_y, double check_squares_length_X_meters, double check_squares_length_Y_meters, mrpt::utils::TCamera &out_camera_params, bool normalize_image=true, double *out_MSE=nullptr, bool skipDrawDetectedImgs=false, bool useScaramuzzaAlternativeDetector=false)
Performs a camera calibration (computation of projection and distortion parameters) from a sequence o...
std::vector< mrpt::utils::TPixelCoordf > projectedPoints_distorted
At output, only will have an empty vector if the checkerboard was not found in this image...
size_t getHeight() const override
Returns the height of the image in pixels.
Definition: CImage.cpp:897
uint32_t ncols
Camera resolution.
Definition: TCamera.h:54
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:19
Classes for computer vision, detectors, features, etc.
void projectPoints_no_distortion(const std::vector< mrpt::math::TPoint3D > &in_points_3D, const mrpt::poses::CPose3D &cameraPose, const mrpt::math::CMatrixDouble33 &intrinsicParams, std::vector< mrpt::utils::TPixelCoordf > &projectedPoints, bool accept_points_behind=false)
Project a set of 3D points into a camera at an arbitrary 6D pose using its calibration matrix (undist...
Definition: pinhole.cpp:30
std::string extractFileExtension(const std::string &filePath, bool ignore_gz=false)
Extract the extension of a filename.
Definition: filesystem.cpp:97
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
Definition: CPoint.h:17
mrpt::math::CMatrixDouble33 intrinsicParams
Matrix of intrinsic parameters (containing the focal length and principal point coordinates) ...
Definition: TCamera.h:57
std::vector< double > getDistortionParamsAsVector() const
Get a vector with the distortion params of the camera.
Definition: TCamera.h:135
GLsizei GLboolean transpose
Definition: glext.h:4133
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
GLdouble GLdouble GLdouble r
Definition: glext.h:3705
mrpt::math::CArrayDouble< 5 > dist
[k1 k2 t1 t2 k3] -> k_i: parameters of radial distortion, t_i: parameters of tangential distortion (d...
Definition: TCamera.h:60
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:88
uint32_t nrows
Definition: TCamera.h:54
#define ASSERT_(f)
void my_cv2eigen(const Mat &src, Eigen::Matrix< _Tp, _rows, _cols, _options, _maxRows, _maxCols > &dst)
GLenum GLint GLint y
Definition: glext.h:3538
mrpt::utils::CImage img_rectified
At output, this will be the rectified image.
void colorImage(CImage &ret) const
Returns a RGB version of the grayscale image, or itself if it is already a RGB image.
Definition: CImage.cpp:2405
GLenum GLint x
Definition: glext.h:3538
bool isExternallyStored() const noexcept
See setExternalStorage().
Definition: CImage.h:780
Lightweight 3D point.
std::string extractFileName(const std::string &filePath)
Extract just the name (without extension) of a filename from a complete path plus name plus extension...
Definition: filesystem.cpp:61
std::map< std::string, TImageCalibData > TCalibrationImageList
A list of images, used in checkerBoardCameraCalibration.
mrpt::poses::CPose3D reconstructed_camera_pose
At output, the reconstructed pose of the camera.
GLfloat GLfloat p
Definition: glext.h:6305
GLuint GLuint GLsizei GLenum type
Definition: glext.h:3528
Structure to hold the parameters of a pinhole camera model.
Definition: TCamera.h:32
void saveToConfigFile(const std::string &section, mrpt::utils::CConfigFileBase &cfg) const
Save as a config block:
Definition: TCamera.cpp:117



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019