详细介绍Opencv实现张正友法相机标定

本文基于:张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)
这篇博客写得虽然很详细,但是其代码和结果有些明显的不当之处,所以本文进行了更为详细准确的介绍,若有问题欢迎指正。


一.本程序基于以下配置:
- Visual Studio 2015
- OpenCV 3.1.0
开发环境配置参考:OpenCV3.1.0+VS2013开发环境配置


二.本程序准备工作:

本程序使用的标定板是10行14列棋盘格图案,示意图如下:
详细介绍Opencv实现张正友法相机标定_第1张图片

在不同视角拍摄这样的照片14张,依次命名为chess1、chess2、…、chess14,并将其放在当前.cpp文件目录下面,如下图所示:
详细介绍Opencv实现张正友法相机标定_第2张图片

其中calibdata文本文档的内容如下:
详细介绍Opencv实现张正友法相机标定_第3张图片


三.完整源程序如下:

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

using namespace std;
using namespace cv;


int main()
{
    ifstream fin("calibdata.txt");//标定所用图像文件的路径
    ofstream fout("calibration_result.txt");//保存标定结果

    //读取每一幅图像,从中提取出内角点,然后对角点进行亚像素精确化
    cout<<"开始提取特征点..........";
    int image_count = 0;//图像的数量
    Size image_size;//图像的尺寸
    Size board_size = Size(13,9);//标定板上每行、列的内角点数,一般地,行列数不要相同
    vector image_points_buf;//用于存储检测到的内角点图像坐标位置
    vector<vector> image_points_seq;//保存检测到的所有角点
    string filename;//图像名
    int count = 0;//保存角点数
    while(getline(fin,filename))//从文本文档中依次读取待标定图片名
    {
        image_count++;
        cout<<"image_count = "<//依次读取当前目录下图片

        if(image_count == 1)//读入第一张图片时获取图像宽高信息
        {
            image_size.width = imageInput.cols;
            image_size.height = imageInput.rows;

            cout<<"image_size.width = "<cout<<"image_size.height = "<//提取标定板的内角点,输入必须是8位的灰度或者彩色图像
        if(0 == findChessboardCorners(imageInput,board_size,image_points_buf))

        {
            cout<<"Cannot find chessboard corners!\n";
            exit(1);
        }
        else
        {
            Mat view_gray;
            cvtColor(imageInput,view_gray,CV_RGB2GRAY);//转灰度图
            //find4QuadCornerSubpix(view_gray,image_points_buf,Size(5,5));
            //亚像素精确化方法一
            //image_points_buf初始化的角点坐标向量,同时作为亚像素坐标位置的输出,浮点型数

            cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),
           //亚像素精确化方法二
             //Size(5,5)是搜索窗口的大小,Size(-1,-1)表示没有死区
            //第四个参数定义求角点的迭代过程的终止条件,可以为迭代次数和角点精度两者的组合
            count += image_points_buf.size();
            image_points_seq.push_back(image_points_buf);//保存亚像素角点
            drawChessboardCorners(view_gray,board_size,image_points_buf,false);
            //用于绘制被成功标定的角点,输入8位灰度或者彩色图像
            //第四个参数是标志位,用来指示定义的棋盘内角点是否被完整的探测到
            //false表示有未被探测到的内角点,这时候函数会以圆圈标记出检测到的内角点
            imshow("Camera Calibration",view_gray);//显示图片
            waitKey(500);//暂停0.5S
        }
        cout << "count = " << count<< endl;//显示角点累加后的值
    }

    int total = image_points_seq.size();//图片总数
    cout << "total = " << total << endl;
    int CornerNum = board_size.width*board_size.height;//每张图片上总的内角点数

    for(int ii = 0;iicout << "第" << ii + 1 << "张图片的数据:" << endl;
        for(int jj = 0;jjif (0 == jj % 3)
                cout << endl;//每三个角点坐标之后换行
            else
                cout.width(10);//输出格式控制

            cout << "(" << image_points_seq[ii][jj].x << "," << image_points_seq[ii][jj].y << ")";
        }
        cout << endl;
    }

    cout<<"角点提取完成!\n";


    cout<<"开始标定............";

    Size square_size = Size(10,10);//设置棋盘格子的实际边长,单位为mm
    vector<vector> object_points;//保存标定板上角点的三维坐标
    Mat cameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));//相机内参数矩阵
    vector<int> point_counts;//每幅图像中角点的数量
    Mat distCoeffs = Mat(1,5,CV_32FC1,Scalar::all(0));//摄像机的5个畸变系数:k1,k2,k3,p1,p2
    vector tvecsMat;//每幅图像的平移向量
    vector rvecsMat;//每幅图像的旋转向量

    //初始化标定板上角点的三维坐标
    int i,j,t;
    for(t = 0;tvector temPointSet;
        for(i = 0;ifor(j = 0;j//假设标定板放在世界坐标系中的z = 0平面上
                //需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标
                realPoint.x = i*square_size.width;
                realPoint.y = j*square_size.height;
                realPoint.z = 0;
                temPointSet.push_back(realPoint);

            }
        }
        object_points.push_back(temPointSet);
    }

    //初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板
    for(i = 0;i0);
/*  object_points 世界坐标系中角点的三维坐标,image_points_seq 每个内角点对应的图像坐标点
    image_size 图像的像素尺寸大小,cameraMatrix 输出,内参数矩阵,distCoeffs 输出,畸变系数
    rvecsMat 输出,旋转向量,tvecsMat 输出,位移向量,0标定时采用的算法
    在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,
    标定的结果是生成相机的内参数矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像
    都会生成属于自己的平移向量和旋转向量
*/
    cout<<"标定完成!"<cout<<"开始输出标定结果....."<double total_err = 0.0; //所有图像的平均误差的总和,初始化为0.0
    double err = 0.0; //每幅图像的平均误差
    vector image_points2; //保存重新计算得到的投影点

    cout<<"每幅图像的标定误差:\n";
    fout<<"每幅图像的标定误差:\n";

    for(i = 0;ivector tempPointSet = object_points[i];//取出每幅图像角点的三维空间坐标
        projectPoints(tempPointSet,rvecsMat[i],tvecsMat[i],
            cameraMatrix,distCoeffs,image_points2);
        //通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点

        vector tempImagePoint = image_points_seq[i];//原来每幅图像中角点图像坐标
        Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
        //用于将原图像坐标点存储成一行多列的Mat,由两个通道浮点型数据构成
        Mat image_points2Mat = Mat(1,image_points2.size(),CV_32FC2);
        //用于将重投影坐标点存储成一行多列的Mat,以便于计算重投影误差

        for(int j = 0;j (0,j) = Vec2f(image_points2[j].x,image_points2[j].y);
            tempImagePointMat.at(0,j) = Vec2f(tempImagePoint[j].x,tempImagePoint[j].y);
        }//赋值
        //Vec2f表示的是2通道float类型的Vector,mat.at(y, x)是访问图像的一种方式

        err = norm(image_points2Mat,tempImagePointMat,NORM_L2);
        //计算每张图片重投影坐标和亚像素角点坐标之间的偏差
        total_err += err /= point_counts[i];//累加误差
        cout<<"第"<1<<"幅图像的平均误差:"<"像素"<"第"<1<<"幅图像的平均误差:"<"像素"<cout<<"总体平均误差:"<"像素"<"总体平均误差:"<"像素"<cout<<"评价完成!"<cout<<"开始保存标定结果............"<3,3,CV_32FC1,Scalar::all(0));//保存每幅图像的旋转矩阵
    fout<<"相机内参数矩阵:"<"畸变系数:\n";
    fout<for(i = 0; i"第"<1<<"幅图像的旋转向量:"<< endl;
        fout<//将旋转向量转换为相应的旋转矩阵
        fout<<"第"<1<<"幅图像的旋转矩阵:"<< endl;
        fout<"第"<1<<"幅图像的平移向量:"<<  endl;
        fout<cout<<"完成保存"<< endl;
    fout<< endl;


    //显示标定结果
    Mat mapx = Mat(image_size,CV_32FC1);//输出的X坐标重映射参数
    Mat mapy = Mat(image_size,CV_32FC1);//输出的Y坐标重映射参数
    Mat R = Mat::eye(3,3,CV_32F);
    cout<<"保存矫正图像"<string imageFileName;
    std::stringstream StrStm;
    for(int i = 0;i < image_count;i++)
    {
        cout<<"Frame # "<1<<"....."<//用来计算畸变映射

        StrStm.clear();//清除缓存
        imageFileName.clear();
        string filePath = "chess";
        StrStm<1;
        StrStm>>imageFileName;
        filePath += imageFileName;
        filePath += ".bmp";
        //获取图片路径
        Mat imageSource = imread(filePath);//读取图像
        Mat newimage = imageSource.clone();//拷贝图像

       remap(imageSource,newimage,mapx,mapy,INTER_LINEAR);//把求得的映射应用到图像上
       //与initUndistortRectifyMap结合使用,为矫正方法之一

       //undistort(imageSource,newimage,cameraMatrix,distCoeffs);//矫正方法二
       //第五个参数newCameraMatrix=noArray(),默认跟cameraMatrix保持一致,故可省

        imageFileName += "_d.jpg";//矫正后图片命名
        imwrite(imageFileName,newimage);//保存矫正后的图片

    }
    cout<<"保存结束"<//等待输入以退出

    return 0;
}

四.运行结果:

详细介绍Opencv实现张正友法相机标定_第4张图片
详细介绍Opencv实现张正友法相机标定_第5张图片
详细介绍Opencv实现张正友法相机标定_第6张图片

图片太多,中间省略几张。。。

详细介绍Opencv实现张正友法相机标定_第7张图片
详细介绍Opencv实现张正友法相机标定_第8张图片


calibration_result.txt的结果如下:


每幅图像的标定误差:
第1幅图像的平均误差:0.0316613像素
第2幅图像的平均误差:0.0389023像素
第3幅图像的平均误差:0.0382147像素
第4幅图像的平均误差:0.0370629像素
第5幅图像的平均误差:0.0385986像素
第6幅图像的平均误差:0.0411222像素
第7幅图像的平均误差:0.0301272像素
第8幅图像的平均误差:0.0356303像素
第9幅图像的平均误差:0.0376777像素
第10幅图像的平均误差:0.0327415像素
第11幅图像的平均误差:0.0257617像素
第12幅图像的平均误差:0.0254189像素
第13幅图像的平均误差:0.0288391像素
第14幅图像的平均误差:0.0281152像素
总体平均误差:0.0335624像素

相机内参数矩阵:
[2468.47496640925, 0, 1048.733444731282;
0, 2466.303729383499, 781.2776671184165;
0, 0, 1]

畸变系数:
[-0.337153976094791, 0.5625790549241111, -0.0004451387687732284, 5.767871907399249e-05, -1.615195594149361]

第1幅图像的旋转向量:
[1.832204013190426;
-1.97952864389898;
-0.4381686980068267]
第1幅图像的旋转矩阵:
[-0.05552643342962338, -0.8675821507778331, -0.4941842033516397;
-0.9950941432290062, 0.088674995984375, -0.04386788345134665;
0.08188077492253884, 0.4893239793213493, -0.868249723270445]

第1幅图像的平移向量:
[42.96524585005136;
38.32952150925843;
222.9912740841345]

第2幅图像的旋转向量:
[1.584731236327221;
-2.207688033499483;
-0.6043175791111356]
第2幅图像的旋转矩阵:
[-0.3091732926321815, -0.798262176337548, -0.5169036399096227;
-0.9502349038328671, 0.2811852708390378, 0.1341211057250835;
0.03828188423676798, 0.5326465444288723, -0.8454715583905021]

第2幅图像的平移向量:
[69.74394948874316;
5.086864923950324;
294.4591241780354]

第3幅图像的旋转向量:
[-1.473949748410117;
2.31855971511969;
1.035999692169115]
第3幅图像的旋转矩阵:
[-0.4803079766576785, -0.8563844330649897, -0.1894992093993095;
-0.7124954115441877, 0.2549531410021226, 0.6537194998021941;
-0.5115217845678576, 0.4490040074399208, -0.7326260063739247]

第3幅图像的平移向量:
[61.33952853487049;
-9.017935885808466;
338.6033873028243]

第4幅图像的旋转向量:
[-0.7465736599001761;
2.506508904972995;
0.600457192725938]
第4幅图像的旋转矩阵:
[-0.7500133063059861, -0.5919424240016485, 0.2951003338375199;
-0.3939740782147958, 0.7581903860462892, 0.5195495781942097;
-0.5312856727399724, 0.2734072149268043, -0.8018628491002705]

第4幅图像的平移向量:
[72.59290680319951;
-26.81723942274018;
284.8320214867886]

第5幅图像的旋转向量:
[-0.7283682830866786;
2.544530908621899;
0.6357133676594682]
第5幅图像的旋转矩阵:
[-0.7762623488644779, -0.5737245311930547, 0.2612602687794558;
-0.3834367786036743, 0.7586505858595253, 0.5267119947267545;
-0.500392848219717, 0.3086898943637603, -0.8088989717932951]

第5幅图像的平移向量:
[100.1209866961551;
-32.29634238993099;
306.584645572055]

第6幅图像的旋转向量:
[-0.5237302004887391;
2.886335271763946;
-0.3575089401502572]
第6幅图像的旋转矩阵:
[-0.9204005991990246, -0.3207721518269676, 0.2235351507176743;
-0.365616894748324, 0.9087093140932435, -0.2014241017226591;
-0.1385172309449962, -0.2671190915911554, -0.9536563152618697]

第6幅图像的平移向量:
[73.10631401051033;
-61.20027337080823;
322.5830605558965]

第7幅图像的旋转向量:
[1.713568861903209;
2.054266570651059;
0.1275839829491686]
第7幅图像的旋转矩阵:
[-0.1189475689363267, 0.9084901241809632, 0.4006209806160879;
0.9510798800820409, 0.2201192678099205, -0.2167823093380896;
-0.2851289840533027, 0.35523682551853, -0.8902293301437832]

第7幅图像的平移向量:
[-68.77620078523513;
-50.33338630124742;
279.3099139868805]

第8幅图像的旋转向量:
[1.818225297581961;
2.416158251961891;
0.6448996550355059]
第8幅图像的旋转矩阵:
[-0.3075469299716437, 0.9081606873844775, 0.284005372756168;
0.9288944014598498, 0.2218260808714645, 0.296560922546706;
0.2063251724864512, 0.35501740201652, -0.9118073082970308]

第8幅图像的平移向量:
[-61.68795969126989;
-48.39125620237197;
259.9247597410303]

第9幅图像的旋转向量:
[-1.847647793051378;
-2.434372024849804;
-0.5641186320132542]
第9幅图像的旋转矩阵:
[-0.2927069486806816, 0.9372808759138351, 0.1892807487311871;
0.9250022564756624, 0.2273995446169124, 0.3044014990484665;
0.242267347591411, 0.2641855536440529, -0.9335483520079759]

第9幅图像的平移向量:
[-34.9737442953684;
-41.07623681991357;
262.4502152811082]

第10幅图像的旋转向量:
[1.69102969291048;
2.015536806640728;
-0.08921956607969511]
第10幅图像的旋转矩阵:
[-0.1002242520458908, 0.9377989307560487, 0.3323977508567916;
0.9047607130849443, 0.2248971882948798, -0.361703340812717;
-0.4139603258282911, 0.264488979297568, -0.871023781805219]

第10幅图像的平移向量:
[-42.13169665197236;
-41.61090361839386;
288.3911563121005]

第11幅图像的旋转向量:
[1.724430894665768;
2.33520070621025;
-0.7246472524282326]
第11幅图像的旋转矩阵:
[-0.3281780053536973, 0.9307450708250076, -0.1612848720029889;
0.8585432544884051, 0.222686147487633, -0.461859675539632;
-0.3939577096269682, -0.2900422259810942, -0.8721655979075336]

第11幅图像的平移向量:
[-50.94693146467624;
-44.76151448070497;
303.7724473124102]

第12幅图像的旋转向量:
[1.696368490203205;
2.153193672447314;
-0.4174155271926968]
第12幅图像的旋转矩阵:
[-0.2093204256487086, 0.9725161681105834, 0.1019669660711291;
0.8639645737210686, 0.2327734149230861, -0.446521838950814;
-0.4579849066958599, -0.005370295015354715, -0.8889437469099114]

第12幅图像的平移向量:
[-58.7176317481046;
-45.7774147598785;
250.7908690340958]

第13幅图像的旋转向量:
[1.746182820818435;
2.38376805814103;
0.8197327892115064]
第13幅图像的旋转矩阵:
[-0.349578928430417, 0.8640106590061456, 0.3623260326297403;
0.9041163433351445, 0.2096799568728731, 0.3723008909471983;
0.2456994312401408, 0.4577334342273422, -0.8544658522601795]

第13幅图像的平移向量:
[-24.35541784964123;
-38.59140556730291;
171.2705769754649]

第14幅图像的旋转向量:
[1.644152775376657;
2.007535689228716;
0.5936125402120078]
第14幅图像的旋转矩阵:
[-0.167199482507626, 0.7761523420058212, 0.6079735808800089;
0.9819782500137466, 0.1862065131653021, 0.03233961896435755;
-0.08810816959352724, 0.6024240005624765, -0.7932983511877354]

第14幅图像的平移向量:
[-41.58825298021998;
-44.96941216039689;
169.3603333851799]


第一次写博客~

你可能感兴趣的:(OpenCV,相机标定,opencv,张正友摄像机标定)