【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现

转载 双目相机标定以及立体测距原理及OpenCV实现

http://blog.csdn.net/dcrmg/article/details/52986522?locationNum=15&fps=1

单目相机标定的目标是获取相机的内参和外参,内参(1/dx,1/dy,Cx,Cy,f)表征了相机的内部结构参数,外参是相机的旋转矩阵R和平移向量t。内参中dx和dy是相机单个感光单元芯片的长度和宽度,是一个物理尺寸,有时候会有dx=dy,这时候感光单元是一个正方形。Cx和Cy分别代表相机感光芯片的中心点在x和y方向上可能存在的偏移,因为芯片在安装到相机模组上的时候,由于制造精度和组装工艺的影响,很难做到中心完全重合。f代表相机的焦距。

双目标定的第一步需要分别获取左右相机的内外参数,之后通过立体标定对左右两幅图像进行立体校准和对其,最后就是确定两个相机的相对位置关系,即中心距。

首先看一下双目测距的基本原理:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第1张图片【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第2张图片


假设有一个点p,沿着垂直于相机中心连线方向上下移动,则其在左右相机上的成像点的位置会不断变化,即d=x1-x2的大小不断变化,并且点p和相机之间的距离Z跟视差d存在着反比关系。上式中视差d可以通过两个相机中心距T减去p点分别在左右图像上的投影点偏离中心点的值获得,所以只要获取到了两个相机的中心距T,就可以评估出p点距离相机的距离,这个中心距T也是双目标定中需要确立的参数之一。

当然这一切有一个前提就是要在两个相机成像上定位到同一个点p上,就是要把左右两个图片的点匹配起来,这就涉及到双目校正的动作。如果通过一幅图片上一个点的特征在另一个二维图像空间上匹配对应点,这个过程会非常耗时。为了减少匹配搜索的运算量,我们可以利用极限约束使得对应点的匹配由二维搜索空间降到一维搜索空间。


【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第3张图片

这时候要用双目校正把消除畸变后的两幅图像在水平方向严格的对齐,使得两幅图像的对极线恰好在同一水平线上,这样一幅图像上任意一点与其在另一幅图像上的匹配点就必然具有相同的行号,只需要在该行进行一维搜索就可匹配到对应点。

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第4张图片


下边Opencv双目相机校正的代码是在自带的程序stereo_calib.cpp基础上修改的,位置在“XX\opencv\sources\samples\cpp\”,使用时拷贝目录下的26张图片和stereo_calib.xml到当前工程目录下,并在工程调试->命令参数里设置参数为:StereoCalibration -w 9 -h 6 stereo_calib.xml


[cpp]  view plain  copy
 print ?
  1. #include "opencv2/calib3d/calib3d.hpp"  
  2. #include "opencv2/highgui/highgui.hpp"  
  3. #include "opencv2/imgproc/imgproc.hpp"  
  4.   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11. #include   
  12. #include   
  13.   
  14. using namespace cv;  
  15. using namespace std;  
  16.   
  17. static void StereoCalib(const vector& imagelist, Size boardSize, bool useCalibrated=truebool showRectified=true)  
  18. {  
  19.     if( imagelist.size() % 2 != 0 )  
  20.     {  
  21.         cout << "Error: the image list contains odd (non-even) number of elements\n";  
  22.         return;  
  23.     }  
  24.   
  25.     bool displayCorners = true;//true;  
  26.     const int maxScale = 2;  
  27.     const float squareSize = 1.f;  // Set this to your actual square size  
  28.     // ARRAY AND VECTOR STORAGE:  
  29.   
  30.     vector > imagePoints[2];  
  31.     vector > objectPoints;  
  32.     Size imageSize;  
  33.   
  34.     int i, j, k, nimages = (int)imagelist.size()/2;  
  35.   
  36.     imagePoints[0].resize(nimages);  
  37.     imagePoints[1].resize(nimages);  
  38.     vector goodImageList;  
  39.   
  40.     for( i = j = 0; i < nimages; i++ )  
  41.     {  
  42.         for( k = 0; k < 2; k++ )  
  43.         {  
  44.             const string& filename = imagelist[i*2+k];  
  45.             Mat img = imread(filename, 0);  
  46.             if(img.empty())  
  47.                 break;  
  48.             if( imageSize == Size() )  
  49.                 imageSize = img.size();  
  50.             else if( img.size() != imageSize )  
  51.             {  
  52.                 cout << "The image " << filename << " has the size different from the first image size. Skipping the pair\n";  
  53.                 break;  
  54.             }  
  55.             bool found = false;  
  56.             vector& corners = imagePoints[k][j];  
  57.             forint scale = 1; scale <= maxScale; scale++ )  
  58.             {  
  59.                 Mat timg;  
  60.                 if( scale == 1 )  
  61.                     timg = img;  
  62.                 else  
  63.                     resize(img, timg, Size(), scale, scale);  
  64.                 found = findChessboardCorners(timg, boardSize, corners,  
  65.                     CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE);  
  66.                 if( found )  
  67.                 {  
  68.                     if( scale > 1 )  
  69.                     {  
  70.                         Mat cornersMat(corners);  
  71.                         cornersMat *= 1./scale;  
  72.                     }  
  73.                     break;  
  74.                 }  
  75.             }  
  76.             if( displayCorners )  
  77.             {  
  78.                 cout << filename << endl;  
  79.                 Mat cimg, cimg1;  
  80.                 cvtColor(img, cimg, COLOR_GRAY2BGR);  
  81.                 drawChessboardCorners(cimg, boardSize, corners, found);  
  82.                 double sf = 640./MAX(img.rows, img.cols);  
  83.                 resize(cimg, cimg1, Size(), sf, sf);  
  84.                 imshow("corners", cimg1);                 
  85.                 char c = (char)waitKey(500);  
  86.                 if( c == 27 || c == 'q' || c == 'Q' ) //Allow ESC to quit  
  87.                     exit(-1);  
  88.             }  
  89.             else  
  90.                 putchar('.');  
  91.             if( !found )  
  92.                 break;  
  93.             cornerSubPix(img, corners, Size(11,11), Size(-1,-1),  
  94.                 TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,  
  95.                 30, 0.01));  
  96.         }  
  97.         if( k == 2 )  
  98.         {  
  99.             goodImageList.push_back(imagelist[i*2]);  
  100.             goodImageList.push_back(imagelist[i*2+1]);  
  101.             j++;  
  102.         }  
  103.     }  
  104.     cout << j << " pairs have been successfully detected.\n";  
  105.     nimages = j;  
  106.     if( nimages < 2 )  
  107.     {  
  108.         cout << "Error: too little pairs to run the calibration\n";  
  109.         return;  
  110.     }  
  111.   
  112.     imagePoints[0].resize(nimages);  
  113.     imagePoints[1].resize(nimages);  
  114.     objectPoints.resize(nimages);  
  115.   
  116.     for( i = 0; i < nimages; i++ )  
  117.     {  
  118.         for( j = 0; j < boardSize.height; j++ )  
  119.             for( k = 0; k < boardSize.width; k++ )  
  120.                 objectPoints[i].push_back(Point3f(k*squareSize, j*squareSize, 0));  
  121.     }  
  122.   
  123.     cout << "Running stereo calibration ...\n";  
  124.   
  125.     Mat cameraMatrix[2], distCoeffs[2];  
  126.     cameraMatrix[0] = Mat::eye(3, 3, CV_64F);  
  127.     cameraMatrix[1] = Mat::eye(3, 3, CV_64F);  
  128.     Mat R, T, E, F;  
  129.   
  130.     double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],  
  131.         cameraMatrix[0], distCoeffs[0],  
  132.         cameraMatrix[1], distCoeffs[1],  
  133.         imageSize, R, T, E, F,  
  134.         TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 100, 1e-5),  
  135.         CV_CALIB_FIX_ASPECT_RATIO +  
  136.         CV_CALIB_ZERO_TANGENT_DIST +  
  137.         CV_CALIB_SAME_FOCAL_LENGTH +  
  138.         CV_CALIB_RATIONAL_MODEL +  
  139.         CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5);  
  140.     cout << "done with RMS error=" << rms << endl;  
  141.   
  142.     // CALIBRATION QUALITY CHECK  
  143.     // because the output fundamental matrix implicitly  
  144.     // includes all the output information,  
  145.     // we can check the quality of calibration using the  
  146.     // epipolar geometry constraint: m2^t*F*m1=0  
  147.     double err = 0;  
  148.     int npoints = 0;  
  149.     vector lines[2];  
  150.     for( i = 0; i < nimages; i++ )  
  151.     {  
  152.         int npt = (int)imagePoints[0][i].size();  
  153.         Mat imgpt[2];  
  154.         for( k = 0; k < 2; k++ )  
  155.         {  
  156.             imgpt[k] = Mat(imagePoints[k][i]);  
  157.             undistortPoints(imgpt[k], imgpt[k], cameraMatrix[k], distCoeffs[k], Mat(), cameraMatrix[k]);  
  158.             computeCorrespondEpilines(imgpt[k], k+1, F, lines[k]);  
  159.         }  
  160.         for( j = 0; j < npt; j++ )  
  161.         {  
  162.             double errij = fabs(imagePoints[0][i][j].x*lines[1][j][0] +  
  163.                 imagePoints[0][i][j].y*lines[1][j][1] + lines[1][j][2]) +  
  164.                 fabs(imagePoints[1][i][j].x*lines[0][j][0] +  
  165.                 imagePoints[1][i][j].y*lines[0][j][1] + lines[0][j][2]);  
  166.             err += errij;  
  167.         }  
  168.         npoints += npt;  
  169.     }  
  170.     cout << "average reprojection err = " <<  err/npoints << endl;  
  171.   
  172.     // save intrinsic parameters  
  173.     FileStorage fs("intrinsics.yml", CV_STORAGE_WRITE);  
  174.     if( fs.isOpened() )  
  175.     {  
  176.         fs << "M1" << cameraMatrix[0] << "D1" << distCoeffs[0] <<  
  177.             "M2" << cameraMatrix[1] << "D2" << distCoeffs[1];  
  178.         fs.release();  
  179.     }  
  180.     else  
  181.         cout << "Error: can not save the intrinsic parameters\n";  
  182.   
  183.     Mat R1, R2, P1, P2, Q;  
  184.     Rect validRoi[2];  
  185.   
  186.     stereoRectify(cameraMatrix[0], distCoeffs[0],  
  187.         cameraMatrix[1], distCoeffs[1],  
  188.         imageSize, R, T, R1, R2, P1, P2, Q,  
  189.         CALIB_ZERO_DISPARITY, 1, imageSize, &validRoi[0], &validRoi[1]);  
  190.   
  191.     fs.open("extrinsics.yml", CV_STORAGE_WRITE);  
  192.     if( fs.isOpened() )  
  193.     {  
  194.         fs << "R" << R << "T" << T << "R1" << R1 << "R2" << R2 << "P1" << P1 << "P2" << P2 << "Q" << Q;  
  195.         fs.release();  
  196.     }  
  197.     else  
  198.         cout << "Error: can not save the extrinsic parameters\n";  
  199.   
  200.     // OpenCV can handle left-right  
  201.     // or up-down camera arrangements  
  202.     bool isVerticalStereo = fabs(P2.at<double>(1, 3)) > fabs(P2.at<double>(0, 3));  
  203.   
  204.     // COMPUTE AND DISPLAY RECTIFICATION  
  205.     if( !showRectified )  
  206.         return;  
  207.   
  208.     Mat rmap[2][2];  
  209.     // IF BY CALIBRATED (BOUGUET'S METHOD)  
  210.     if( useCalibrated )  
  211.     {  
  212.         // we already computed everything  
  213.     }  
  214.     // OR ELSE HARTLEY'S METHOD  
  215.     else  
  216.         // use intrinsic parameters of each camera, but  
  217.         // compute the rectification transformation directly  
  218.         // from the fundamental matrix  
  219.     {  
  220.         vector allimgpt[2];  
  221.         for( k = 0; k < 2; k++ )  
  222.         {  
  223.             for( i = 0; i < nimages; i++ )  
  224.                 std::copy(imagePoints[k][i].begin(), imagePoints[k][i].end(), back_inserter(allimgpt[k]));  
  225.         }  
  226.         F = findFundamentalMat(Mat(allimgpt[0]), Mat(allimgpt[1]), FM_8POINT, 0, 0);  
  227.         Mat H1, H2;  
  228.         stereoRectifyUncalibrated(Mat(allimgpt[0]), Mat(allimgpt[1]), F, imageSize, H1, H2, 3);  
  229.   
  230.         R1 = cameraMatrix[0].inv()*H1*cameraMatrix[0];  
  231.         R2 = cameraMatrix[1].inv()*H2*cameraMatrix[1];  
  232.         P1 = cameraMatrix[0];  
  233.         P2 = cameraMatrix[1];  
  234.     }  
  235.   
  236.     //Precompute maps for cv::remap()  
  237.     initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);  
  238.     initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);  
  239.   
  240.     Mat canvas;  
  241.     double sf;  
  242.     int w, h;  
  243.     if( !isVerticalStereo )  
  244.     {  
  245.         sf = 600./MAX(imageSize.width, imageSize.height);  
  246.         w = cvRound(imageSize.width*sf);  
  247.         h = cvRound(imageSize.height*sf);  
  248.         canvas.create(h, w*2, CV_8UC3);  
  249.     }  
  250.     else  
  251.     {  
  252.         sf = 300./MAX(imageSize.width, imageSize.height);  
  253.         w = cvRound(imageSize.width*sf);  
  254.         h = cvRound(imageSize.height*sf);  
  255.         canvas.create(h*2, w, CV_8UC3);  
  256.     }  
  257.   
  258.     for( i = 0; i < nimages; i++ )  
  259.     {  
  260.         for( k = 0; k < 2; k++ )  
  261.         {  
  262.             Mat img = imread(goodImageList[i*2+k], 0), rimg, cimg;  
  263.             remap(img, rimg, rmap[k][0], rmap[k][1], CV_INTER_LINEAR);  
  264.             imshow("单目相机校正",rimg);  
  265.             waitKey();  
  266.             cvtColor(rimg, cimg, COLOR_GRAY2BGR);  
  267.             Mat canvasPart = !isVerticalStereo ? canvas(Rect(w*k, 0, w, h)) : canvas(Rect(0, h*k, w, h));  
  268.             resize(cimg, canvasPart, canvasPart.size(), 0, 0, CV_INTER_AREA);  
  269.             if( useCalibrated )  
  270.             {  
  271.                 Rect vroi(cvRound(validRoi[k].x*sf), cvRound(validRoi[k].y*sf),  
  272.                     cvRound(validRoi[k].width*sf), cvRound(validRoi[k].height*sf));  
  273.                 rectangle(canvasPart, vroi, Scalar(0,0,255), 3, 8);  
  274.             }  
  275.         }  
  276.   
  277.         if( !isVerticalStereo )  
  278.             for( j = 0; j < canvas.rows; j += 16 )  
  279.                 line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);  
  280.         else  
  281.             for( j = 0; j < canvas.cols; j += 16 )  
  282.                 line(canvas, Point(j, 0), Point(j, canvas.rows), Scalar(0, 255, 0), 1, 8);  
  283.         imshow("双目相机校正对齐", canvas);  
  284.         waitKey();  
  285.         char c = (char)waitKey();  
  286.         if( c == 27 || c == 'q' || c == 'Q' )  
  287.             break;  
  288.     }  
  289. }  
  290.   
  291.   
  292. static bool readStringList( const string& filename, vector& l )  
  293. {  
  294.     l.resize(0);  
  295.     FileStorage fs(filename, FileStorage::READ);  
  296.     if( !fs.isOpened() )  
  297.         return false;  
  298.     FileNode n = fs.getFirstTopLevelNode();  
  299.     if( n.type() != FileNode::SEQ )  
  300.         return false;  
  301.     FileNodeIterator it = n.begin(), it_end = n.end();  
  302.     for( ; it != it_end; ++it )  
  303.         l.push_back((string)*it);  
  304.     return true;  
  305. }  
  306.   
  307. int main(int argc, char** argv)  
  308. {  
  309.     Size boardSize;  
  310.     string imagelistfn;  
  311.     bool showRectified = true;  
  312.   
  313.     forint i = 1; i < argc; i++ )  
  314.     {  
  315.         if( string(argv[i]) == "-w" )  
  316.         {  
  317.             if( sscanf(argv[++i], "%d", &boardSize.width) != 1 || boardSize.width <= 0 )  
  318.             {  
  319.                 cout << "invalid board width" << endl;  
  320.                 return -1;  
  321.             }  
  322.         }  
  323.         else if( string(argv[i]) == "-h" )  
  324.         {  
  325.             if( sscanf(argv[++i], "%d", &boardSize.height) != 1 || boardSize.height <= 0 )  
  326.             {  
  327.                 cout << "invalid board height" << endl;  
  328.                 return -1;  
  329.             }  
  330.         }  
  331.         else if( string(argv[i]) == "-nr" )  
  332.             showRectified = false;  
  333.         else if( string(argv[i]) == "--help" )  
  334.             return -1;  
  335.         else if( argv[i][0] == '-' )  
  336.         {  
  337.             cout << "invalid option " << argv[i] << endl;  
  338.             return 0;  
  339.         }  
  340.         else  
  341.             imagelistfn = argv[i];  
  342.     }  
  343.   
  344.     if( imagelistfn == "" )  
  345.     {  
  346.         imagelistfn = "stereo_calib.xml";  
  347.         boardSize = Size(9, 6);  
  348.     }  
  349.     else if( boardSize.width <= 0 || boardSize.height <= 0 )  
  350.     {  
  351.         cout << "if you specified XML file with chessboards, you should also specify the board width and height (-w and -h options)" << endl;  
  352.         return 0;  
  353.     }  
  354.   
  355.     vector imagelist;  
  356.     bool ok = readStringList(imagelistfn, imagelist);  
  357.     if(!ok || imagelist.empty())  
  358.     {  
  359.         cout << "can not open " << imagelistfn << " or the string list is empty" << endl;  
  360.         return -1;  
  361.     }  
  362.   
  363.     StereoCalib(imagelist, boardSize, true, showRectified);  
  364.     return 0;  
  365. }  


右相机其中一个标定图片查找到的角点:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第5张图片


左相机其中一个标定图片查找到的角点:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第6张图片


右相机单目校正:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第7张图片


左相机单目校正:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第8张图片


左右相机双目立体校正:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第9张图片


双目相机标定后,可以看到左右相机对应匹配点基本上已经水平对齐。


之后在该程序基础上运行stereo_match.cpp,求左右相机的视差。同样工程调试->命令参数里设置参数为:left01.jpg right01.jpg --algorithm=bm -i intrinsics.yml -e extrinsics.yml:


[cpp]  view plain  copy
 print ?
  1. #include "opencv2/calib3d/calib3d.hpp"  
  2. #include "opencv2/imgproc/imgproc.hpp"  
  3. #include "opencv2/highgui/highgui.hpp"  
  4. #include "opencv2/contrib/contrib.hpp"  
  5.   
  6. #include   
  7.   
  8. using namespace cv;  
  9.   
  10. static void saveXYZ(const char* filename, const Mat& mat)  
  11. {  
  12.     const double max_z = 1.0e4;  
  13.     FILE* fp = fopen(filename, "wt");  
  14.     for(int y = 0; y < mat.rows; y++)  
  15.     {  
  16.         for(int x = 0; x < mat.cols; x++)  
  17.         {  
  18.             Vec3f point = mat.at(y, x);  
  19.             if(fabs(point[2] - max_z) < FLT_EPSILON || fabs(point[2]) > max_z) continue;  
  20.             fprintf(fp, "%f %f %f\n", point[0], point[1], point[2]);  
  21.         }  
  22.     }  
  23.     fclose(fp);  
  24. }  
  25.   
  26. int main(int argc, char** argv)  
  27. {  
  28.     const char* algorithm_opt = "--algorithm=";  
  29.     const char* maxdisp_opt = "--max-disparity=";  
  30.     const char* blocksize_opt = "--blocksize=";  
  31.     const char* nodisplay_opt = "--no-display";  
  32.     const char* scale_opt = "--scale=";  
  33.   
  34.     if(argc < 3)  
  35.     {  
  36.         return 0;  
  37.     }  
  38.     const char* img1_filename = 0;  
  39.     const char* img2_filename = 0;  
  40.     const char* intrinsic_filename = 0;  
  41.     const char* extrinsic_filename = 0;  
  42.     const char* disparity_filename = 0;  
  43.     const char* point_cloud_filename = 0;  
  44.   
  45.     enum { STEREO_BM=0, STEREO_SGBM=1, STEREO_HH=2, STEREO_VAR=3 };  
  46.     int alg = STEREO_SGBM;  
  47.     int SADWindowSize = 0, numberOfDisparities = 0;  
  48.     bool no_display = false;  
  49.     float scale = 1.f;  
  50.   
  51.     StereoBM bm;  
  52.     StereoSGBM sgbm;  
  53.     StereoVar var;  
  54.   
  55.     forint i = 1; i < argc; i++ )  
  56.     {  
  57.         if( argv[i][0] != '-' )  
  58.         {  
  59.             if( !img1_filename )  
  60.                 img1_filename = argv[i];  
  61.             else  
  62.                 img2_filename = argv[i];  
  63.         }  
  64.         else if( strncmp(argv[i], algorithm_opt, strlen(algorithm_opt)) == 0 )  
  65.         {  
  66.             char* _alg = argv[i] + strlen(algorithm_opt);  
  67.             alg = strcmp(_alg, "bm") == 0 ? STEREO_BM :  
  68.                   strcmp(_alg, "sgbm") == 0 ? STEREO_SGBM :  
  69.                   strcmp(_alg, "hh") == 0 ? STEREO_HH :  
  70.                   strcmp(_alg, "var") == 0 ? STEREO_VAR : -1;  
  71.             if( alg < 0 )  
  72.             {  
  73.                 printf("Command-line parameter error: Unknown stereo algorithm\n\n");  
  74.                 return -1;  
  75.             }  
  76.         }  
  77.         else if( strncmp(argv[i], maxdisp_opt, strlen(maxdisp_opt)) == 0 )  
  78.         {  
  79.             if( sscanf( argv[i] + strlen(maxdisp_opt), "%d", &numberOfDisparities ) != 1 ||  
  80.                 numberOfDisparities < 1 || numberOfDisparities % 16 != 0 )  
  81.             {  
  82.                 printf("Command-line parameter error: The max disparity (--maxdisparity=<...>) must be a positive integer divisible by 16\n");  
  83.                  
  84.                 return -1;  
  85.             }  
  86.         }  
  87.         else if( strncmp(argv[i], blocksize_opt, strlen(blocksize_opt)) == 0 )  
  88.         {  
  89.             if( sscanf( argv[i] + strlen(blocksize_opt), "%d", &SADWindowSize ) != 1 ||  
  90.                 SADWindowSize < 1 || SADWindowSize % 2 != 1 )  
  91.             {  
  92.                 printf("Command-line parameter error: The block size (--blocksize=<...>) must be a positive odd number\n");  
  93.                 return -1;  
  94.             }  
  95.         }  
  96.         else if( strncmp(argv[i], scale_opt, strlen(scale_opt)) == 0 )  
  97.         {  
  98.             if( sscanf( argv[i] + strlen(scale_opt), "%f", &scale ) != 1 || scale < 0 )  
  99.             {  
  100.                 printf("Command-line parameter error: The scale factor (--scale=<...>) must be a positive floating-point number\n");  
  101.                 return -1;  
  102.             }  
  103.         }  
  104.         else if( strcmp(argv[i], nodisplay_opt) == 0 )  
  105.             no_display = true;  
  106.         else if( strcmp(argv[i], "-i" ) == 0 )  
  107.             intrinsic_filename = argv[++i];  
  108.         else if( strcmp(argv[i], "-e" ) == 0 )  
  109.             extrinsic_filename = argv[++i];  
  110.         else if( strcmp(argv[i], "-o" ) == 0 )  
  111.             disparity_filename = argv[++i];  
  112.         else if( strcmp(argv[i], "-p" ) == 0 )  
  113.             point_cloud_filename = argv[++i];  
  114.         else  
  115.         {  
  116.             printf("Command-line parameter error: unknown option %s\n", argv[i]);  
  117.             return -1;  
  118.         }  
  119.     }  
  120.   
  121.     if( !img1_filename || !img2_filename )  
  122.     {  
  123.         printf("Command-line parameter error: both left and right images must be specified\n");  
  124.         return -1;  
  125.     }  
  126.   
  127.     if( (intrinsic_filename != 0) ^ (extrinsic_filename != 0) )  
  128.     {  
  129.         printf("Command-line parameter error: either both intrinsic and extrinsic parameters must be specified, or none of them (when the stereo pair is already rectified)\n");  
  130.         return -1;  
  131.     }  
  132.   
  133.     if( extrinsic_filename == 0 && point_cloud_filename )  
  134.     {  
  135.         printf("Command-line parameter error: extrinsic and intrinsic parameters must be specified to compute the point cloud\n");  
  136.         return -1;  
  137.     }  
  138.   
  139.     int color_mode = alg == STEREO_BM ? 0 : -1;  
  140.     Mat img1 = imread(img1_filename, color_mode);  
  141.     Mat img2 = imread(img2_filename, color_mode);  
  142.   
  143.     if (img1.empty())  
  144.     {  
  145.         printf("Command-line parameter error: could not load the first input image file\n");  
  146.         return -1;  
  147.     }  
  148.     if (img2.empty())  
  149.     {  
  150.         printf("Command-line parameter error: could not load the second input image file\n");  
  151.         return -1;  
  152.     }  
  153.   
  154.     if (scale != 1.f)  
  155.     {  
  156.         Mat temp1, temp2;  
  157.         int method = scale < 1 ? INTER_AREA : INTER_CUBIC;  
  158.         resize(img1, temp1, Size(), scale, scale, method);  
  159.         img1 = temp1;  
  160.         resize(img2, temp2, Size(), scale, scale, method);  
  161.         img2 = temp2;  
  162.     }  
  163.   
  164.     Size img_size = img1.size();  
  165.   
  166.     Rect roi1, roi2;  
  167.     Mat Q;  
  168.   
  169.     if( intrinsic_filename )  
  170.     {  
  171.         // reading intrinsic parameters  
  172.         FileStorage fs(intrinsic_filename, CV_STORAGE_READ);  
  173.         if(!fs.isOpened())  
  174.         {  
  175.             printf("Failed to open file %s\n", intrinsic_filename);  
  176.             return -1;  
  177.         }  
  178.   
  179.         Mat M1, D1, M2, D2;  
  180.         fs["M1"] >> M1;  
  181.         fs["D1"] >> D1;  
  182.         fs["M2"] >> M2;  
  183.         fs["D2"] >> D2;  
  184.   
  185.         M1 *= scale;  
  186.         M2 *= scale;  
  187.   
  188.         fs.open(extrinsic_filename, CV_STORAGE_READ);  
  189.         if(!fs.isOpened())  
  190.         {  
  191.             printf("Failed to open file %s\n", extrinsic_filename);  
  192.             return -1;  
  193.         }  
  194.   
  195.         Mat R, T, R1, P1, R2, P2;  
  196.         fs["R"] >> R;  
  197.         fs["T"] >> T;  
  198.   
  199.         stereoRectify( M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, -1, img_size, &roi1, &roi2 );  
  200.   
  201.         Mat map11, map12, map21, map22;  
  202.         initUndistortRectifyMap(M1, D1, R1, P1, img_size, CV_16SC2, map11, map12);  
  203.         initUndistortRectifyMap(M2, D2, R2, P2, img_size, CV_16SC2, map21, map22);  
  204.   
  205.         Mat img1r, img2r;  
  206.         remap(img1, img1r, map11, map12, INTER_LINEAR);  
  207.         remap(img2, img2r, map21, map22, INTER_LINEAR);  
  208.   
  209.         img1 = img1r;         
  210.         img2 = img2r;  
  211.     }  
  212.   
  213.     numberOfDisparities = numberOfDisparities > 0 ? numberOfDisparities : ((img_size.width/8) + 15) & -16;  
  214.   
  215.     bm.state->roi1 = roi1;  
  216.     bm.state->roi2 = roi2;  
  217.     bm.state->preFilterCap = 31;  
  218.     bm.state->SADWindowSize = SADWindowSize > 0 ? SADWindowSize : 9;  
  219.     bm.state->minDisparity = 0;  
  220.     bm.state->numberOfDisparities = numberOfDisparities;  
  221.     bm.state->textureThreshold = 10;  
  222.     bm.state->uniquenessRatio = 15;  
  223.     bm.state->speckleWindowSize = 100;  
  224.     bm.state->speckleRange = 32;  
  225.     bm.state->disp12MaxDiff = 1;  
  226.   
  227.     sgbm.preFilterCap = 63;  
  228.     sgbm.SADWindowSize = SADWindowSize > 0 ? SADWindowSize : 3;  
  229.   
  230.     int cn = img1.channels();  
  231.   
  232.     sgbm.P1 = 8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;  
  233.     sgbm.P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;  
  234.     sgbm.minDisparity = 0;  
  235.     sgbm.numberOfDisparities = numberOfDisparities;  
  236.     sgbm.uniquenessRatio = 10;  
  237.     sgbm.speckleWindowSize = bm.state->speckleWindowSize;  
  238.     sgbm.speckleRange = bm.state->speckleRange;  
  239.     sgbm.disp12MaxDiff = 1;  
  240.     sgbm.fullDP = alg == STEREO_HH;  
  241.   
  242.     var.levels = 3;                                 // ignored with USE_AUTO_PARAMS  
  243.     var.pyrScale = 0.5;                             // ignored with USE_AUTO_PARAMS  
  244.     var.nIt = 25;  
  245.     var.minDisp = -numberOfDisparities;  
  246.     var.maxDisp = 0;  
  247.     var.poly_n = 3;  
  248.     var.poly_sigma = 0.0;  
  249.     var.fi = 15.0f;  
  250.     var.lambda = 0.03f;  
  251.     var.penalization = var.PENALIZATION_TICHONOV;   // ignored with USE_AUTO_PARAMS  
  252.     var.cycle = var.CYCLE_V;                        // ignored with USE_AUTO_PARAMS  
  253.     var.flags = var.USE_SMART_ID | var.USE_AUTO_PARAMS | var.USE_INITIAL_DISPARITY | var.USE_MEDIAN_FILTERING ;  
  254.   
  255.     Mat disp, disp8;  
  256.     //Mat img1p, img2p, dispp;  
  257.     //copyMakeBorder(img1, img1p, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);  
  258.     //copyMakeBorder(img2, img2p, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);  
  259.   
  260.     int64 t = getTickCount();  
  261.     if( alg == STEREO_BM )  
  262.         bm(img1, img2, disp);  
  263.     else if( alg == STEREO_VAR ) {  
  264.         var(img1, img2, disp);  
  265.     }  
  266.     else if( alg == STEREO_SGBM || alg == STEREO_HH )  
  267.         sgbm(img1, img2, disp);  
  268.     t = getTickCount() - t;  
  269.     printf("Time elapsed: %fms\n", t*1000/getTickFrequency());  
  270.   
  271.     //disp = dispp.colRange(numberOfDisparities, img1p.cols);  
  272.     waitKey();  
  273.     if( alg != STEREO_VAR )  
  274.         disp.convertTo(disp8, CV_8U, 255/(numberOfDisparities*16.));  
  275.     else  
  276.         disp.convertTo(disp8, CV_8U);  
  277.     if( !no_display )  
  278.     {  
  279.         namedWindow("左相机", 0);  
  280.         imshow("左相机", img1);  
  281.         namedWindow("右相机", 0);  
  282.         imshow("右相机", img2);         
  283.         imshow("左右相机视差图", disp8);  
  284.         printf("press any key to continue...");  
  285.         fflush(stdout);  
  286.         waitKey();  
  287.         printf("\n");  
  288.     }  
  289.   
  290.     if(disparity_filename)  
  291.         imwrite(disparity_filename, disp8);  
  292.   
  293.     if(point_cloud_filename)  
  294.     {  
  295.         printf("storing the point cloud...");  
  296.         fflush(stdout);  
  297.         Mat xyz;  
  298.         reprojectImageTo3D(disp, xyz, Q, true);  
  299.         saveXYZ(point_cloud_filename, xyz);  
  300.         printf("\n");  
  301.     }  
  302.   
  303.     return 0;  
  304. }  

左右相机校正效果:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第10张图片


左右相机视差图:

【计算机视觉】 双目相机标定以及立体测距原理及OpenCV实现_第11张图片


视差用亮度表示,越亮表示当前位置距离相机越远。

你可能感兴趣的:(计算机视觉,图像处理)