opencv 双目摄像头标定

来源:互联网 发布:mac d for danger 编辑:程序博客网 时间:2024/04/26 20:37

我用的是opencv官方的例程,花了一天把代码大致注释了下,但是双目感觉好难,很多地方都不懂,都是在大佬们的博客找的资料

下面是对例程使用的说明

http://blog.csdn.net/t247555529/article/details/48046859

双目摄像头矫正就是为了

  • 极点在无穷远处
  • 对应的极线水平对齐
  • 旋转左右相机使得他们看起来是在一个平面上面的,并且使得他们对应的极线是水平对其的,最后进行scale缩放使得水平的畸变最小

可以自己拍摄13张左相机,13张右相机图片进行代替(ps:图片名称需要和.xml文件中的对应)

OpenCV的摄像机标定采用通用的棋盘标定模板,先用函数findChessboardCorners()粗略提取出棋盘的角点,然后再使用FindCornerSubPix()函数,进一步精确得到角点亚象素级的坐标值。

最后把坐标值代入stereoCalibrate()函数,得到摄像机的内外参数值。内参数是摄像机坐标系和理想坐标系之间的关系

4个内参(fx-焦距,fy,cx-对于x方向上光轴的偏移,cy)

5个畸变参数-3个径向(k1,k2,k3)-2个切向(p1,p2)

外参计算需要旋转参数(jiao1,jiao2,jiao3)和平移参数(Tx,Ty,Tz)

然后用undistortPoints函数对上面求得的相机内外参数和畸变进行处理,对角点进行校准

用stereoRectify函数对(标定过得相机进行校准)stereoCalibrate求得的

cameraMatrix[0],//第一个相机矩阵
distCoeffs[0],//第一个相机畸变参数
cameraMatrix[1],// 第二个相机矩阵
distCoeffs[1],//第二个相机畸变参数
imageSize,// 用于校正的图像大小.
R,// 第一和第二相机坐标系之间的旋转矩阵。
T,//第一和第二相机坐标系之间的平移矩阵. 

 R1,//输出第一个相机的3x3矫正变换(旋转矩阵) 
R2, //输出第二个相机的3x3矫正变换(旋转矩阵)

进行校准得到

 P1,//在第一台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵
 P2,//在第二台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵
Q,//输出深度视差映射矩阵

然后使用函数findFundamentalMat()计算两幅图像关联点的基础矩阵

用stereoRectifyUncalibrated函数是对未标定的摄像头进校准,由于上面进行校准所以这一步不会发生   大神对stereoRectifyUncalibrated介绍 http://blog.sina.com.cn/s/blog_4298002e01013yb8.html

initUndistortRectifyMap// 计算左右两幅图像的映射矩阵(畸变映射)

remap函数进行重映射,用remap来校准输入的左右图像--(通过initUndistortRectifyMap输出的畸变映射来矫正图像



代码:


#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"


#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


using namespace cv;
using namespace std;
//左图中的同一目标比右图中的同一目标偏右,即左右图命名不要弄反,与人眼视觉一致;
static int print_help()
{
    cout <<
            " Given a list of chessboard images, the number of corners (nx, ny)\n"//给出棋盘列表,角点个数
            " on the chessboards, and a flag: useCalibrated for \n"
            "   calibrated (0) or\n"
            "   uncalibrated \n"
            "     (1: use cvStereoCalibrate(), 2: compute fundamental\n"
            "         matrix separately) stereo. \n"
            " Calibrate the cameras and display the\n"
            " rectified results along with the computed disparity images.   \n" << endl;
    cout << "Usage:\n ./stereo_calib -w board_width -h board_height [-nr /*dot not view results*/] <image list XML/YML file>\n" << endl;
    return 0;
}




static void
StereoCalib(const vector<string>& imagelist, Size boardSize, bool useCalibrated=true, bool showRectified=true)
//输入图片向量,格子角点宽高
{
    if( imagelist.size() % 2 != 0 )//如果图片数不成双
    {
        cout << "Error: the image list contains odd (non-even) number of elements\n";
        return;
    }


    bool displayCorners = false;//true;
    const int maxScale = 2;
    const float squareSize = 29.f;  // Set this to your actual square size,设置真实方格大小 以毫米或者像素为单位的keypoint之间间隔距离,棋盘间隔为1
    // ARRAY AND VECTOR STORAGE:数组储存


    vector<vector<Point2f> > imagePoints[2];
    vector<vector<Point3f> > objectPoints;
    Size imageSize;


    int i, j, k, nimages = (int)imagelist.size()/2;//左右相分割,单个相机图片数,vector.size()是容器的数据个数


    imagePoints[0].resize(nimages);//设置向量大小
    imagePoints[1].resize(nimages);
    vector<string> goodImageList;


    for( i = j = 0; i < nimages; i++ )//0-13.
    {
        for( k = 0; k < 2; k++ )//左右
        {
            const string& filename = imagelist[i*2+k];//奇数读取左图片数组名,偶数右图像名
            Mat img = imread(filename, 0);////读取图像
            if(img.empty())
                break;
            if(imageSize == Size())//第一次输入时都是空,那么把第一张图的大小赋值
                imageSize = img.size();
            else if( img.size() != imageSize )//使输入的图像大小都一样
            {
                cout << "The image " << filename << " has the size different from the first image size. Skipping the pair\n";
                break;
            }
            bool found = false;
            vector<Point2f>& corners = imagePoints[k][j];//k=0左图所有角点,k=1右图所有角点,通过findChessboardCorners对向量传参,每一组左右图像用一个向量组(坐标是左右的平均处理)
//寻找角点,保存到imagePoints
            for( int scale = 1; scale <= maxScale; scale++ )//通过scale是来防止检测不到,调整图幅的在下面的if( scale > 1 )会用到,如果一次就检测到就不会进入
            {
                Mat timg;
                if( scale == 1 )
                    timg = img;
                else
                    resize(img, timg, Size(), scale, scale);
                found = findChessboardCorners(// 如果找到角点则返回true  
timg, // 输入的棋盘格图像(8UC1或8UC3)
boardSize, // 棋盘格内部角点的行、列数 
corners, // 输出的棋盘格角点
                    CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE);
                if( found )//若找到角点,find为真
                {
                    if( scale > 1 )
                    {
                        Mat cornersMat(corners);//把findChessboardCorners找到的vector角点转化为Mat矩阵,方便计算
                        cornersMat *= 1./scale;//若第一次没有找到角点,那么会通过resize函数的scale的fx,fy进行缩小,若第一次没收到,然后缩小
                    }
                    break;
                }
            }
////显示角点
            if( displayCorners )//displayCorners=false,永远不会进入
            {
                cout << filename << endl;
                Mat cimg, cimg1;
                cvtColor(img, cimg, COLOR_GRAY2BGR);
                drawChessboardCorners(cimg// 棋盘格图像(8UC3)即是输入也是输出 
, boardSize,// 棋盘格内部角点的行、列数
corners, // findChessboardCorners()输出的角点  
found); // findChessboardCorners()的返回值
                double sf = 640./MAX(img.rows, img.cols);
                resize(cimg, cimg1, Size(), sf, sf);
                imshow("corners", cimg1);
                char c = (char)waitKey(500);
                if( c == 27 || c == 'q' || c == 'Q' ) //Allow ESC to quit
                    exit(-1);
            }
            else
                putchar('.');
            if( !found )
                break;
//插值亚像素角点,用来精确得到的corners坐标参数
            cornerSubPix(img, // 输入图像 
corners, //输入角点的初始坐标以及精准化后的坐标用于输出。
Size(11,11),// 区域大小为 NXN; N=(winSize*2+1) ,搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小(5*2+1,*,5+1)=11*11为的搜索窗口将被使用。
Size(-1,-1),//Size(-1,-1)表示忽略 当值为 (-1,-1) 表示没有死区。
                         TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,
                                      30, 0.01//停止优化的标准,当迭代次数大于30或当角点坐标变化小于0.01时停止
 ));
        }
//上个for( k = 0; k < 2; k++ )循环结束,那么k==2了
        if( k == 2 )//对图像在做拷贝imagelist是传入的vector的string数组,包含的是图片的路径
        {
            goodImageList.push_back(imagelist[i*2]);
            goodImageList.push_back(imagelist[i*2+1]);
            j++;
        }
    }
    cout << j << " pairs have been successfully detected.\n";
    nimages = j;
    if( nimages < 2 )//如果图片数量少于一对,那么报错
    {
        cout << "Error: too little pairs to run the calibration\n";
        return;
    }


    imagePoints[0].resize(nimages);//把用亚像素处理过得坐标点放入imagePoints【0】,imagePoints【1】中分左右
    imagePoints[1].resize(nimages);
//计算角点的3d物理坐标
    objectPoints.resize(nimages);//定义Point3f的向量大小是nimages


    for( i = 0; i < nimages; i++ )
    {
        for(j = 0; j < boardSize.height; j++)
            for( k = 0; k < boardSize.width; k++ )
                objectPoints[i].push_back(Point3f(j*squareSize, k*squareSize, 0));//通过角点长宽个数以及squareSize每个角点的步长算出角点位置
    }


    cout << "Running stereo calibration ...\n";
//3d校正
    Mat cameraMatrix[2], distCoeffs[2];
    cameraMatrix[0] = Mat::eye(3, 3, CV_64F);//定义左相机3*3内参数矩阵
    cameraMatrix[1] = Mat::eye(3, 3, CV_64F);//定义右相机3*3内参数矩阵
    Mat R, T, E, F;


    double rms = stereoCalibrate(//该函数计算了两个摄像头进行立体像对之间的转换关系
objectPoints, //校正的图像点向量组
imagePoints[0],//通过第一台相机观测到的图像上面的向量组 
imagePoints[1],//通过第二台相机观测到的图像上面的向量组
                    cameraMatrix[0], //输入或者输出第一个相机的内参数矩阵
distCoeffs[0],//输入/输出第一个相机的畸变系数向量
                    cameraMatrix[1], //输入或者输出第二个相机的内参数矩阵
distCoeffs[1],// 输入/输出第二个相机的畸变系数向量
                    imageSize,// 图像文件的大小——只用于初始化相机内参数矩阵。
R,//输出第一和第二相机坐标系之间的旋转矩阵。
T,//输出第一和第二相机坐标系之间的旋转矩阵平移向量
E,//输出本征矩阵 
F,//输出基础矩阵。
                    TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 100, 1e-5),//迭代优化算法终止的标准(用来判断校准效果)
                    CV_CALIB_FIX_ASPECT_RATIO +//优化 . 确定的比值
                    CV_CALIB_ZERO_TANGENT_DIST +//设置每个相机切向畸变系数为零并且设为固定值。
                    CV_CALIB_SAME_FOCAL_LENGTH +//执行以及
                    CV_CALIB_RATIONAL_MODEL +//能够输出系数k4,k5,k6。提供向后兼容性,这额外FLAG应该明确指定校正函数使用理性模型和返回8个系数。如果FLAG没有被设置,该函数计算并只返回5畸变系数。
                    CV_CALIB_FIX_K3 + CV_CALIB_FIX_K4 + CV_CALIB_FIX_K5);////计算极线向量
    cout << "done with RMS error=" << rms << endl;


// CALIBRATION QUALITY CHECK     校准质量核查
// because the output fundamental matrix implicitly  因为输出的原始矩阵无疑问的包含所有输出信息
// includes all the output information,
// we can check the quality of calibration using the
// epipolar geometry constraint: m2^t*F*m1=0//我们能用极限约束几何公式核查校准质量//两幅图像的对极线恰好在同一水平线上
//内参矩阵A,相机的感光长x,宽y,中心点偏移cx,cy,焦距f
//外参矩阵:世界坐标系转换到相机坐标系的旋转矩阵【R】平移矩阵[T],
//畸变系数[径向畸变K1,K2,K3,切向畸变P1,P2]
    double err = 0;
    int npoints = 0;
    vector<Vec3f> lines[2];//极线
    for( i = 0; i < nimages; i++ )
    {
        int npt = (int)imagePoints[0][i].size();//左某图所有角点数量
        Mat imgpt[2];
        for( k = 0; k < 2; k++ )
        {
            imgpt[k] = Mat(imagePoints[k][i]);//某图的角点向量矩阵
//计算校正后的角点坐标
            undistortPoints(imgpt[k],//校正前坐标
imgpt[k], // 校正后坐标
cameraMatrix[k],// 内参数矩阵
distCoeffs[k],// 畸变参数四个变形系数组成的向量,大小为4x1或者1x4,格式为[k1,k2,p1,p2]。
Mat(), cameraMatrix[k]);
//计算对应点的外极线epilines是一个三元组(a,b,c),表示点在另一视图中对应的外极线ax+by+c=0;  
//为一幅图像中的点计算其在另一幅图像中对应的对极线。
            computeCorrespondEpilines(imgpt[k], k+1, F, lines[k]);
        }
        for( j = 0; j < npt; j++ )
        {
            double errij = fabs(imagePoints[0][i][j].x*lines[1][j][0] +
                                imagePoints[0][i][j].y*lines[1][j][1] + lines[1][j][2]) +
                           fabs(imagePoints[1][i][j].x*lines[0][j][0] +
                                imagePoints[1][i][j].y*lines[0][j][1] + lines[0][j][2]);//fbs绝对值函数
            err += errij;
        }
        npoints += npt;
    }
//检查图像上点与另一幅图像的极线的距离的远近来评价标定的精度。
//使用undistortPoints对原始点做去畸变处理。
//使用computeCorrespondEpilines来计算极线。
//然后,计算这些点和线的点积(理想情况,这些点积都为0)。
//累计的绝对距离形成了误差。
    cout << "average reprojection err = " <<  err/npoints << endl;


    // save intrinsic parameters
    FileStorage fs("intrinsics.yml", CV_STORAGE_WRITE);//创建.yml文件
    if( fs.isOpened() )
    {
        fs << "M1" << cameraMatrix[0] << "D1" << distCoeffs[0] <<//相机的内参矩阵M1,M2,畸变矩阵D1,D2
            "M2" << cameraMatrix[1] << "D2" << distCoeffs[1];//参考上面的cameraMatrix[0]为相机的第一个内参矩阵distCoeffs[0]为相机的第一个畸变矩阵
        fs.release();
    }
    else
        cout << "Error: can not save the intrinsic parameters\n";
//计算外参数
    Mat R1, R2, P1, P2, Q;
    Rect validRoi[2];
//stereoRectify根据内参和畸变系数计算右相机相对左相机的旋转R和平移矩阵T
//并将旋转与平移矩阵分解为左右相机个旋转一般的旋转矩阵R1,R2和平移矩阵T1,T2
//这里用的是bougust极线校准方法
    stereoRectify(//stereoRectify是对标定过的摄像机进行校正
cameraMatrix[0],//第一个相机矩阵
distCoeffs[0],//第一个相机畸变参数
                  cameraMatrix[1],// 第二个相机矩阵
 distCoeffs[1],//第二个相机畸变参数
                  imageSize,// 用于校正的图像大小.
 R,// 第一和第二相机坐标系之间的旋转矩阵。
 T,//第一和第二相机坐标系之间的平移矩阵. 
 R1,//输出第一个相机的3x3矫正变换(旋转矩阵) 
 R2, //输出第二个相机的3x3矫正变换(旋转矩阵)
 P1,//在第一台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵
 P2,//在第二台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵
 Q,//输出深度视差映射矩阵
                  CALIB_ZERO_DISPARITY, 1, imageSize, &validRoi[0], &validRoi[1]);


    fs.open("extrinsics.yml", CV_STORAGE_WRITE);//打开文件
    if( fs.isOpened() )//在.yml中写入矩阵参数
    {
        fs << "R" << R << "T" << T << "R1" << R1 << "R2" << R2 << "P1" << P1 << "P2" << P2 << "Q" << Q;
//旋转矩阵     平移矩阵   左旋转矫正参数    右旋转矫正参数     左平移矫正参数    右平移矫正参数   深度矫正参数
        fs.release();
    }
    else
        cout << "Error: can not save the intrinsic parameters\n";


    // OpenCV can handle left-right
    // or up-down camera arrangements  opencv能处理左右或者上下相机的布置
    bool isVerticalStereo = fabs(P2.at<double>(1, 3)) > fabs(P2.at<double>(0, 3));


// COMPUTE AND DISPLAY RECTIFICATION  计算和显示校准
    if( !showRectified )
        return;
//HARTLEY方法校正
    Mat rmap[2][2];//查找映射表
// IF BY CALIBRATED (BOUGUET'S METHOD)//如果校准(用的是BOUGUET极线方法)
    if( useCalibrated )//这里由于进行了标定,那么一直是true
    {
        // we already computed everything
    }
// OR ELSE HARTLEY'S METHOD
    else//则下面不会发生
 // use intrinsic parameters of each camera, but  用每个相机的固有特征
 // compute the rectification transformation directly  计算校准变换正好来自基础矩阵
 // from the fundamental matrix
    {
        vector<Point2f> allimgpt[2];//拷贝角点
        for( k = 0; k < 2; k++ )
        {
            for( i = 0; i < nimages; i++ )
                std::copy(imagePoints[k][i].begin(), imagePoints[k][i].end(), back_inserter(allimgpt[k]));//start和end是输入序列(假设有N个元素)的迭代器(iterator),container是一个容器,
        }
//计算基础矩阵
        F = findFundamentalMat(
Mat(allimgpt[0])//第一幅图像点的数组,大小为2xN/Nx2 或 3xN/Nx3 (N 点的个数),多通道的1xN或Nx1也可以。点坐标应该是浮点数
, Mat(allimgpt[1]),//第二副图像的点的数组,格式、大小与第一幅图像相同
FM_8POINT, //计算基本矩阵的方法
0, 0);
        Mat H1, H2;//计算单应矩阵
//函数在不知道摄像头的固有参数和它们在空间的相对位置计算校正变换,阐述了“未标定”后缀。另一个与stereorectify()相关的差异是输出功能不在对象的矫正后的变换(3D)空间,但单应矩阵H1和H2构成了平面的透视变换。
        stereoRectifyUncalibrated(
Mat(allimgpt[0]),//第一个相机所得到的点
Mat(allimgpt[1]),//第二个相机所得到的点
F, //输入基础矩阵. 它可为findFundamentalMat()所用计算同一点组
imageSize,//图像尺寸.
H1, // 输出第一幅图像矫正后的单应性矩阵
H2,// 输出第二幅图像矫正后的单应性矩阵 
3);//可选的阈值用于滤除异常点。如果参数是大于零,所有点对不符合规定的对极几何(即,该点)拒绝优先计算单应性


        R1 = cameraMatrix[0].inv()*H1*cameraMatrix[0];
        R2 = cameraMatrix[1].inv()*H2*cameraMatrix[1];
        P1 = cameraMatrix[0];
        P2 = cameraMatrix[1];
    }


    //Precompute maps for cv::remap()
//显示矫正后的图像
//计算左右视图的校正查找映射表
    initUndistortRectifyMap(
cameraMatrix[0],//输入的摄像机内参数矩阵
distCoeffs[0],//输入的摄像机畸变系数矩阵
R1, //输入的第一和第二相机坐标系之间的旋转矩阵
P1,//输入的校正后的3X3摄像机矩阵
imageSize,//摄像机采集的无失真图像尺寸
CV_16SC2, //map1的数据类型,可以是CV_32FC1或CV_16SC2
rmap[0][0], //输出的X坐标重映射参数
rmap[0][1]);//输出的y坐标重映射参数
    initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);


    Mat canvas;
    double sf;
    int w, h;
    if( !isVerticalStereo )
    {
        sf = 600./MAX(imageSize.width, imageSize.height);
        w = cvRound(imageSize.width*sf);
        h = cvRound(imageSize.height*sf);
        canvas.create(h, w*2, CV_8UC3);
    }
    else
    {
        sf = 300./MAX(imageSize.width, imageSize.height);
        w = cvRound(imageSize.width*sf);//cvRound4舍五入,取最近整数
        h = cvRound(imageSize.height*sf);
        canvas.create(h*2, w, CV_8UC3);
    }


    for( i = 0; i < nimages; i++ )
    {
        for( k = 0; k < 2; k++ )
        {
            Mat img = imread(goodImageList[i*2+k], 0), rimg, cimg;
            remap(//重映射:就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程,把rmap[k][0]和rmap[k][1]像素对调
img,//输入图像,即原图像,需要单通道8位或者浮点类型的图像
rimg,//输出图像,即目标图像,需和原图形一样的尺寸和类型
rmap[k][0], //X坐标重映射参数
rmap[k][1],//y坐标重映射参数
CV_INTER_LINEAR);//最近邻插值
            cvtColor(rimg, cimg, COLOR_GRAY2BGR);
            Mat canvasPart = !isVerticalStereo ? canvas(Rect(w*k, 0, w, h)) : canvas(Rect(0, h*k, w, h));
            resize(cimg, canvasPart, canvasPart.size(), 0, 0, CV_INTER_AREA);
            if( useCalibrated )
            {
                Rect vroi(cvRound(validRoi[k].x*sf), cvRound(validRoi[k].y*sf),
                          cvRound(validRoi[k].width*sf), cvRound(validRoi[k].height*sf));
                rectangle(canvasPart, vroi, Scalar(0,0,255), 3, 8);
            }
        }


        if( !isVerticalStereo )
            for( j = 0; j < canvas.rows; j += 16 )
                line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);
        else
            for( j = 0; j < canvas.cols; j += 16 )
                line(canvas, Point(j, 0), Point(j, canvas.rows), Scalar(0, 255, 0), 1, 8);
        imshow("rectified", canvas);
        char c = (char)waitKey();
        if( c == 27 || c == 'q' || c == 'Q' )
            break;
    }
}




static bool readStringList( const string& filename, vector<string>& l )
{
    l.resize(0);
    FileStorage fs(filename, FileStorage::READ);//以FileStorage::READ 方式打开一个已经存在的文件stereo_calib.xml是官方存在的文件,保存图片名称left1.jpg....
    if( !fs.isOpened() )
        return false;
    FileNode n = fs.getFirstTopLevelNode();//返回映射(mapping)顶层的第一个元素,及.xml文件第一个元素
    if( n.type() != FileNode::SEQ )
        return false;
    FileNodeIterator it = n.begin(), it_end = n.end();
    for( ; it != it_end; ++it )
        l.push_back((string)*it);//该方法将一个或多个元素添加到矩阵l的底部
    return true;
}


int main(int argc, char** argv)
{
    Size boardSize;//标定板尺寸
    string imagelistfn;
    bool showRectified = true;
argc = 6;
//-w -h是棋盘的长和高,也就是有几个黑白交点,-s是每个格子的长度,单位是cm 
argv[0] = "opencv双目标定";//项目名称


argv[1] = "-w";//图片某一维方向上的交点个数  -9




argv[2] = "9";


argv[3] = "-h";//图片另一维上的交点个数  -6
argv[4] = "6";


argv[5] = "stereo_calib.xml";
    for( int i = 1; i < argc; i++ )
    {
        if( string(argv[i]) == "-w" )
        {
            if( sscanf(argv[++i], "%d", &boardSize.width) != 1 || boardSize.width <= 0 )
            {
                cout << "invalid board width" << endl;//标定板的宽有角点个数
                return print_help();
            }
        }
        else if( string(argv[i]) == "-h" )
        {
            if( sscanf(argv[++i], "%d", &boardSize.height) != 1 || boardSize.height <= 0 )
            {
                cout << "invalid board height" << endl;///标定板的高有角点个数
                return print_help();
            }
        }
        else if( string(argv[i]) == "-nr" )
            showRectified = false;
        else if( string(argv[i]) == "--help" )
            return print_help();
        else if( argv[i][0] == '-' )
        {
            cout << "invalid option " << argv[i] << endl;
            return 0;
        }
        else
            imagelistfn = argv[i];//读取为stereo_calib.xml
    }


    if( imagelistfn == "" )
    {
        imagelistfn = "stereo_calib.xml";
        boardSize = Size(9, 6);
    }
    else if( boardSize.width <= 0 || boardSize.height <= 0 )
    {
        cout << "if you specified XML file with chessboards, you should also specify the board width and height (-w and -h options)" << endl;
        return 0;
    }


    vector<string> imagelist;//传入的第一个参数
//用readStringList函数在.xml文件中读取相应jpg图片
    bool ok = readStringList(imagelistfn, imagelist);//此函数在上面定义,第一个参数是"stereo_calib.xml"数组,第二个参数是向量
    if(!ok || imagelist.empty())
    {
        cout << "can not open " << imagelistfn << " or the string list is empty" << endl;
        return print_help();
    }


    StereoCalib(imagelist, boardSize, true, showRectified);
    return 0;
}

效果:


可能由于标定板占整个图幅面积过小导致的畸变处理只有标定板附件那么一点区域