omni_stereo_calibration.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #include "opencv2/ccalib/omnidir.hpp"
  2. #include "opencv2/core.hpp"
  3. #include "opencv2/imgproc.hpp"
  4. #include "opencv2/highgui.hpp"
  5. #include "opencv2/calib3d.hpp"
  6. #include <vector>
  7. #include <iostream>
  8. #include <string>
  9. #include <time.h>
  10. using namespace cv;
  11. using namespace std;
  12. const char * usage =
  13. "\n example command line for calibrate a pair of omnidirectional camera.\n"
  14. " omni_stereo_calibration -w 8 -h 6 -sw 2.4399 -sh 2.4399 imagelist_left.xml imagelist_right.xml\n"
  15. " \n"
  16. " the file image_list_1.xml and image_list_2.xml generated by imagelist_creator as\n"
  17. "imagelist_creator image_list_1.xml *.*";
  18. static void help()
  19. {
  20. printf("\n This is a sample for omnidirectional camera calibration.\n"
  21. "Usage: omni_calibration\n"
  22. " -w <board_width> # the number of inner corners per one of board dimension\n"
  23. " -h <board_height> # the number of inner corners per another board dimension\n"
  24. " [-sw <square_width>] # the width of square in some user-defined units (1 by default)\n"
  25. " [-sh <square_height>] # the height of square in some user-defined units (1 by default)\n"
  26. " [-o <out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters\n"
  27. " [-fs <fix_skew>] # fix skew\n"
  28. " [-fp ] # fix the principal point at the center\n"
  29. " input_data_1 # input data - text file with a list of the images of the first camera, which is generated by imagelist_creator"
  30. " input_data_2 # input data - text file with a list of the images of the second camera, which is generated by imagelist_creator"
  31. );
  32. printf("\n %s", usage);
  33. }
  34. static void calcChessboardCorners(Size boardSize, double square_width, double square_height,
  35. Mat& corners)
  36. {
  37. // corners has type of CV_64FC3
  38. corners.release();
  39. int n = boardSize.width * boardSize.height;
  40. corners.create(n, 1, CV_64FC3);
  41. Vec3d *ptr = corners.ptr<Vec3d>();
  42. for (int i = 0; i < boardSize.height; ++i)
  43. {
  44. for (int j = 0; j < boardSize.width; ++j)
  45. {
  46. ptr[i*boardSize.width + j] = Vec3d(double(j * square_width), double(i * square_height), 0.0);
  47. }
  48. }
  49. }
  50. static bool detecChessboardCorners(const vector<string>& list1, vector<string>& list_detected_1,
  51. const vector<string>& list2, vector<string>& list_detected_2,
  52. vector<Mat>& image_points_1, vector<Mat>& image_points_2, Size boardSize, Size& imageSize1, Size& imageSize2)
  53. {
  54. image_points_1.resize(0);
  55. image_points_2.resize(0);
  56. list_detected_1.resize(0);
  57. list_detected_2.resize(0);
  58. int n_img = (int)list1.size();
  59. Mat img_l, img_r;
  60. for(int i = 0; i < n_img; ++i)
  61. {
  62. Mat points_1, points_2;
  63. img_l = imread(list1[i], IMREAD_GRAYSCALE);
  64. img_r = imread(list2[i], IMREAD_GRAYSCALE);
  65. bool found_l = findChessboardCorners( img_l, boardSize, points_1);
  66. bool found_r = findChessboardCorners( img_r, boardSize, points_2);
  67. if (found_l && found_r)
  68. {
  69. if (points_1.type() != CV_64FC2)
  70. points_1.convertTo(points_1, CV_64FC2);
  71. if (points_2.type() != CV_64FC2)
  72. points_2.convertTo(points_2, CV_64FC2);
  73. image_points_1.push_back(points_1);
  74. image_points_2.push_back(points_2);
  75. list_detected_1.push_back(list1[i]);
  76. list_detected_2.push_back(list2[i]);
  77. }
  78. }
  79. if (!img_l.empty())
  80. imageSize1 = img_l.size();
  81. if (!img_r.empty())
  82. {
  83. imageSize2 = img_r.size();
  84. }
  85. if (image_points_1.size() < 3)
  86. return false;
  87. else
  88. return true;
  89. }
  90. static bool readStringList( const string& filename, vector<string>& l )
  91. {
  92. l.resize(0);
  93. FileStorage fs(filename, FileStorage::READ);
  94. if( !fs.isOpened() )
  95. return false;
  96. FileNode n = fs.getFirstTopLevelNode();
  97. if( n.type() != FileNode::SEQ )
  98. return false;
  99. FileNodeIterator it = n.begin(), it_end = n.end();
  100. for( ; it != it_end; ++it )
  101. l.push_back((string)*it);
  102. return true;
  103. }
  104. static void saveCameraParams( const string & filename, const int flags, const Mat& cameraMatrix1, const Mat& cameraMatrix2, const Mat& distCoeffs1,
  105. const Mat& disCoeffs2, const double xi1, const double xi2, const Vec3d rvec, const Vec3d tvec,
  106. const vector<Vec3d>& rvecs, const vector<Vec3d>& tvecs, vector<string> detec_list_1, vector<string> detec_list_2,
  107. const Mat& idx, const double rms, const vector<Mat>& imagePoints1, const vector<Mat>& imagePoints2)
  108. {
  109. FileStorage fs( filename, FileStorage::WRITE );
  110. time_t tt;
  111. time( &tt );
  112. struct tm *t2 = localtime( &tt );
  113. char buf[1024];
  114. strftime( buf, sizeof(buf)-1, "%c", t2 );
  115. fs << "calibration_time" << buf;
  116. if ( !rvecs.empty())
  117. fs << "nFrames" << (int)rvecs.size();
  118. if ( flags != 0)
  119. {
  120. sprintf( buf, "flags: %s%s%s%s%s%s%s%s%s",
  121. flags & omnidir::CALIB_USE_GUESS ? "+use_intrinsic_guess" : "",
  122. flags & omnidir::CALIB_FIX_SKEW ? "+fix_skew" : "",
  123. flags & omnidir::CALIB_FIX_K1 ? "+fix_k1" : "",
  124. flags & omnidir::CALIB_FIX_K2 ? "+fix_k2" : "",
  125. flags & omnidir::CALIB_FIX_P1 ? "+fix_p1" : "",
  126. flags & omnidir::CALIB_FIX_P2 ? "+fix_p2" : "",
  127. flags & omnidir::CALIB_FIX_XI ? "+fix_xi" : "",
  128. flags & omnidir::CALIB_FIX_GAMMA ? "+fix_gamma" : "",
  129. flags & omnidir::CALIB_FIX_CENTER ? "+fix_center" : "");
  130. //cvWriteComment( *fs, buf, 0 );
  131. }
  132. fs << "flags" << flags;
  133. fs << "camera_matrix_1" << cameraMatrix1;
  134. fs << "distortion_coefficients_1" << distCoeffs1;
  135. fs << "xi_1" << xi1;
  136. fs << "camera_matrix_2" << cameraMatrix2;
  137. fs << "distortion_coefficients_2" << disCoeffs2;
  138. fs << "xi_2" << xi2;
  139. Mat om_t(1, 6, CV_64F);
  140. Mat(rvec).reshape(1, 1).copyTo(om_t.colRange(0, 3));
  141. Mat(tvec).reshape(1, 1).copyTo(om_t.colRange(3, 6));
  142. //cvWriteComment( *fs, "6-tuples (rotation vector + translation vector) for each view", 0 );
  143. fs << "extrinsic_parameters" << om_t;
  144. if ( !rvecs.empty() && !tvecs.empty() )
  145. {
  146. Mat rvec_tvec((int)rvecs.size(), 6, CV_64F);
  147. for (int i = 0; i < (int)rvecs.size(); ++i)
  148. {
  149. Mat(rvecs[i]).reshape(1, 1).copyTo(rvec_tvec(Rect(0, i, 3, 1)));
  150. Mat(tvecs[i]).reshape(1, 1).copyTo(rvec_tvec(Rect(3, i, 3, 1)));
  151. }
  152. //cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 );
  153. fs << "extrinsic_parameters_1" << rvec_tvec;
  154. }
  155. fs << "rms" << rms;
  156. //cvWriteComment( *fs, "names of images that are acturally used in calibration", 0 );
  157. fs << "used_imgs_1" << "[";
  158. for (int i = 0; i < (int)idx.total(); ++i)
  159. {
  160. fs << detec_list_1[(int)idx.at<int>(i)];
  161. }
  162. fs << "]";
  163. fs << "used_imgs_2" << "[";
  164. for (int i = 0; i < (int)idx.total(); ++i)
  165. {
  166. fs << detec_list_2[(int)idx.at<int>(i)];
  167. }
  168. fs << "]";
  169. if ( !imagePoints1.empty() )
  170. {
  171. Mat imageMat((int)imagePoints1.size(), (int)imagePoints1[0].total(), CV_64FC2);
  172. for (int i = 0; i < (int)imagePoints1.size(); ++i)
  173. {
  174. Mat r = imageMat.row(i).reshape(2, imageMat.cols);
  175. Mat imagei(imagePoints1[i]);
  176. imagei.copyTo(r);
  177. }
  178. fs << "image_points_1" << imageMat;
  179. }
  180. if ( !imagePoints2.empty() )
  181. {
  182. Mat imageMat((int)imagePoints2.size(), (int)imagePoints2[0].total(), CV_64FC2);
  183. for (int i = 0; i < (int)imagePoints2.size(); ++i)
  184. {
  185. Mat r = imageMat.row(i).reshape(2, imageMat.cols);
  186. Mat imagei(imagePoints2[i]);
  187. imagei.copyTo(r);
  188. }
  189. fs << "image_points_2" << imageMat;
  190. }
  191. }
  192. int main(int argc, char** argv)
  193. {
  194. Size boardSize, imageSize1, imageSize2;
  195. int flags = 0;
  196. double square_width = 0.0, square_height = 0.0;
  197. const char* outputFilename = "out_camera_params_stereo.xml";
  198. const char* inputFilename1 = 0;
  199. const char* inputFilename2 = 0;
  200. vector<Mat> objectPoints;
  201. vector<Mat> imagePoints1;
  202. vector<Mat> imagePoints2;
  203. if(argc < 2)
  204. {
  205. help();
  206. return 1;
  207. }
  208. bool fist_flag = true;
  209. for(int i = 1; i < argc; i++)
  210. {
  211. const char* s = argv[i];
  212. if( strcmp( s, "-w") == 0)
  213. {
  214. if( sscanf( argv[++i], "%u", &boardSize.width ) != 1 || boardSize.width <= 0 )
  215. return fprintf( stderr, "Invalid board width\n" ), -1;
  216. }
  217. else if( strcmp( s, "-h" ) == 0 )
  218. {
  219. if( sscanf( argv[++i], "%u", &boardSize.height ) != 1 || boardSize.height <= 0 )
  220. return fprintf( stderr, "Invalid board height\n" ), -1;
  221. }
  222. else if( strcmp( s, "-sw" ) == 0 )
  223. {
  224. if( sscanf( argv[++i], "%lf", &square_width ) != 1 || square_width <= 0 )
  225. return fprintf(stderr, "Invalid square width\n"), -1;
  226. }
  227. else if( strcmp( s, "-sh" ) == 0 )
  228. {
  229. if( sscanf( argv[++i], "%lf", &square_height) != 1 || square_height <= 0 )
  230. return fprintf(stderr, "Invalid square height\n"), -1;
  231. }
  232. else if( strcmp( s, "-o" ) == 0 )
  233. {
  234. outputFilename = argv[++i];
  235. }
  236. else if( strcmp( s, "-fs" ) == 0 )
  237. {
  238. flags |= omnidir::CALIB_FIX_SKEW;
  239. }
  240. else if( strcmp( s, "-fp" ) == 0 )
  241. {
  242. flags |= omnidir::CALIB_FIX_CENTER;
  243. }
  244. else if( s[0] != '-' && fist_flag)
  245. {
  246. fist_flag = false;
  247. inputFilename1 = s;
  248. }
  249. else if( s[0] != '-' && !fist_flag)
  250. {
  251. inputFilename2 = s;
  252. }
  253. else
  254. {
  255. return fprintf( stderr, "Unknown option %s\n", s ), -1;
  256. }
  257. }
  258. // get image name list
  259. vector<string> image_list1, detec_list_1, image_list2, detec_list_2;
  260. if((!readStringList(inputFilename1, image_list1)) || (!readStringList(inputFilename2, image_list2)))
  261. return fprintf( stderr, "Failed to read image list\n"), -1;
  262. // find corners in images
  263. // some images may be failed in automatic corner detection, passed cases are in detec_list
  264. if(!detecChessboardCorners(image_list1, detec_list_1, image_list2, detec_list_2,
  265. imagePoints1, imagePoints2, boardSize, imageSize1, imageSize2))
  266. return fprintf(stderr, "Not enough corner detected images\n"), -1;
  267. // calculate object coordinates
  268. Mat object;
  269. calcChessboardCorners(boardSize, square_width, square_height, object);
  270. for(int i = 0; i < (int)detec_list_1.size(); ++i)
  271. {
  272. objectPoints.push_back(object);
  273. }
  274. // run calibration, some images are discarded in calibration process because they are failed
  275. // in initialization. Retained image indexes are in idx variable.
  276. Mat K1, K2, D1, D2, xi1, xi2, idx;
  277. vector<Vec3d> rvecs, tvecs;
  278. Vec3d rvec, tvec;
  279. double _xi1, _xi2, rms;
  280. TermCriteria criteria(3, 200, 1e-8);
  281. rms = omnidir::stereoCalibrate(objectPoints, imagePoints1, imagePoints2, imageSize1, imageSize2, K1, xi1, D1,
  282. K2, xi2, D2, rvec, tvec, rvecs, tvecs, flags, criteria, idx);
  283. _xi1 = xi1.at<double>(0);
  284. _xi2 = xi2.at<double>(0);
  285. saveCameraParams(outputFilename, flags, K1, K2, D1, D2, _xi1, _xi2, rvec, tvec, rvecs, tvecs,
  286. detec_list_1, detec_list_2, idx, rms, imagePoints1, imagePoints2);
  287. }