3calibration.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * 3calibration.cpp -- Calibrate 3 cameras in a horizontal line together.
  3. */
  4. #include "opencv2/calib3d.hpp"
  5. #include "opencv2/imgproc.hpp"
  6. #include "opencv2/imgcodecs.hpp"
  7. #include "opencv2/highgui.hpp"
  8. #include "opencv2/core/utility.hpp"
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <time.h>
  12. using namespace cv;
  13. using namespace std;
  14. enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
  15. static void help(char** argv)
  16. {
  17. printf( "\nThis is a camera calibration sample that calibrates 3 horizontally placed cameras together.\n"
  18. "Usage: %s\n"
  19. " -w=<board_width> # the number of inner corners per one of board dimension\n"
  20. " -h=<board_height> # the number of inner corners per another board dimension\n"
  21. " [-s=<squareSize>] # square size in some user-defined units (1 by default)\n"
  22. " [-o=<out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters\n"
  23. " [-zt] # assume zero tangential distortion\n"
  24. " [-a=<aspectRatio>] # fix aspect ratio (fx/fy)\n"
  25. " [-p] # fix the principal point at the center\n"
  26. " [input_data] # input data - text file with a list of the images of the board\n"
  27. "\n", argv[0] );
  28. }
  29. static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners)
  30. {
  31. corners.resize(0);
  32. for( int i = 0; i < boardSize.height; i++ )
  33. for( int j = 0; j < boardSize.width; j++ )
  34. corners.push_back(Point3f(float(j*squareSize),
  35. float(i*squareSize), 0));
  36. }
  37. static bool run3Calibration(vector<vector<Point2f> > imagePoints1,
  38. vector<vector<Point2f> > imagePoints2,
  39. vector<vector<Point2f> > imagePoints3,
  40. Size imageSize, Size boardSize,
  41. float squareSize, float aspectRatio,
  42. int flags,
  43. Mat& cameraMatrix1, Mat& distCoeffs1,
  44. Mat& cameraMatrix2, Mat& distCoeffs2,
  45. Mat& cameraMatrix3, Mat& distCoeffs3,
  46. Mat& R12, Mat& T12, Mat& R13, Mat& T13)
  47. {
  48. int c, i;
  49. // step 1: calibrate each camera individually
  50. vector<vector<Point3f> > objpt(1);
  51. vector<vector<Point2f> > imgpt;
  52. calcChessboardCorners(boardSize, squareSize, objpt[0]);
  53. vector<Mat> rvecs, tvecs;
  54. for( c = 1; c <= 3; c++ )
  55. {
  56. const vector<vector<Point2f> >& imgpt0 = c == 1 ? imagePoints1 : c == 2 ? imagePoints2 : imagePoints3;
  57. imgpt.clear();
  58. int N = 0;
  59. for( i = 0; i < (int)imgpt0.size(); i++ )
  60. if( !imgpt0[i].empty() )
  61. {
  62. imgpt.push_back(imgpt0[i]);
  63. N += (int)imgpt0[i].size();
  64. }
  65. if( imgpt.size() < 3 )
  66. {
  67. printf("Error: not enough views for camera %d\n", c);
  68. return false;
  69. }
  70. objpt.resize(imgpt.size(),objpt[0]);
  71. Mat cameraMatrix = Mat::eye(3, 3, CV_64F);
  72. if( flags & CALIB_FIX_ASPECT_RATIO )
  73. cameraMatrix.at<double>(0,0) = aspectRatio;
  74. Mat distCoeffs = Mat::zeros(5, 1, CV_64F);
  75. double err = calibrateCamera(objpt, imgpt, imageSize, cameraMatrix,
  76. distCoeffs, rvecs, tvecs,
  77. flags|CALIB_FIX_K3/*|CALIB_FIX_K4|CALIB_FIX_K5|CALIB_FIX_K6*/);
  78. bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
  79. if(!ok)
  80. {
  81. printf("Error: camera %d was not calibrated\n", c);
  82. return false;
  83. }
  84. printf("Camera %d calibration reprojection error = %g\n", c, sqrt(err/N));
  85. if( c == 1 )
  86. cameraMatrix1 = cameraMatrix, distCoeffs1 = distCoeffs;
  87. else if( c == 2 )
  88. cameraMatrix2 = cameraMatrix, distCoeffs2 = distCoeffs;
  89. else
  90. cameraMatrix3 = cameraMatrix, distCoeffs3 = distCoeffs;
  91. }
  92. vector<vector<Point2f> > imgpt_right;
  93. // step 2: calibrate (1,2) and (3,2) pairs
  94. for( c = 2; c <= 3; c++ )
  95. {
  96. const vector<vector<Point2f> >& imgpt0 = c == 2 ? imagePoints2 : imagePoints3;
  97. imgpt.clear();
  98. imgpt_right.clear();
  99. int N = 0;
  100. for( i = 0; i < (int)std::min(imagePoints1.size(), imgpt0.size()); i++ )
  101. if( !imagePoints1.empty() && !imgpt0[i].empty() )
  102. {
  103. imgpt.push_back(imagePoints1[i]);
  104. imgpt_right.push_back(imgpt0[i]);
  105. N += (int)imgpt0[i].size();
  106. }
  107. if( imgpt.size() < 3 )
  108. {
  109. printf("Error: not enough shared views for cameras 1 and %d\n", c);
  110. return false;
  111. }
  112. objpt.resize(imgpt.size(),objpt[0]);
  113. Mat cameraMatrix = c == 2 ? cameraMatrix2 : cameraMatrix3;
  114. Mat distCoeffs = c == 2 ? distCoeffs2 : distCoeffs3;
  115. Mat R, T, E, F;
  116. double err = stereoCalibrate(objpt, imgpt, imgpt_right, cameraMatrix1, distCoeffs1,
  117. cameraMatrix, distCoeffs,
  118. imageSize, R, T, E, F,
  119. CALIB_FIX_INTRINSIC,
  120. TermCriteria(TermCriteria::COUNT, 30, 0));
  121. printf("Pair (1,%d) calibration reprojection error = %g\n", c, sqrt(err/(N*2)));
  122. if( c == 2 )
  123. {
  124. cameraMatrix2 = cameraMatrix;
  125. distCoeffs2 = distCoeffs;
  126. R12 = R; T12 = T;
  127. }
  128. else
  129. {
  130. R13 = R; T13 = T;
  131. }
  132. }
  133. return true;
  134. }
  135. static bool readStringList( const string& filename, vector<string>& l )
  136. {
  137. l.resize(0);
  138. FileStorage fs(filename, FileStorage::READ);
  139. if( !fs.isOpened() )
  140. return false;
  141. FileNode n = fs.getFirstTopLevelNode();
  142. if( n.type() != FileNode::SEQ )
  143. return false;
  144. FileNodeIterator it = n.begin(), it_end = n.end();
  145. for( ; it != it_end; ++it )
  146. l.push_back((string)*it);
  147. return true;
  148. }
  149. int main( int argc, char** argv )
  150. {
  151. int i, k;
  152. int flags = 0;
  153. Size boardSize, imageSize;
  154. float squareSize, aspectRatio;
  155. string outputFilename;
  156. string inputFilename = "";
  157. vector<vector<Point2f> > imgpt[3];
  158. vector<string> imageList;
  159. cv::CommandLineParser parser(argc, argv,
  160. "{help ||}{w||}{h||}{s|1|}{o|out_camera_data.yml|}"
  161. "{zt||}{a|1|}{p||}{@input||}");
  162. if (parser.has("help"))
  163. {
  164. help(argv);
  165. return 0;
  166. }
  167. boardSize.width = parser.get<int>("w");
  168. boardSize.height = parser.get<int>("h");
  169. squareSize = parser.get<float>("s");
  170. aspectRatio = parser.get<float>("a");
  171. if (parser.has("a"))
  172. flags |= CALIB_FIX_ASPECT_RATIO;
  173. if (parser.has("zt"))
  174. flags |= CALIB_ZERO_TANGENT_DIST;
  175. if (parser.has("p"))
  176. flags |= CALIB_FIX_PRINCIPAL_POINT;
  177. outputFilename = parser.get<string>("o");
  178. inputFilename = parser.get<string>("@input");
  179. if (!parser.check())
  180. {
  181. help(argv);
  182. parser.printErrors();
  183. return -1;
  184. }
  185. if (boardSize.width <= 0)
  186. return fprintf( stderr, "Invalid board width\n" ), -1;
  187. if (boardSize.height <= 0)
  188. return fprintf( stderr, "Invalid board height\n" ), -1;
  189. if (squareSize <= 0)
  190. return fprintf( stderr, "Invalid board square width\n" ), -1;
  191. if (aspectRatio <= 0)
  192. return printf("Invalid aspect ratio\n" ), -1;
  193. if( inputFilename.empty() ||
  194. !readStringList(inputFilename, imageList) ||
  195. imageList.size() == 0 || imageList.size() % 3 != 0 )
  196. {
  197. printf("Error: the input image list is not specified, or can not be read, or the number of files is not divisible by 3\n");
  198. return -1;
  199. }
  200. Mat view, viewGray;
  201. Mat cameraMatrix[3], distCoeffs[3], R[3], P[3], R12, T12;
  202. for( k = 0; k < 3; k++ )
  203. {
  204. cameraMatrix[k] = Mat_<double>::eye(3,3);
  205. cameraMatrix[k].at<double>(0,0) = aspectRatio;
  206. cameraMatrix[k].at<double>(1,1) = 1;
  207. distCoeffs[k] = Mat_<double>::zeros(5,1);
  208. }
  209. Mat R13=Mat_<double>::eye(3,3), T13=Mat_<double>::zeros(3,1);
  210. FileStorage fs;
  211. namedWindow( "Image View", 0 );
  212. for( k = 0; k < 3; k++ )
  213. imgpt[k].resize(imageList.size()/3);
  214. for( i = 0; i < (int)(imageList.size()/3); i++ )
  215. {
  216. for( k = 0; k < 3; k++ )
  217. {
  218. int k1 = k == 0 ? 2 : k == 1 ? 0 : 1;
  219. printf("%s\n", imageList[i*3+k].c_str());
  220. view = imread(imageList[i*3+k], 1);
  221. if(!view.empty())
  222. {
  223. vector<Point2f> ptvec;
  224. imageSize = view.size();
  225. cvtColor(view, viewGray, COLOR_BGR2GRAY);
  226. bool found = findChessboardCorners( view, boardSize, ptvec, CALIB_CB_ADAPTIVE_THRESH );
  227. drawChessboardCorners( view, boardSize, Mat(ptvec), found );
  228. if( found )
  229. {
  230. imgpt[k1][i].resize(ptvec.size());
  231. std::copy(ptvec.begin(), ptvec.end(), imgpt[k1][i].begin());
  232. }
  233. //imshow("view", view);
  234. //int c = waitKey(0) & 255;
  235. //if( c == 27 || c == 'q' || c == 'Q' )
  236. // return -1;
  237. }
  238. }
  239. }
  240. printf("Running calibration ...\n");
  241. run3Calibration(imgpt[0], imgpt[1], imgpt[2], imageSize,
  242. boardSize, squareSize, aspectRatio, flags|CALIB_FIX_K4|CALIB_FIX_K5,
  243. cameraMatrix[0], distCoeffs[0],
  244. cameraMatrix[1], distCoeffs[1],
  245. cameraMatrix[2], distCoeffs[2],
  246. R12, T12, R13, T13);
  247. fs.open(outputFilename, FileStorage::WRITE);
  248. fs << "cameraMatrix1" << cameraMatrix[0];
  249. fs << "cameraMatrix2" << cameraMatrix[1];
  250. fs << "cameraMatrix3" << cameraMatrix[2];
  251. fs << "distCoeffs1" << distCoeffs[0];
  252. fs << "distCoeffs2" << distCoeffs[1];
  253. fs << "distCoeffs3" << distCoeffs[2];
  254. fs << "R12" << R12;
  255. fs << "T12" << T12;
  256. fs << "R13" << R13;
  257. fs << "T13" << T13;
  258. fs << "imageWidth" << imageSize.width;
  259. fs << "imageHeight" << imageSize.height;
  260. Mat Q;
  261. // step 3: find rectification transforms
  262. double ratio = rectify3Collinear(cameraMatrix[0], distCoeffs[0], cameraMatrix[1],
  263. distCoeffs[1], cameraMatrix[2], distCoeffs[2],
  264. imgpt[0], imgpt[2],
  265. imageSize, R12, T12, R13, T13,
  266. R[0], R[1], R[2], P[0], P[1], P[2], Q, -1.,
  267. imageSize, 0, 0, CALIB_ZERO_DISPARITY);
  268. Mat map1[3], map2[3];
  269. fs << "R1" << R[0];
  270. fs << "R2" << R[1];
  271. fs << "R3" << R[2];
  272. fs << "P1" << P[0];
  273. fs << "P2" << P[1];
  274. fs << "P3" << P[2];
  275. fs << "disparityRatio" << ratio;
  276. fs.release();
  277. printf("Disparity ratio = %g\n", ratio);
  278. for( k = 0; k < 3; k++ )
  279. initUndistortRectifyMap(cameraMatrix[k], distCoeffs[k], R[k], P[k], imageSize, CV_16SC2, map1[k], map2[k]);
  280. Mat canvas(imageSize.height, imageSize.width*3, CV_8UC3), small_canvas;
  281. destroyWindow("view");
  282. canvas = Scalar::all(0);
  283. for( i = 0; i < (int)(imageList.size()/3); i++ )
  284. {
  285. canvas = Scalar::all(0);
  286. for( k = 0; k < 3; k++ )
  287. {
  288. int k1 = k == 0 ? 2 : k == 1 ? 0 : 1;
  289. int k2 = k == 0 ? 1 : k == 1 ? 0 : 2;
  290. view = imread(imageList[i*3+k], 1);
  291. if(view.empty())
  292. continue;
  293. Mat rview = canvas.colRange(k2*imageSize.width, (k2+1)*imageSize.width);
  294. remap(view, rview, map1[k1], map2[k1], INTER_LINEAR);
  295. }
  296. printf("%s %s %s\n", imageList[i*3].c_str(), imageList[i*3+1].c_str(), imageList[i*3+2].c_str());
  297. resize( canvas, small_canvas, Size(1500, 1500/3), 0, 0, INTER_LINEAR_EXACT );
  298. for( k = 0; k < small_canvas.rows; k += 16 )
  299. line(small_canvas, Point(0, k), Point(small_canvas.cols, k), Scalar(0,255,0), 1);
  300. imshow("rectified", small_canvas);
  301. char c = (char)waitKey(0);
  302. if( c == 27 || c == 'q' || c == 'Q' )
  303. break;
  304. }
  305. return 0;
  306. }