张正友标定相机内参

张正友标定相机内参

  1. 拍摄棋盘图像,并按照形式如下命名,放置在工程的images目录下

请添加图片描述

  1. 编写代码,首先读取图像,然后提取棋盘角点,然后利用opencv计算重投影误差做标定,最后做相机内参的评价,代码注释完整,简单易懂

    //
    // Created by gj on 2021/11/15.
    //
    
    #include "opencv2/core/core.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/calib3d/calib3d.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <iostream>
    #include <fstream>
    
    using namespace cv;
    using namespace std;
    
    int main()
    {
        ifstream fin("../calibdata.txt"); /* 标定所用图像文件的路径 */
        ofstream fout("calibration_result.txt");  /* 保存标定结果的文件 */
        //读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化
        cout<<".........开始提取角点.........\n";
        int image_count=0;  /* 图像数量 */
        Size image_size;  /* 图像的尺寸 */
        Size board_size = Size(8,6);    /* 标定板上每行、列的角点数 */
        vector<Point2f> image_points_buf;  /* 缓存每幅图像上检测到的角点 */
        vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */
        string filename;
        int count= -1 ;//用于存储角点个数。
        while (getline(fin,filename))
        {
            image_count++;
    
            string input_path="../images/"+to_string(image_count-1)+".png";
            Mat imageInput=imread(input_path);
            if (imageInput.empty()) {
                cout << "未找到拍摄的棋盘图片";
                return 0;
            }
            /* 输出检验*/
            cout << "input image" << image_count << ":  " << filename << endl;
            if (image_count == 1)  //读入第一张图片时获取图像宽高信息
            {
                image_size.width = imageInput.cols;
                image_size.height =imageInput.rows;
                cout<<"image_size.width = "<<image_size.width<<endl;
                cout<<"image_size.height = "<<image_size.height<<endl;
            }
    
            /* 提取角点 */
            if (0 == findChessboardCorners(imageInput,board_size,image_points_buf))
            {
                cout<<"can not 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)); //对粗提取的角点进行精确化
                //cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));
                image_points_seq.push_back(image_points_buf);  //保存亚像素角点
                /* 在图像上显示角点位置 */
                drawChessboardCorners(view_gray,board_size,image_points_buf,false); //用于在图片中标记角点
                imshow("Camera Calibration",view_gray);//显示图片
                waitKey(500);//暂停0.5S
            }
            int CornerNum=board_size.width*board_size.height;  //每张图片上总的角点数
            if (1 == image_count)// 48 是每幅图片的角点个数。此判断语句是为了输出 图片号,便于控制台观看
            {
                cout << "只展示第一张图片的数据:\n";
                cout<<" 第 1 张图片角点的数据 : ";
    
                //输出所有的角点
                for (int j = 0; j < CornerNum; ++j) {
                    cout<<" x:"<<image_points_seq[0][j].x;
                    cout<<" y:"<<image_points_seq[0][j].y;
                }
            }
        }
        int total = image_points_seq.size();
        cout<<"图片总数 = "<<total<<endl;
        cout<<"角点提取完成!\n";
    
        //以下是摄像机标定
        cout<<"..........开始标定........."<<endl;
        /*棋盘三维信息*/
        Size square_size = Size(10.8,10.8);  /* 实际测量得到的标定板上每个棋盘格的大小 */
        vector<vector<Point3f>> 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,p1,p2,k3 */
        vector<Mat> tvecsMat;  /* 每幅图像的旋转向量 */
        vector<Mat> rvecsMat; /* 每幅图像的平移向量 */
        /* 初始化标定板上角点的三维坐标 */
        int i,j,t;
        for (t=0;t<image_count;t++)
        {
            vector<Point3f> tempPointSet;
            for (i=0;i<board_size.height;i++)
            {
                for (j=0;j<board_size.width;j++)
                {
                    Point3f realPoint;
                    /* 假设标定板放在世界坐标系中z=0的平面上 */
                    realPoint.x = i*square_size.width;
                    realPoint.y = j*square_size.height;
                    realPoint.z = 0;
                    tempPointSet.push_back(realPoint);
                }
            }
            object_points.push_back(tempPointSet);
        }
        /* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
        for (i=0;i<image_count;i++)
        {
            point_counts.push_back(board_size.width*board_size.height);
        }
        /* 开始标定 */
        calibrateCamera(object_points,image_points_seq,image_size,cameraMatrix,distCoeffs,rvecsMat,tvecsMat,0);
        cout<<"标定完成!\n";
        //对标定结果进行评价
        cout<<".........开始评价标定结果.........\n";
        double total_err = 0.0; /* 所有图像的平均误差的总和 */
        double err = 0.0; /* 每幅图像的平均误差 */
        vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
        cout<<"\t每幅图像的标定误差:\n";
        fout<<"每幅图像的标定误差:\n";
        for (i=0;i<image_count;i++)
        {
            vector<Point3f> tempPointSet=object_points[i];
            /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
            projectPoints(tempPointSet,rvecsMat[i],tvecsMat[i],cameraMatrix,distCoeffs,image_points2);
            /* 计算新的投影点和旧的投影点之间的误差*/
            vector<Point2f> tempImagePoint = image_points_seq[i];
            Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
            Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2);
            for (int j = 0 ; j < tempImagePoint.size(); j++)
            {
                image_points2Mat.at<Vec2f>(0,j) = Vec2f(image_points2[j].x, image_points2[j].y);
                tempImagePointMat.at<Vec2f>(0,j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
            }
            err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
            total_err += err/=  point_counts[i];
            std::cout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
            fout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
        }
        std::cout<<"总体平均误差:"<<total_err/image_count<<"像素"<<endl;
        fout<<"总体平均误差:"<<total_err/image_count<<"像素"<<endl<<endl;
        std::cout<<"评价完成!"<<endl;
        //保存定标结果
        std::cout<<"开始保存定标结果………………"<<endl;
        Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
        fout<<"相机内参数矩阵:"<<endl;
        fout<<cameraMatrix<<endl<<endl;
        fout<<"畸变系数:\n";
        fout<<distCoeffs<<endl<<endl<<endl;
        for (int i=0; i<image_count; i++)
        {
            fout<<"第"<<i+1<<"幅图像的旋转向量:"<<endl;
            fout<<tvecsMat[i]<<endl;
            /* 将旋转向量转换为相对应的旋转矩阵 */
            Rodrigues(tvecsMat[i],rotation_matrix);
            fout<<"第"<<i+1<<"幅图像的旋转矩阵:"<<endl;
            fout<<rotation_matrix<<endl;
            fout<<"第"<<i+1<<"幅图像的平移向量:"<<endl;
            fout<<rvecsMat[i]<<endl<<endl;
        }
        std::cout<<"完成保存"<<endl;
        fout<<endl;
        return 0;
    }
    
  2. 因为我是在clion中编译的,最后的结果在cmake-build-debug目录中的calibration_result.txt中

    每幅图像的标定误差:
    第1幅图像的平均误差:0.0611951像素
    第2幅图像的平均误差:0.102013像素
    第3幅图像的平均误差:0.0923479像素
    第4幅图像的平均误差:0.0711323像素
    第5幅图像的平均误差:0.0456678像素
    第6幅图像的平均误差:0.114321像素
    第7幅图像的平均误差:0.103894像素
    第8幅图像的平均误差:0.0934296像素
    第9幅图像的平均误差:0.108852像素
    第10幅图像的平均误差:0.109755像素
    第11幅图像的平均误差:0.141707像素
    第12幅图像的平均误差:0.0613077像素
    第13幅图像的平均误差:0.246986像素
    第14幅图像的平均误差:0.110797像素
    第15幅图像的平均误差:0.0961908像素
    总体平均误差:0.103973像素
    
    相机内参数矩阵:
    [379.3643974682103, 0, 311.4027147649293;
     0, 378.2406004051696, 237.2316005121863;
     0, 0, 1]
    
    畸变系数:
    [-0.1632403355249804, 0.5007861402302362, -0.004126230171804792, -0.005271513365273738, -0.6580069027097243]
    
    
    第1幅图像的旋转向量:
    [-27.53007980274755;
     -11.79933419715619;
     102.2026862178985]
    第1幅图像的旋转矩阵:
    [0.9546925136147651, 0.2967753848688849, 0.02205845379546779;
     -0.2939944556310656, 0.9520442467079487, -0.08472905268258321;
     -0.04614612124669788, 0.07440512916607167, 0.9961598326813149]
    第1幅图像的平移向量:
    [2.20176132512684;
     2.185533601019431;
     -0.0963506995960441]
    
    第2幅图像的旋转向量:
    [-37.31630217955718;
     -30.08048831398644;
     99.09048755986471]
    第2幅图像的旋转矩阵:
    [-0.7639699279363039, 0.2907745627722495, -0.5760209222359947;
     0.07850693611841375, -0.8441877142173231, -0.5302676344505955;
     -0.6404581252434056, -0.4503301642230267, 0.6221062071709004]
    第2幅图像的平移向量:
    [1.832827675754982;
     1.758313404587785;
     -0.6647769097344208]
    
    第3幅图像的旋转向量:
    [-36.59204564266251;
     -33.17295204605372;
     95.10235209253118]
    第3幅图像的旋转矩阵:
    [0.9468298512063428, -0.2968742834814561, -0.1240116634512532;
     0.30959820026062, 0.9455796826020066, 0.1001399932378333;
     0.08753392062486325, -0.1332093227130156, 0.9872148140513187]
    第3幅图像的平移向量:
    [1.987801195530061;
     1.93780404642594;
     -0.46776413483636]
    
    第4幅图像的旋转向量:
    [-38.40529981129137;
     -29.43804839128432;
     81.94361231189357]
    第4幅图像的旋转矩阵:
    [0.6720630918922319, -0.6345595428661064, -0.3816613512969469;
     0.7323673845422911, 0.6457476828694688, 0.2159813513417207;
     0.1094039056658276, -0.4246694204040008, 0.8987138970767019]
    第4幅图像的平移向量:
    [2.240121074835526;
     2.128518190969042;
     -0.09890788625265373]
    
    第5幅图像的旋转向量:
    [-38.13055262423675;
     -28.49177115281911;
     84.77698630745486]
    第5幅图像的旋转矩阵:
    [-0.6810868933534013, 0.08633638922628201, -0.7270946785650274;
     0.3703114812451084, -0.8160455681081916, -0.4437781400982169;
     -0.6316565922716759, -0.5715029822024963, 0.5238265846378815]
    第5幅图像的平移向量:
    [-2.183850592874764;
     -2.092247996793978;
     -0.195439058766755]
    
    第6幅图像的旋转向量:
    [-38.3092789370934;
     -22.6186892240845;
     89.29163845796771]
    第6幅图像的旋转矩阵:
    [0.7593677430386671, 0.6478092947935352, 0.06085842925563847;
     -0.5986592648771036, 0.7322546951334563, -0.3246692871705221;
     -0.2548876525239443, 0.2101099213128965, 0.9438676313746126]
    第6幅图像的平移向量:
    [-1.992603788307841;
     -1.921067351580686;
     -0.4678212251747607]
    
    第7幅图像的旋转向量:
    [-42.29392266662697;
     -29.53875019445829;
     103.0943279368649]
    第7幅图像的旋转矩阵:
    [-0.3633334773670537, -0.583332413782476, -0.7264379390251272;
     0.8795203713050701, -0.4719449445351589, -0.06092524753414802;
     -0.3072990411370593, -0.6610531479075004, 0.6845261389880064]
    第7幅图像的平移向量:
    [-2.038107857398446;
     -1.910960564126662;
     0.4871780355598174]
    
    第8幅图像的旋转向量:
    [-19.8548154590127;
     -33.28781090802723;
     124.456363809498]
    第8幅图像的旋转矩阵:
    [-0.0002036794387580074, 0.9943251990671246, 0.1063830673312706;
     -0.9146674964994629, 0.04281554453553882, -0.4019330789988934;
     -0.4042070377443862, -0.0973869993697758, 0.9094682198909757]
    第8幅图像的平移向量:
    [-1.87951674111304;
     -1.88096702117784;
     0.7453835582518447]
    
    第9幅图像的旋转向量:
    [-43.66216451422579;
     -22.85747031718025;
     115.6451050232866]
    第9幅图像的旋转矩阵:
    [0.9991160660398525, -0.04117418748635484, -0.008471886796192188;
     0.04130115291679647, 0.9990280355212062, 0.01540126651878901;
     0.007829517787985971, -0.01573755150834711, 0.9998455021270685]
    第9幅图像的平移向量:
    [-1.93167570682439;
     -1.693608329039564;
     0.3084130144264655]
    
    第10幅图像的旋转向量:
    [-22.50034331453363;
     -32.89586926067927;
     126.4389142091563]
    第10幅图像的旋转矩阵:
    [0.8166306652416266, -0.5497773165310738, -0.1756680358372285;
     0.5656803779109127, 0.8228171901763742, 0.05456722090067771;
     0.114542859374058, -0.1439332268037784, 0.9829359895681238]
    第10幅图像的平移向量:
    [-1.832165042876404;
     -2.088924412648336;
     1.134371347296932]
    
    第11幅图像的旋转向量:
    [-20.78997605305722;
     -25.31394777448223;
     72.18217000773689]
    第11幅图像的旋转矩阵:
    [-0.6267640275124919, 0.7517882051584726, -0.2048935050237624;
     -0.459148161415432, -0.5687747551791589, -0.6824062160764044;
     -0.629563197530755, -0.3336311922699829, 0.7016697284758411]
    第11幅图像的平移向量:
    [1.847473223172457;
     1.72738886961609;
     0.6585688875521605]
    
    第12幅图像的旋转向量:
    [-32.80393536580875;
     -28.52194351479875;
     73.8405276891212]
    第12幅图像的旋转矩阵:
    [-0.4085588026967788, 0.8648679303152287, -0.2916901915581442;
     -0.4442140853656964, -0.4675897050082407, -0.7642209851430566;
     -0.7973415523486105, -0.1826563190326541, 0.5752244066586089]
    第12幅图像的平移向量:
    [1.904208503215055;
     1.768778728703117;
     0.3966278248943234]
    
    第13幅图像的旋转向量:
    [-14.5997794388444;
     -21.04996827256533;
     60.98800816393639]
    第13幅图像的旋转矩阵:
    [-0.887826650444868, 0.301099981569611, -0.3479980457684012;
     -0.02234594011432139, -0.7835415349763792, -0.6209374541185824;
     -0.4596351789411396, -0.5435084765352234, 0.702377418639087]
    第13幅图像的平移向量:
    [1.86204888781338;
     2.053921855673591;
     0.9987560523683087]
    
    第14幅图像的旋转向量:
    [-22.1954903529919;
     -26.83286753787837;
     68.69086212649877]
    第14幅图像的旋转矩阵:
    [0.04230107762330343, -0.7861726872253972, -0.6165574788231061;
     0.9959298006083485, 0.08233928640779563, -0.03666161717750999;
     0.079589264930156, -0.6124971410338604, 0.7864558481779028]
    第14幅图像的平移向量:
    [1.782981714578705;
     1.331350426884584;
     0.4203645050400237]
    
    第15幅图像的旋转向量:
    [-30.83187103185273;
     -28.91653360678463;
     73.94896222158316]
    第15幅图像的旋转矩阵:
    [-0.6838981874850625, 0.5395453298402869, -0.491094803680697;
     -0.06329710480155293, -0.714463502186754, -0.6968036886862606;
     -0.7268264893895844, -0.4454579004693643, 0.5227719514578399]
    第15幅图像的平移向量:
    [1.855010045356094;
     1.592445843852865;
     0.1319879765401926]
    

如果想要源码小伙伴,网盘链接如下:

链接: https://pan.baidu.com/s/1K9s73lWri_Ijr6Zjs-KQGw  密码: s01v
--来自百度网盘超级会员V4的分享
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值