ViSP  2.9.0
camera_calibration.cpp
1 /****************************************************************************
2  *
3  * $Id: camera_calibration.cpp 4663 2014-02-14 10:32:11Z fspindle $
4  *
5  * This file is part of the ViSP software.
6  * Copyright (C) 2005 - 2014 by INRIA. All rights reserved.
7  *
8  * This software is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * ("GPL") version 2 as published by the Free Software Foundation.
11  * See the file LICENSE.txt at the root directory of this source
12  * distribution for additional information about the GNU GPL.
13  *
14  * For using ViSP with software that can not be combined with the GNU
15  * GPL, please contact INRIA about acquiring a ViSP Professional
16  * Edition License.
17  *
18  * See http://www.irisa.fr/lagadic/visp/visp.html for more information.
19  *
20  * This software was developed at:
21  * INRIA Rennes - Bretagne Atlantique
22  * Campus Universitaire de Beaulieu
23  * 35042 Rennes Cedex
24  * France
25  * http://www.irisa.fr/lagadic
26  *
27  * If you have questions regarding the use of this file, please contact
28  * INRIA at visp@inria.fr
29  *
30  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
31  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  *
33  *
34  * Description:
35  * Camera calibration with chessboard or circle calibration grid.
36  *
37  * Authors:
38  * Fabien Spindler
39  *
40  *****************************************************************************/
41 #include <iostream>
42 
43 #include <visp/vpConfig.h>
44 
45 #if VISP_HAVE_OPENCV_VERSION >= 0x020300
46 
47 #include <opencv2/core/core.hpp>
48 #include <opencv2/imgproc/imgproc.hpp>
49 #include <opencv2/calib3d/calib3d.hpp>
50 #include <opencv2/highgui/highgui.hpp>
51 
52 #include <visp/vpCalibration.h>
53 
54 #include <visp/vpDisplayX.h>
55 #include <visp/vpDisplayGDI.h>
56 #include <visp/vpDisplayOpenCV.h>
57 #include <visp/vpDisplayD3D.h>
58 #include <visp/vpDisplayGTK.h>
59 #include <visp/vpIoTools.h>
60 #include <visp/vpPoint.h>
61 #include <visp/vpVideoReader.h>
62 #include <visp/vpXmlParserCamera.h>
63 
64 #ifndef DOXYGEN_SHOULD_SKIP_THIS
65 
66 class Settings
67 {
68 public:
69  Settings() {
70  boardSize = cv::Size(0, 0);
71  calibrationPattern = UNDEFINED;
72  squareSize = 0.025f;
73  goodInput = false;
74  tempo = 1.f;
75 
76  }
77  enum Pattern { UNDEFINED, CHESSBOARD, CIRCLES_GRID};
78 
79  bool read(const std::string &filename) // Read the parameters
80  {
81  // reading configuration file
82  if (! vpIoTools::loadConfigFile(filename) )
83  return false;
84  vpIoTools::readConfigVar("BoardSize_Width:", boardSize.width);
85  vpIoTools::readConfigVar("BoardSize_Height:", boardSize.height);
86  vpIoTools::readConfigVar("Square_Size:", squareSize);
87  vpIoTools::readConfigVar("Calibrate_Pattern:", patternToUse);
88  vpIoTools::readConfigVar("Input:", input);
89  vpIoTools::readConfigVar("Tempo:", tempo);
90 
91  std::cout << "grid width : " << boardSize.width << std::endl;
92  std::cout << "grid height: " << boardSize.height << std::endl;
93  std::cout << "square size: " << squareSize << std::endl;
94  std::cout << "pattern : " << patternToUse << std::endl;
95  std::cout << "input seq : " << input << std::endl;
96  std::cout << "tempo : " << tempo << std::endl;
97  interprate();
98  return true;
99  }
100  void interprate()
101  {
102  goodInput = true;
103  if (boardSize.width <= 0 || boardSize.height <= 0) {
104  std::cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << std::endl;
105  goodInput = false;
106  }
107  if (squareSize <= 10e-6) {
108  std::cerr << "Invalid square size " << squareSize << std::endl;
109  goodInput = false;
110  }
111 
112  if (input.empty()) // Check for valid input
113  goodInput = false;
114 
115  calibrationPattern = UNDEFINED;
116  if (patternToUse.compare("CHESSBOARD") == 0) calibrationPattern = CHESSBOARD;
117  else if (patternToUse.compare("CIRCLES_GRID") == 0) calibrationPattern = CIRCLES_GRID;
118  if (calibrationPattern == UNDEFINED) {
119  std::cerr << " Inexistent camera calibration mode: " << patternToUse << std::endl;
120  goodInput = false;
121  }
122  }
123 
124 public:
125  cv::Size boardSize; // The size of the board -> Number of items by width and height
126  Pattern calibrationPattern;// One of the Chessboard, circles, or asymmetric circle pattern
127  float squareSize; // The size of a square in your defined unit (point, millimeter,etc).
128  std::string input; // The input image sequence
129  float tempo; // Tempo in seconds between two images. If > 10 wait a click to continue
130  bool goodInput;
131 
132 private:
133  std::string patternToUse;
134 };
135 
136 #endif
137 int main(int argc, const char ** argv)
138 {
139  try {
140  std::string outputFileName = "camera.xml";
141 
142  Settings s;
143  const std::string inputSettingsFile = argc > 1 ? argv[1] : "default.cfg";
144  if (! s.read(inputSettingsFile) ) {
145  std::cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << std::endl;
146  std::cout << std::endl << "Usage: " << argv[0] << " <configuration file>.cfg" << std::endl;
147  return -1;
148  }
149 
150  if (! s.goodInput)
151  {
152  std::cout << "Invalid input detected. Application stopping. " << std::endl;
153  return -1;
154  }
155 
156  // Start the calibration code
158  vpVideoReader reader;
159  reader.setFileName(s.input);
160  reader.open(I);
161 
162 #ifdef VISP_HAVE_X11
163  vpDisplayX d(I);
164 #elif defined VISP_HAVE_GDI
165  vpDisplayGDI d(I);
166 #elif defined VISP_HAVE_GTK
167  vpDisplayGTK d(I);
168 #elif defined VISP_HAVE_OPENCV
169  vpDisplayOpenCV d(I);
170 #endif
171 
172  std::vector<vpPoint> model;
173  std::vector<vpCalibration> calibrator;
174 
175  for (int i=0; i< s.boardSize.height; i++) {
176  for (int j=0; j< s.boardSize.width; j++) {
177  vpPoint P;
178  P.setWorldCoordinates(j*s.squareSize, i*s.squareSize, 0);
179  model.push_back(P);
180  }
181  }
182 
183  long frame_index;
184  while(! reader.end()) {
185  frame_index = reader.getFrameIndex();
186  reader.acquire(I);
188 
189  cv::Mat cvI;
190  std::vector<cv::Point2f> pointBuf;
191  vpImageConvert::convert(I, cvI);
192 
193  bool found = false;
194  switch( s.calibrationPattern ) // Find feature points on the input format
195  {
196  case Settings::CHESSBOARD:
197  //std::cout << "Use chessboard " << std::endl;
198  found = findChessboardCorners( cvI, s.boardSize, pointBuf,
199  CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE);
200  break;
201  case Settings::CIRCLES_GRID:
202  //std::cout << "Use circle grid " << std::endl;
203  found = findCirclesGrid( cvI, s.boardSize, pointBuf, cv::CALIB_CB_SYMMETRIC_GRID );
204  break;
205  case Settings::UNDEFINED:
206  default:
207  std::cout << "Unkown calibration grid " << std::endl;
208  break;
209  }
210 
211  std::cout << "frame: " << frame_index << ", status: " << found;
212  if (!found)
213  std::cout << ", image rejected" << std::endl;
214  else
215  std::cout << ", image used as input data" << std::endl;
216 
217  if ( found) // If done with success,
218  {
219  std::vector<vpImagePoint> data;
220 
221  if (s.calibrationPattern == Settings::CHESSBOARD) {
222  // improve the found corners' coordinate accuracy for chessboard
223  cornerSubPix( cvI, pointBuf, cv::Size(11,11),
224  cv::Size(-1,-1), cv::TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1 ));
225  }
226  char title[20]; sprintf(title, "image %ld", frame_index);
227  vpDisplay::setTitle(I, title);
228  for (unsigned int i=0; i < pointBuf.size(); i++) {
229  vpImagePoint ip(pointBuf[i].y, pointBuf[i].x);
230  data.push_back(ip);
232  }
233 
234  // Calibration on a single mono image
235  vpCalibration calib;
236  calib.setLambda(0.5);
237  calib.clearPoint();
238  for (unsigned int i=0; i<model.size(); i++) {
239  calib.addPoint(model[i].get_oX(), model[i].get_oY(), model[i].get_oZ(), data[i]);
240  }
242  vpCameraParameters cam;
243 
244  // Set (u0,v0) in the middle of the image
245  double px = cam.get_px();
246  double py = cam.get_px();
247  double u0 = I.getWidth()/2;
248  double v0 = I.getHeight()/2;
249  cam.initPersProjWithoutDistortion(px, py, u0, v0);
250 
251  if (calib.computeCalibration(vpCalibration::CALIB_VIRTUAL_VS, cMo, cam, false) == 0) {
252  //std::cout << "camera parameters: " << cam << std::endl;
253  calibrator.push_back(calib);
254  }
255  }
256 
257  if (found)
258  vpDisplay::displayCharString(I, 15, 15, "Image processing succeed", vpColor::green);
259  else
260  vpDisplay::displayCharString(I, 15, 15, "Image processing fails", vpColor::green);
261 
262  if (s.tempo > 10.f) {
263  vpDisplay::displayCharString(I, 35, 15, "A click to process the next image", vpColor::green);
264  vpDisplay::flush(I);
266  }
267  else {
268  vpDisplay::flush(I);
269  vpTime::wait(s.tempo*1000);
270  }
271  }
272 
273  // Now we consider the multi image calibration
274  // Calibrate by a non linear method based on virtual visual servoing
275  if (calibrator.empty()) {
276  std::cerr << "Unable to calibrate. Image processing failed !" << std::endl;
277  return 0;
278  }
279 
280  std::cout << "\nCalibration without distorsion in progress on " << calibrator.size() << " images..." << std::endl;
281  vpCameraParameters cam;
282  double error;
283  if (vpCalibration::computeCalibrationMulti(vpCalibration::CALIB_VIRTUAL_VS, calibrator, cam, error, false) == 0) {
284  std::cout << cam << std::endl;
285  std::cout << "Global reprojection error: " << error << std::endl;
286 #ifdef VISP_HAVE_XML2
287  vpXmlParserCamera xml;
288 
289  if(xml.save(cam, outputFileName.c_str(), "Camera", I.getWidth(), I.getHeight()) == vpXmlParserCamera::SEQUENCE_OK)
290  std::cout << "Camera parameters without distortion successfully saved in \"" << outputFileName << "\"" << std::endl;
291  else {
292  std::cout << "Failed to save the camera parameters without distortion in \"" << outputFileName << "\"" << std::endl;
293  std::cout << "A file with the same name exists. Remove it to be able to save the parameters..." << std::endl;
294  }
295 #endif
296  }
297  else
298  std::cout << "Calibration without distortion failed." << std::endl;
299 
300  std::cout << "\nCalibration with distorsion in progress on " << calibrator.size() << " images..." << std::endl;
301  if (vpCalibration::computeCalibrationMulti(vpCalibration::CALIB_VIRTUAL_VS_DIST, calibrator, cam, error, false) == 0) {
302  std::cout << cam << std::endl;
303  std::cout << "Global reprojection error: " << error << std::endl;
304 
305 #ifdef VISP_HAVE_XML2
306  vpXmlParserCamera xml;
307 
308  if(xml.save(cam, outputFileName.c_str(), "Camera", I.getWidth(), I.getHeight()) == vpXmlParserCamera::SEQUENCE_OK)
309  std::cout << "Camera parameters without distortion successfully saved in \"" << outputFileName << "\"" << std::endl;
310  else {
311  std::cout << "Failed to save the camera parameters without distortion in \"" << outputFileName << "\"" << std::endl;
312  std::cout << "A file with the same name exists. Remove it to be able to save the parameters..." << std::endl;
313  }
314 #endif
315  std::cout << std::endl;
316  for (unsigned int i=0; i<calibrator.size(); i++)
317  std::cout << "Estimated pose on input data " << i << ": " << vpPoseVector(calibrator[i].cMo_dist).t() << std::endl;
318 
319  }
320  else
321  std::cout << "Calibration with distortion failed." << std::endl;
322 
323  return 0;
324  }
325  catch(vpException e) {
326  std::cout << "Catch an exception: " << e << std::endl;
327  return 1;
328  }
329 }
330 #else
331 int main()
332 {
333  std::cout << "OpenCV 2.3.0 or higher is requested to run the calibration." << std::endl;
334 }
335 #endif
long getFrameIndex() const
unsigned int getWidth() const
Definition: vpImage.h:159
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
int computeCalibration(vpCalibrationMethodType method, vpHomogeneousMatrix &cMo_est, vpCameraParameters &cam_est, bool verbose=false)
The class provides a data structure for the homogeneous matrices as well as a set of operations on th...
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:132
Define the X11 console to display images.
Definition: vpDisplayX.h:152
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.
Definition: vpException.h:76
int addPoint(double X, double Y, double Z, vpImagePoint &ip)
int save(const vpCameraParameters &cam, const char *filename, const std::string &camera_name, const unsigned int image_width=0, const unsigned int image_height=0)
Tools for perspective camera calibration.
Definition: vpCalibration.h:78
static int wait(double t0, double t)
Definition: vpTime.cpp:149
XML parser to load and save intrinsic camera parameters.
static const vpColor green
Definition: vpColor.h:170
static void flush(const vpImage< unsigned char > &I)
Definition: vpDisplay.cpp:1994
static const vpColor red
Definition: vpColor.h:167
Class that defines what is a point.
Definition: vpPoint.h:65
static void setLambda(const double &lambda)
set the gain for the virtual visual servoing algorithm
void initPersProjWithoutDistortion(const double px, const double py, const double u0, const double v0)
void open(vpImage< vpRGBa > &I)
int clearPoint()
Suppress all the point in the array of point.
static void display(const vpImage< unsigned char > &I)
Definition: vpDisplay.cpp:206
The vpDisplayOpenCV allows to display image using the opencv library.
vpRowVector t() const
transpose of Vector
virtual void displayCross(const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)=0
Generic class defining intrinsic camera parameters.
virtual void setTitle(const char *title)=0
The vpDisplayGTK allows to display image using the GTK+ library version 1.2.
Definition: vpDisplayGTK.h:145
void acquire(vpImage< vpRGBa > &I)
void setFileName(const char *filename)
double get_px() const
virtual void displayCharString(const vpImagePoint &ip, const char *text, const vpColor &color=vpColor::green)=0
The pose is a complete representation of every rigid motion in the euclidian space.
Definition: vpPoseVector.h:92
unsigned int getHeight() const
Definition: vpImage.h:150
virtual bool getClick(bool blocking=true)=0
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:92
static bool readConfigVar(const std::string &var, float &value)
Definition: vpIoTools.cpp:811
static int computeCalibrationMulti(vpCalibrationMethodType method, std::vector< vpCalibration > &table_cal, vpCameraParameters &cam, double &globalReprojectionError, bool verbose=false)
static bool loadConfigFile(const std::string &confFile)
Definition: vpIoTools.cpp:760
void setWorldCoordinates(const double ox, const double oy, const double oz)
Set the point world coordinates. We mean here the coordinates of the point in the object frame...
Definition: vpPoint.cpp:74