相机标定过程

摄像机标定的数学过程如下

 wKiom1nEsuDARuPQAADpLQUWLhA506.jpg-wh_50

详细的数学解析可以看如下网址

http://blog.csdn.net/ssw_1990/article/details/53216767

 

这里程序的实现是在opencv中,所以就用opencv的程序来说明具体的过程.注意各个版本的opencv之间的程序移植性并不好,以下程序是在opencv2.4.3下编制运行的,每一步的要用到的输入输出都做了红色标记.

立体相机标定分为两个步骤,一个是单目标定(本文档第2步),另一个是双目标定

单目标定获得相机的x,y轴的焦距;x,y轴的坐标原点位置;世界坐标系和平面坐标之间的旋转和平移矩阵,5个畸变系数

双目标定获得两个相机成像平面之间的旋转和平移矩阵

注意

1.程序运行前需要插上摄像头,否则程序有可能不能正常运行

 

2.单目标定

(1).获取棋盘格图像

张正友论文中建议棋盘格数大于7*7,棋盘格在图像中所占面积在50%以上,其实这里是有问题的,因为原来遇到过球面相机将棋盘格放置大于50%以上反而出问题,这里还没有弄清楚怎么回事,所以以下的标定都是针对非球面相机的.

    for (int i=1; i<=19; i++)//输入左标定板图像
    {
        std::stringstream str;//声明输入输出流
        str << "./left"  << i << ".jpg";//以名字方式把图像输入到流
        std::cout << str.str() << std::endl;//.str("")清除内容 .clear()清空标记位
        leftFileList.push_back(str.str());//.push_back从容器后向前插入数据
        leftBoardImage = cv::imread(str.str(),0);//用来显示即时输入的图像
        cv::namedWindow("left chessboard image");
        cv::imshow("left chessboard image",leftBoardImage);
        cv::waitKey(10);
    }

 

(2).定义棋盘格的角点数目

 cv::Size boardSize(14,10)

 

(3).程序定位提取角点

    这里建立的是理想成像平面(三维,第三维为0,单位为格子数)和图像坐标系(二维,单位是像素)之间的关系

(a)首先声明两个坐标容器

    std::vector<cv::Point2f> imageCorners;//二位坐标点
    std::vector<cv::Point3f> objectCorners;//三维坐标点

(b)初始化棋盘角点,令其位置位于(x,y,z)=(i,j,0),一个棋盘格为一个坐标值

    for (int i=0; i<boardSize.height; i++)
    {
        for (int j=0; j<boardSize.width; j++)
        {
            objectCorners.push_back(cv::Point3f(i, j, 0.0f));
        }
    }

(c)直接使用opencv内函数找到二维角点坐标,并建立标定标定格子和实际坐标间的关系(像素级别)

    这个函数使用时,当标定板是长方形时可以找到角点,但是标定板是正方形时,就找不到,原因还未知.

    cv::findChessboardCorners(image, boardSize, imageCorners);

(d)获得像素精度往往是不够的,还需要获得亚像素的精度

    cv::cornerSubPix(image,
            imageCorners, //输入/输出
            cv::Size(5,5),//搜索框的一半,表示在多大窗口定位角点
            cv::Size(-1,-1), //死区
            cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS,
            30,        // max number of iterations//迭代最大次数
            0.01));     // min accuracy//最小精度

注:

TermCriteria模板类,取代了之前的CvTermCriteria,这个类是作为迭代算法的终止条件的,这个类在参考手册里介绍的很简单,我查了些资料,这里介绍一下。该类变量需要3个参数,一个是类型,第二个参数为迭代的最大次数,最后一个是特定的阈值。类型有CV_TERMCRIT_ITER、CV_TERMCRIT_EPS、CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,分别代表着迭代终止条件为达到最大迭代次数终止,迭代到阈值终止,或者两者都作为迭代终止条件。以上的宏对应的c++的版本分别为TermCriteria::COUNT、TermCriteria::EPS,这里的COUNT也可以写成MAX_ITER。

 

(4)将角点图像显示出来

        cv::drawChessboardCorners(image, boardSize, imageCorners, found);
        cv::imshow("Corners on Chessboard", image);
        cv::waitKey(10);

 

(5).相机标定

 calibrateCamera(leftObjectPoints, // the 3D points,3d点,整数的物理坐标(角点数目为单位)
                    leftImagePoints,  // the image points,图像点
                    imageSize,
                    //cv::Size(310,240),    // image size, 以像素衡量的图像尺寸
                    leftCameraMatrix, // output camera matrix,输出的3*3相机矩阵
                    leftDistCoeffs,   // output distortion matrix.输出的5*1畸变系数
                    leftRvecs, leftTvecs, // Rs, Ts.旋转和平移(每一张图都有各自的旋转平移向量)
                    leftFlag);        // set options.额外选项
//                    ,CV_CALIB_USE_INTRINSIC_GUESS);

其中:

leftObjectPoints为世界坐标系中的点。在使用时,应该输入一个三维点的vector的vector类型

imagePoints为其对应的二维图像点。和objectPoints一样,应该输入vector类型。

imageSize为图像的大小

leftCameraMatrix是通过这个标定函数我们可以获得的内参矩阵,内参矩阵总是3*3的

leftDistCoeffs可以获得的畸变矩阵,畸变矩阵是一个5*1的矩阵(k1,k2,p1,p2,k3)

leftRvecs, leftTvecs,可以获得的图像平面相对于世界坐标的旋转和平移矩阵

flags为标定是所采用的算法。程序中直接使用0即可,还可以使用如下某个或者某几个参数:

    CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,cx,cy的估

    计值。否则的话,将初始化(cx,cy)图像的中心点,使用最小二乘估算出fx,fy。如果内参数矩阵和畸

    变居中已知的时候,应该标定模块中的solvePnP()函数计算外参数矩阵。

    CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当    

    CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。

    CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当

    CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被

    用到。

    CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。

    CV_CALIB_FIX_K1,...,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。如果设置了

    CV_CALIB_USE_INTRINSIC_GUESS参数,

    CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个

    畸变参数。

 

3.双目标定

(1)右相机的标定,过程如上.其实是个重复的过程

 

(2).双目标定.立体标定需要有一个相机作为基准,常用的是用左相机作为基准

在这里需要提前明确什么是本征矩阵,基础矩阵

    本征矩阵E:它包含了物理空间中两个摄像机相关的旋转(R)和平移信息(T)。T和R描述了一台摄像机相

            对于另外一台摄像机在全局坐标系中的相对位置,与基础矩阵相对应,这建立的相机主轴之间的关

            系,联系的是物理坐标,不是像素坐标。

    基础矩阵F:除了包含本征矩阵E的信息外,还包含了两个摄像机的内参数。由于F包含了这些内参数,

            因此它可以在像素坐标系将两个摄像机关联起来。我们观察摄像机内参数矩阵M,如果图像没有

            畸变,即cx,cy为0,并且焦距进行了归一化处理,那么M就成了单位阵,此时本征矩阵F就等于

            基础矩阵E,即F=E。

更加详细的关于本征矩阵和基础矩阵的数学知识可以参考如下网址

http://blog.csdn.net/xiaoyinload/article/details/49000855

https://www.zhihu.com/question/27581884

 

(3)使用函数进行双目标定

    stereoCalibrate(stereoObjectPoints,
        leftImagePoints,//输入左相机图像点
        rightImagePoints,//输入右相机图像点
        leftCameraMatrix,//输入/输出左相机内参矩阵(是输入还是输出由最后一项flag决定)
        leftDistCoeffs,//输入/输出左相机的畸变矩阵(是输入还是输出由最后一项flag决定)
        rightCameraMatrix,//输入/输出右相机的内参矩阵(是输入还是输出由最后一项flag决定)
        rightDistCoeffs,//输入/输出右相机的畸变矩阵(是输入还是输出由最后一项flag决定)
        cv::Size(310,240),//图像的大小
        stereoR,//获得两相机旋转矩阵
        stereoT,//获得两相机平移矩阵
        stereoE,//获得两相机本征矩阵
        stereoF,//获得两相机基础矩阵
        cvTermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 100, 1e-5),
        CV_CALIB_FIX_INTRINSIC
        );

CV_CALIB_FIX_INTRINSIC要确认cameraMatrix? and distCoeffs?所以只有R, T, E , 和F矩阵被估计出来
CV_CALIB_USE_INTRINSIC_GUESS根据指定的FLAG优化一些或全部的内在参数。初始值是由用户提供。
CV_CALIB_FIX_PRINCIPAL_POINT在优化过程中确定主点。
CV_CALIB_FIX_FOCAL_LENGTH确定和 .
CV_CALIB_FIX_ASPECT_RATIO优化 . 确定的比值.
CV_CALIB_SAME_FOCAL_LENGTH执行以及 .
CV_CALIB_ZERO_TANGENT_DIST设置每个相机切向畸变系数为零并且设为固定值。
CV_CALIB_FIX_K1,...,CV_CALIB_FIX_K6在优化中不改变相应的径向畸变系数. 如果设置CV_CALIB_USE_INTRINSIC_GUESS , 使用distCoeffs矩阵提供的系数。否则将其置零.
CV_CALIB_RATIONAL_MODEL能够输出系数k4,k5,k6。提供向后兼容性,这额外FLAG应该明确指定校正函数使用理性模型和返回8个系数。如果FLAG没有被设置,该函数计算并只返回5畸变系数。

注:

这里旋转矩阵(M*3)和平移矩阵(M*3)与相机和世界坐标系的旋转矩阵和平移矩阵不同,每个旋转向量可以通过调用cvRodrigues2()来转换为3*3的旋转矩阵.

 

(4)相机矫正,得到3*3的旋转矩阵,得到3*4的投影矩阵

    stereoRectify(
        leftCameraMatrix, //输入左相机内参矩阵

        leftDistCoeffs, //输入左相机畸变矩阵

        rightCameraMatrix, //输入右相机内参矩阵

        rightDistCoeffs, //输入右相机畸变矩阵

        cv::Size(310,240), //用于校正的图像大小

        stereoR, //输入第一和第二相机坐标系之间的旋转矩阵

        stereoT, //输入第一和第二相机坐标系之间的平移矩阵

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

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

        projectLeft, //在第一台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵

        projectRight, //在第二台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵

        Q,
        CV_CALIB_ZERO_DISPARITY,
        1

        //Size newImageSize=Size(),

        //Rect* validPixROI1=0,

        //Rect* validPixROI2=0

        );

Q: 输出深度视差映射矩阵,矩阵Q是一个任意提供的矩阵(比如, stereoRectify()所能得出的矩阵).

flags: 操作的 flag可以是零或者是CV_CALIB_ZERO_DISPARITY . 如果设置了CV_CALIB_ZERO_DISPARITY,函数的作用是使每个相机的主点在校正后的图像上有相同的像素坐标。如果未设置标志,功能还可以改变图像在水平或垂直方向(取决于极线的方向)来最大化有用的图像区域。

alpha: 自由缩放参数。如果是-1或没有,该函数执行默认缩放。否则,该参数应在0和1之间。alpha=0,校正后的图像进行缩放和偏移,只有有效像素是可见的(校正后没有黑色区域)。alpha= 1意味着校正图像的抽取和转移,所有相机原始图像素像保留在校正后的图像(源图像像素没有丢失)。显然,任何中间值产生这两种极端情况之间的中间结果。

o newImageSize– 校正后新的图像分辨率。相同的尺寸应传递给initUndistortRectifyMap()(见OpenCV样品目录stereo_calib.cpp样品)。当(0,0)传递(默认),它设置为原始图像大小。设置为较大的值能帮助你保存原始图像的细节,特别是当有一个大的径向畸变时。

o validPixROI1– 校正后的图像可选的输出矩形,里面所有像素都是有效的。如果alpha= 0,ROIs覆盖整个图像。否则,他们可能会比较小。

o validPixROI2– 校正后的图像可选的输出矩形,里面所有像素都是有效的。如果alpha= 0,ROIs覆盖整个图像。否则,他们可能会比较小。

 

(5)用于计算最后的映射

cv::initUndistortRectifyMap(//用于计算畸变映射
            leftCameraMatrix,  //输入3*3相机内参矩阵.computed camera matrix
            leftDistCoeffs,    //输入5*1相机畸变矩阵.computed distortion matrix
            //cv::Mat(),
            //cv::Mat(),
            correctLeftR,     //可选输入3*3的矫正矩阵.optional rectification (none)
            projectLeft,//输入的3*4投影矩阵camera matrix to generate undistorted
            cv::Size(310,240),
            //leftImage.size(),  //去畸变图像尺寸.size of undistorted
            CV_32FC1,      //输出映射图像的类型.type of output map
            leftMap1, leftMap2   //输出x坐标和y坐标映射函数.the x and y mapping functions
            );

 

(6)将矫正后的像素重新投影到图片上

    cv::remap(leftImage, undistorted, leftMap1, leftMap2,
        cv::INTER_LINEAR); //插值类型interpolation type

    cv::remap(rightImage, undistorted, rightMap1, rightMap2,
        cv::INTER_LINEAR); //插值类型interpolation type

(7)最后重投影后的图片是含有黑边的,要进行裁剪

posted on 2017-10-08 21:45  悟江居士  阅读(182)  评论(0)    收藏  举报

导航