天天看點

OpenCV 标定和畸變校正(2)

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/u013498583/article/details/71703175

<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-e2445db1a8.css" target="_blank" rel="external nofollow" >
                    <div class="htmledit_views">
           

海爾630冰箱的标定和畸變校正。上篇文章中直接使用OpenCV的例程進行畸變校正的效果不太理想。使用以下方法(張正友)效果更好。

京東連結:

http://item.jd.com/4027713.html#crumb-wrap

标定代碼:

  1. /************************************************************************
  2. 運作環境:VS2013+OpenCV 2.4.13
  3. 運作結果:檢測拍攝的棋盤畸變圖像,擷取攝像頭的畸變資訊
  4. *************************************************************************/
  5. #include <opencv2\opencv.hpp>
  6. #include <fstream>
  7. #include <iostream>
  8. using namespace std;
  9. using namespace cv;
  10. char dir[ ];
  11. char fileNames[ ];
  12. char chess_boardImage_path[] = "E:\\hanxiaoxuan\\distort\\";
  13. char chess_boardDetect_path[] = "E:\\hanxiaoxuan\\distort_detect\\";
  14. char chess_boardCorner_path[] = "E:\\hanxiaoxuan\\distort_corner\\";
  15. char calibrationResult[] = "E:\\hanxiaoxuan\\calibration_result.txt";
  16. char datFileName[] = "E:\\hanxiaoxuan\\camParam.dat";
  17. int main()
  18. {
  19. string *imageList = new string[ ];
  20. string *chess_boardList = new string[ ];
  21. ofstream fout(calibrationResult); //儲存标定結果的檔案
  22. // 利用dir指令将目前目錄下的.jpg檔案名寫入names.txt
  23. sprintf(dir, "%s%s%s%s%s%s", "dir ", chess_boardImage_path, "*.jpg", " /a /b >", chess_boardImage_path, "names.txt");
  24. system(dir);
  25. char name[ ] = "";
  26. // 打開檔案讀取其中的檔案名
  27. sprintf(fileNames, "%s%s", chess_boardImage_path, "names.txt");
  28. FILE* fp = fopen(fileNames, "r");
  29. if ( NULL == fp)
  30. printf( "error,cannot open the name list");
  31. // 獲得檔案數量
  32. int line = ;
  33. while (fgets(name, , fp) != NULL)
  34. {
  35. char subname[ ];
  36. sscanf(name, "%[^\n]%s", subname);
  37. string image_name;
  38. stringstream stream;
  39. stream << subname;
  40. image_name = stream.str();
  41. imageList[line] = image_name.substr( , image_name.length() - );
  42. line++;
  43. }
  44. //讀取每一幅圖像,從中提取出角點,然後對角點進行亞像素精确化
  45. cout << "開始提取角點………………" << endl;
  46. int image_count = line; //圖像數量
  47. Size board_size = Size( , ); //棋盤上每行、列的内角點數
  48. vector<Point2f> corners; //緩存每幅圖像上檢測到的角點
  49. vector< vector<Point2f>> corners_Seq; //儲存檢測到的所有角點
  50. vector<Mat> image_Seq;
  51. int successImageNum = ; //成功提取角點的棋盤圖數量
  52. int count = ;
  53. for ( int i = ; i != image_count; i++)
  54. {
  55. cout << "Frame #" << i + << "..." << endl;
  56. string imageFileName;
  57. imageFileName = imageList[i]; //圖像的檔案名
  58. imageFileName += ".jpg"; //圖像的檔案名.jpg
  59. cv::Mat image = imread(chess_boardImage_path + imageFileName);
  60. //提取角點
  61. cv::Mat imageGray;
  62. cvtColor(image, imageGray, CV_RGB2GRAY);
  63. bool patternfound = findChessboardCorners(image, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE +
  64. CALIB_CB_FAST_CHECK);
  65. if (!patternfound)
  66. {
  67. cout << "can not find chessboard corners!\n";
  68. continue;
  69. exit( );
  70. }
  71. else
  72. {
  73. //亞像素精确化
  74. cornerSubPix(imageGray, corners, Size( , ), Size( , ), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, , ));
  75. //繪制檢測到的角點并儲存
  76. Mat imageTemp = image.clone();
  77. for ( int j = ; j < corners.size(); j++)
  78. {
  79. circle(imageTemp, corners[j], , Scalar( , , ), , , );
  80. }
  81. string imageFileName;
  82. imageFileName = imageList[i];
  83. imageFileName += "_corner.jpg";
  84. imwrite(chess_boardCorner_path + imageFileName, imageTemp);
  85. cout << "Frame corner#" << i + << "...end" << endl;
  86. imwrite(chess_boardDetect_path + imageFileName, image);
  87. chess_boardList[successImageNum] = imageList[i];
  88. count = count + corners.size();
  89. successImageNum = successImageNum + ;
  90. corners_Seq.push_back(corners);
  91. }
  92. image_Seq.push_back(image);
  93. }
  94. cout << "角點提取完成!\n";
  95. //錄影機标定
  96. cout << "開始标定………………" << endl;
  97. Size square_size = Size( , );
  98. vector< vector<Point3f>> object_Points; //儲存标定闆上角點的三維坐标
  99. Mat image_points = Mat( , count, CV_32FC2, Scalar::all( )); //儲存提取的所有角點
  100. vector< int> point_counts;
  101. //初始化标定闆上角點的三維坐标
  102. for ( int t = ; t < successImageNum; t++)
  103. {
  104. vector<Point3f> tempPointSet;
  105. for ( int i = ; i < board_size.height; i++)
  106. {
  107. for ( int j = ; j < board_size.width; j++)
  108. {
  109. //假設标定闆放在世界坐标系中z=0的平面上
  110. Point3f tempPoint;
  111. tempPoint.x = i*square_size.width;
  112. tempPoint.y = j*square_size.height;
  113. tempPoint.z = ;
  114. tempPointSet.push_back(tempPoint);
  115. }
  116. }
  117. object_Points.push_back(tempPointSet);
  118. }
  119. for ( int i = ; i < successImageNum; i++)
  120. {
  121. point_counts.push_back(board_size.width*board_size.height);
  122. }
  123. //開始标定
  124. Size image_size = image_Seq[ ].size();
  125. cv::Matx33d intrinsic_matrix; //錄影機内參數矩陣
  126. cv::Vec4d distortion_coeffs; //錄影機的4個畸變系數:k1,k2,k3,k4
  127. std:: vector<cv::Vec3d> rotation_vectors; //每幅圖像的旋轉向量
  128. std:: vector<cv::Vec3d> translation_vectors; //每幅圖像的平移向量
  129. int flags = ;
  130. flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
  131. flags |= cv::fisheye::CALIB_CHECK_COND;
  132. flags |= cv::fisheye::CALIB_FIX_SKEW;
  133. fisheye::calibrate(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, flags, cv::TermCriteria( , , ));
  134. cout << "标定完成!\n";
  135. FILE *camParam = fopen(datFileName, "wb");
  136. if (camParam == NULL) {
  137. std:: cout << "can not create data file: " << datFileName << " !!!" << std:: endl;
  138. return false;
  139. }
  140. fwrite(&intrinsic_matrix, sizeof(cv::Matx33d), , camParam);
  141. fwrite(&distortion_coeffs, sizeof(cv::Vec4d), , camParam);
  142. fwrite(&image_size, sizeof(Size), , camParam);
  143. fclose(camParam);
  144. //對标定結果進行評價
  145. cout << "開始評價标定結果………………" << endl;
  146. double total_err = ; //所有圖像的平均誤差的總和
  147. double err = ; //每幅圖像的平均誤差
  148. vector<Point2f> image_points2; //儲存重新計算得到的投影點
  149. cout << "每幅圖像的标定誤差:" << endl;
  150. cout << "每幅圖像的标定誤差:" << endl << endl;
  151. for ( int i = ; i < successImageNum; i++)
  152. {
  153. vector<Point3f> tempPointSet = object_Points[i];
  154. //通過得到的錄影機内外參數,對空間的三維點進行重新投影計算,得到新的投影點
  155. fisheye::projectPoints(tempPointSet, image_points2, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs);
  156. //計算新的投影點和舊的投影點之間的誤差
  157. vector<Point2f> tempImagePoint = corners_Seq[i];
  158. Mat tempImagePointMat = Mat( , tempImagePoint.size(), CV_32FC2);
  159. Mat image_points2Mat = Mat( , image_points2.size(), CV_32FC2);
  160. for ( size_t i = ; i != tempImagePoint.size(); i++)
  161. {
  162. image_points2Mat.at<Vec2f>( , i) = Vec2f(image_points2[i].x, image_points2[i].y);
  163. tempImagePointMat.at<Vec2f>( , i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y);
  164. }
  165. err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
  166. total_err += err /= point_counts[i];
  167. cout << "第" << i + << "幅圖像的平均誤差:" << err << "像素" << endl;
  168. fout << "第" << i + << "幅圖像的平均誤差:" << err << "像素" << endl;
  169. }
  170. cout << "總體平均誤差:" << total_err / image_count << "像素" << endl;
  171. fout << "總體平均誤差:" << total_err / image_count << "像素" << endl << endl;
  172. cout << "評價完成!" << endl;
  173. //儲存标定結果
  174. cout << "開始儲存标定結果………………" << endl;
  175. Mat rotation_matrix = Mat( , , CV_32FC1, Scalar::all( )); //儲存每幅圖像的旋轉矩陣
  176. fout << "相機内參數矩陣:" << endl;
  177. fout << intrinsic_matrix << endl;
  178. fout << "畸變系數:\n";
  179. fout << distortion_coeffs << endl;
  180. for ( int i = ; i < successImageNum; i++)
  181. {
  182. fout << "第" << i + << "幅圖像的旋轉向量:" << endl;
  183. fout << rotation_vectors[i] << endl;
  184. //将旋轉向量轉換為相對應的旋轉矩陣
  185. Rodrigues(rotation_vectors[i], rotation_matrix);
  186. fout << "第" << i + << "幅圖像的旋轉矩陣:" << endl;
  187. fout << rotation_matrix << endl;
  188. fout << "第" << i + << "幅圖像的平移向量:" << endl;
  189. fout << translation_vectors[i] << endl;
  190. }
  191. cout << "完成儲存" << endl;
  192. fout << endl;
  193. //顯示标定結果
  194. Mat mapx = Mat(image_size, CV_32FC1);
  195. Mat mapy = Mat(image_size, CV_32FC1);
  196. Mat R = Mat::eye( , , CV_32F);
  197. cout << "儲存矯正圖像" << endl;
  198. for ( int i = ; i != successImageNum; i++)
  199. {
  200. cout << "Frame #" << i + << "..." << endl;
  201. Mat newCameraMatrix = Mat( , , CV_32FC1, Scalar::all( ));
  202. fisheye::initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, intrinsic_matrix, image_size, CV_32FC1, mapx, mapy);
  203. Mat t = image_Seq[i].clone();
  204. cv::remap(image_Seq[i], t, mapx, mapy, INTER_LINEAR);
  205. string imageFileName;
  206. imageFileName = chess_boardList[i];
  207. imageFileName += "_d.jpg";
  208. imwrite(chess_boardCorner_path + imageFileName, t);
  209. }
  210. cout << "儲存結束" << endl;
  211. delete [] imageList;
  212. delete [] chess_boardList;
  213. return ;
  214. }

得到兩組參數,分别是相機的内參矩陣和畸變系數。儲存在camPara.dat供calibration代碼校正使用。

校正代碼:

  1. /************************************************************************
  2. 運作環境:VS2013+OpenCV 2.4.13
  3. 運作結果:根據攝像頭的畸變資訊,進行相機标定
  4. *************************************************************************/
  5. #include <opencv2\opencv.hpp>
  6. #include <fstream>
  7. #include <iostream>
  8. using namespace std;
  9. using namespace cv;
  10. char dir[ ];
  11. char test_fileNames[ ];
  12. char testFile_path[] = "E:\\hanxiaoxuan\\distort\\";
  13. char outputFile_path[] = "E:\\hanxiaoxuan\\undistort\\";
  14. char test_datFileName[] = "E:\\hanxiaoxuan\\camParam.dat";
  15. //string test_imageList[100];
  16. int main()
  17. {
  18. string *test_imageList = new string[ ];
  19. // 利用dir指令将目前目錄下的.jpg檔案名寫入test_names.txt
  20. sprintf(dir, "%s%s%s%s%s%s", "dir ", testFile_path, "*.jpg", " /a /b >", testFile_path, "test_names.txt");
  21. system(dir);
  22. char test_name[ ] = "";
  23. // 打開檔案讀取其中的檔案名
  24. sprintf(test_fileNames, "%s%s", testFile_path, "test_names.txt");
  25. FILE* fp_test = fopen(test_fileNames, "r");
  26. if ( NULL == fp_test)
  27. printf( "error,cannot open the name list");
  28. // 獲得檔案數量
  29. int line = ;
  30. while (fgets(test_name, , fp_test) != NULL)
  31. {
  32. char subname[ ];
  33. sscanf(test_name, "%[^\n]%s", subname);
  34. string image_name;
  35. stringstream stream;
  36. stream << subname;
  37. image_name = stream.str();
  38. test_imageList[line] = image_name.substr( , image_name.length() - );
  39. line++;
  40. }
  41. string testName;
  42. //利用錄影機畸變參數對圖檔進行矯正
  43. cout << "儲存矯正圖像" << endl;
  44. for ( int i = ; i < line; i++)
  45. {
  46. cout << "Frame #" << i + << "..." << endl;
  47. testName = test_imageList[i] + ".jpg";
  48. Mat testImage = imread(testFile_path + testName);
  49. cv::Matx33d test_intrinsic_matrix;
  50. cv::Vec4d test_distortion_coeffs;
  51. Size test_image_size = testImage.size();
  52. FILE *test_camParam = fopen(test_datFileName, "rb");
  53. if (test_camParam == NULL) {
  54. std:: cout << "can not create data file: " << test_datFileName << " !!!" << std:: endl;
  55. return false;
  56. }
  57. fread(&test_intrinsic_matrix, sizeof(cv::Matx33d), , test_camParam);
  58. fread(&test_distortion_coeffs, sizeof(cv::Vec4d), , test_camParam);
  59. fread(&test_image_size, sizeof(Size), , test_camParam);
  60. fclose(test_camParam);
  61. Mat test_mapx = Mat(test_image_size, CV_32FC1);
  62. Mat test_mapy = Mat(test_image_size, CV_32FC1);
  63. Mat test_R = Mat::eye( , , CV_32F);
  64. fisheye::initUndistortRectifyMap(test_intrinsic_matrix, test_distortion_coeffs, test_R, test_intrinsic_matrix, test_image_size, CV_32FC1, test_mapx, test_mapy);
  65. Mat t = testImage.clone();
  66. cv::remap(testImage, t, test_mapx, test_mapy, INTER_LINEAR);
  67. imwrite(outputFile_path + testName, t);
  68. }
  69. cout << "标定結束" << endl;
  70. delete [] test_imageList;
  71. return ;
  72. }

校正效果如下:

原圖1

OpenCV 标定和畸變校正(2)

原圖校正後

OpenCV 标定和畸變校正(2)

原圖2

OpenCV 标定和畸變校正(2)

校正後

OpenCV 标定和畸變校正(2)

校正效果基本令人滿意。

</div>
            </div>