opencv-相机标定步骤、评估标定误差以及标定之后图像坐标到世界坐标的转换

前一段时间,研究了下相机标定。关于opencv相机标定,包括标定完后,世界坐标到 图像坐标的转换,以评估图像的标定误差,这些网上有很多资源和源代码。

可是,相机标定完之后,我们想要的是,知道了图像坐标,想要得到它的世界坐标,或者我们已知图像上两个点之间的像素距离,现在我们想知道两个点之间的实际距离。

楼主在网上搜了很多资源,问了很多人,都没有相关的代码,可以得到这样的结论:opencv没有提供现成的函数,满足从图像坐标到世界坐标的转换。

所以,我们最想要的这一步,是需要自己写的。(如果我理解的不对,希望看到这篇博文的人,能够批评指正)

相机标定的主要思路为:

一、获取十几张不同角度拍摄的图片,角点检测,得到每个角点的坐标;

二、分别定义十几张照片中,世界坐标系下的角点坐标,一般x、y为等间距,z为0 ;

三、开始标定,主要函数为calibrateCamera;

四、得到了相机内参和畸变系数,这是标定完后相机的属性,还会得到外参,外参代表着每张图片所在的平面;

五、opencv提供了世界坐标到图像坐标的转换函数,主要用来评估标定的误差;

六、我们最想要的,根据图像坐标到世界坐标的转换,本质上就是矩阵的运算,需要自己写;




下面贴出代码,开发环境opencv2.4.9+vs2013

[cpp] view plain copy
  1. #include "opencv2/core/core.hpp"  
  2. #include "opencv2/imgproc/imgproc.hpp"  
  3. #include "opencv2/calib3d/calib3d.hpp"  
  4. #include "opencv2/highgui/highgui.hpp"  
  5. #include   
  6. #include   
  7.   
  8. using namespace std;  
  9. using namespace cv;  
  10.   
  11. const int imageWidth = 1600;                             //摄像头的分辨率    
  12. const int imageHeight = 1200;  
  13. const int boardWidth = 39;                               //横向的角点数目    
  14. const int boardHeight = 39;                              //纵向的角点数据    
  15. const int boardCorner = boardWidth * boardHeight;       //总的角点数据    
  16. const int frameNumber =7;                             //相机标定时需要采用的图像帧数    
  17. const int squareSize = 10;                              //标定板黑白格子的大小 单位mm    
  18. const Size boardSize = Size(boardWidth, boardHeight);   //    
  19.   
  20. Mat intrinsic;                                          //相机内参数    
  21. Mat distortion_coeff;                                   //相机畸变参数    
  22. vector rvecs;                                        //旋转向量    
  23. vector tvecs;                                        //平移向量    
  24. vector> corners;                        //各个图像找到的角点的集合 和objRealPoint 一一对应    
  25. vector> objRealPoint;                   //各副图像的角点的实际物理坐标集合    
  26.   
  27.   
  28. vector corner;                                   //某一副图像找到的角点    
  29.   
  30. Mat rgbImage, grayImage;  
  31.   
  32. /*计算标定板上模块的实际物理坐标*/  
  33. void calRealPoint(vector>& obj, int boardwidth, int boardheight, int imgNumber, int squaresize)  
  34. {  
  35.     //  Mat imgpoint(boardheight, boardwidth, CV_32FC3,Scalar(0,0,0));    
  36.     vector imgpoint;  
  37.     for (int rowIndex = 0; rowIndex < boardheight; rowIndex++)  
  38.     {  
  39.         for (int colIndex = 0; colIndex < boardwidth; colIndex++)  
  40.         {  
  41.             //  imgpoint.at(rowIndex, colIndex) = Vec3f(rowIndex * squaresize, colIndex*squaresize, 0);    
  42.             imgpoint.push_back(Point3f(colIndex * squaresize, rowIndex * squaresize, 0));  
  43.         }  
  44.     }  
  45.     for (int imgIndex = 0; imgIndex < imgNumber; imgIndex++)  
  46.     {  
  47.         obj.push_back(imgpoint);  
  48.     }  
  49. }  
  50.   
  51. /*设置相机的初始参数 也可以不估计*/  
  52.   
  53. void CalibrationEvaluate(void)//标定结束后进行评价  
  54. {  
  55.     double err=0;  
  56.     double total_err=0;  
  57.     //calibrateCamera(objRealPoint, corners, Size(imageWidth, imageHeight), intrinsic, distortion_coeff, rvecs, tvecs, 0);  
  58.     cout << "每幅图像的定标误差:" << endl;  
  59.     for (int i = 0; i < corners.size(); i++)  
  60.     {  
  61.         vector image_points2;  
  62.         vector tempPointSet = objRealPoint[i];  
  63.         projectPoints(tempPointSet, rvecs[i], tvecs[i], intrinsic, distortion_coeff, image_points2);  
  64.   
  65.   
  66.   
  67.   
  68.         vector tempImagePoint = corners[i];  
  69.         Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);  
  70.         Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);  
  71.         for (int j = 0; j < tempImagePoint.size(); j++)  
  72.         {  
  73.             image_points2Mat.at(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);  
  74.             tempImagePointMat.at(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);  
  75.         }  
  76.         err = norm(image_points2Mat, tempImagePointMat, NORM_L2);  
  77.         total_err = err + total_err;  
  78.         cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;  
  79.     }  
  80.     cout << "总体平均误差:" << total_err / (corners.size() + 1) << "像素" << endl;  
  81. }  
  82.   
  83. void guessCameraParam(void)  
  84. {  
  85.     /*分配内存*/  
  86.     intrinsic.create(3, 3, CV_64FC1);  
  87.     distortion_coeff.create(5, 1, CV_64FC1);  
  88.   
  89.     /* 
  90.     fx 0 cx 
  91.     0 fy cy 
  92.     0 0  1 
  93.     */  
  94.     intrinsic.at<double>(0, 0) = 256.8093262;   //fx           
  95.     intrinsic.at<double>(0, 2) = 160.2826538;   //cx    
  96.     intrinsic.at<double>(1, 1) = 254.7511139;   //fy    
  97.     intrinsic.at<double>(1, 2) = 127.6264572;   //cy    
  98.   
  99.     intrinsic.at<double>(0, 1) = 0;  
  100.     intrinsic.at<double>(1, 0) = 0;  
  101.     intrinsic.at<double>(2, 0) = 0;  
  102.     intrinsic.at<double>(2, 1) = 0;  
  103.     intrinsic.at<double>(2, 2) = 1;  
  104.   
  105.     /* 
  106.     k1 k2 p1 p2 p3 
  107.     */  
  108.     distortion_coeff.at<double>(0, 0) = -0.193740;  //k1    
  109.     distortion_coeff.at<double>(1, 0) = -0.378588;  //k2    
  110.     distortion_coeff.at<double>(2, 0) = 0.028980;   //p1    
  111.     distortion_coeff.at<double>(3, 0) = 0.008136;   //p2    
  112.     distortion_coeff.at<double>(4, 0) = 0;          //p3    
  113. }  
  114.   
  115. void outputCameraParam(void)  
  116. {  
  117.     /*保存数据*/  
  118.     //cvSave("cameraMatrix.xml", &intrinsic);    
  119.     //cvSave("cameraDistoration.xml", &distortion_coeff);    
  120.     //cvSave("rotatoVector.xml", &rvecs);    
  121.     //cvSave("translationVector.xml", &tvecs);    
  122.     /*输出数据*/  
  123.     cout << "fx :" << intrinsic.at<double>(0, 0) << endl << "fy :" << intrinsic.at<double>(1, 1) << endl;  
  124.     cout << "cx :" << intrinsic.at<double>(0, 2) << endl << "cy :" << intrinsic.at<double>(1, 2) << endl;  
  125.   
  126.     cout << "k1 :" << distortion_coeff.at<double>(0, 0) << endl;  
  127.     cout << "k2 :" << distortion_coeff.at<double>(1, 0) << endl;  
  128.     cout << "p1 :" << distortion_coeff.at<double>(2, 0) << endl;  
  129.     cout << "p2 :" << distortion_coeff.at<double>(3, 0) << endl;  
  130.     cout << "p3 :" << distortion_coeff.at<double>(4, 0) << endl;  
  131. }  
  132.   
  133. //int _tmain(int argc, _TCHAR* argv[])  
  134. int main()  
  135. {  
  136.     Mat img;  
  137.     int goodFrameCount = 0;  
  138.     namedWindow("chessboard");  
  139.     cout << "按Q退出 ..." << endl;  
  140.     while (goodFrameCount < frameNumber)  
  141.     {  
  142.           
  143.         char filename[100];  
  144.         sprintf_s(filename, "chao%d.bmp", goodFrameCount);  
  145.         //sprintf_s(filename, "chess%d.jpg", goodFrameCount);  
  146.         goodFrameCount++;  
  147.           
  148.         rgbImage = imread(filename, 1);  
  149.         cvtColor(rgbImage, grayImage, CV_BGR2GRAY);  
  150.         imshow("Camera", grayImage);  
  151.   
  152.         bool isFind = findChessboardCorners(rgbImage, boardSize, corner, 0);  
  153.         //bool isFind = findChessboardCorners(rgbImage, boardSize, corner, CV_CALIB_CB_NORMALIZE_IMAGE);  
  154.         if (isFind == true//所有角点都被找到 说明这幅图像是可行的    
  155.         {  
  156.             /* 
  157.             Size(5,5) 搜索窗口的一半大小 
  158.             Size(-1,-1) 死区的一半尺寸 
  159.             TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 0.1)迭代终止条件 
  160.             */  
  161.             cornerSubPix(grayImage, corner, Size(5, 5), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 0.1));  
  162.             drawChessboardCorners(rgbImage, boardSize, corner, isFind);  
  163.             imshow("chessboard", rgbImage);  
  164.             corners.push_back(corner);  
  165.             //string filename = "res\\image\\calibration";    
  166.             //filename += goodFrameCount + ".jpg";    
  167.             //cvSaveImage(filename.c_str(), &IplImage(rgbImage));       //把合格的图片保存起来    
  168.   
  169.             cout << "The image is good" << endl;  
  170.         }  
  171.         else  
  172.         {  
  173.             cout << "The image is bad please try again" << endl;  
  174.         }  
  175.         //  cout << "Press any key to continue..." << endl;    
  176.         //  waitKey(0);    
  177.   
  178.         if (waitKey(10) == 'q')  
  179.         {  
  180.             break;  
  181.         }  
  182.         //  imshow("chessboard", rgbImage);    
  183.     }  
  184.   
  185.     /* 
  186.     图像采集完毕 接下来开始摄像头的校正 
  187.     calibrateCamera() 
  188.     输入参数 objectPoints  角点的实际物理坐标 
  189.     imagePoints   角点的图像坐标 
  190.     imageSize     图像的大小 
  191.     输出参数 
  192.     cameraMatrix  相机的内参矩阵 
  193.     distCoeffs    相机的畸变参数 
  194.     rvecs         旋转矢量(外参数) 
  195.     tvecs         平移矢量(外参数) 
  196.     */  
  197.   
  198.     /*设置实际初始参数 根据calibrateCamera来 如果flag = 0 也可以不进行设置*/  
  199.     guessCameraParam();  
  200.     cout << "guess successful" << endl;  
  201.     /*计算实际的校正点的三维坐标*/  
  202.     calRealPoint(objRealPoint, boardWidth, boardHeight, frameNumber, squareSize);  
  203.     cout << "cal real successful" << endl;  
  204.     /*标定摄像头*/  
  205.     calibrateCamera(objRealPoint, corners, Size(imageWidth, imageHeight), intrinsic, distortion_coeff, rvecs, tvecs, 0);  
  206.     cout << "calibration successful" << endl;  
  207.     /*保存并输出参数*/  
  208.     outputCameraParam();  
  209.     CalibrationEvaluate();  
  210.     cout << "out successful" << endl;  
  211.   
  212.     /*显示畸变校正效果*/  
  213.     Mat cImage;  
  214.     undistort(rgbImage, cImage, intrinsic, distortion_coeff);  
  215.     imshow("Corret Image", cImage);  
  216.     cout << "Correct Image" << endl;  
  217.     cout << "Wait for Key" << endl;  
  218.   
  219.     waitKey(0);  
  220.     system("pause");  
  221.     return 0;  
  222. }  


以上只贴出了部分代码,完整的C++代码,可到 Hust平凡之路相机标定工程 下载。

你可能感兴趣的:(视觉-opencv)