使用opencv实现单目标定相机标定(摘抄)

使用opencv实现单目标定

相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。

相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上)。

相机标定的输出:摄像机的内参、外参系数。

这三个基础的问题就决定了使用Opencv实现张正友法标定相机的标定流程、标定结果评价以及使用标定结果矫正原始图像的完整流程:

  • 1. 准备标定图片

  • 2. 对每一张标定图片,提取角点信息

  • 3.对每一张标定图片,进一步提取亚像素角点信息

  • 4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)

  • 5. 相机标定

  • 6. 对标定结果进行评价

  • 7. 查看标定效果——利用标定结果对棋盘图进行矫正

准备标定图片

标定图片需要使用标定板在不同位置、不同角度、不同姿态下拍摄,最少需要3张,以10~20张为宜。标定板需要是黑白相间的矩形构成的棋盘图,制作精度要求较高。

这里我们使用OpenCV提供的sample程序中的标定图片,图片位于opencv(C++版本)的安装路径:opencv\sources\samples\data下:

我们先创建一个C++控制台项目,并把标定图片按如下格式存放:

sample文件夹下有两个文件夹left和right,分别对应左摄像头和右摄像头拍摄到的标定板图片:

filename.txt存放标定图片的路径,内容如下:

关于OpenCV提供的用于相机标定的API函数可以查看博客双目视觉标定程序讲解,单目标定的代码如下:​​​​​

/**************************************************************************************   Description:相机标定,张氏标定法  单目标定*   Author     :JNU*   Data       :2018.7.22*************************************************************************************/#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/calib3d/calib3d.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>#include <fstream>#include <vector>
using namespace cv;using namespace std;
void main(char *args){
   
       //保存文件名称    std::vector<std::string>  filenames;
    //需要更改的参数    //左相机标定,指定左相机图片路径,以及标定结果保存文件    string infilename = "sample/left/filename.txt";        //如果是右相机把left改为right    string outfilename = "sample/left/caliberation_result.txt";
    //标定所用图片文件的路径,每一行保存一个标定图片的路径  ifstream 是从硬盘读到内存    ifstream fin(infilename);    //保存标定的结果  ofstream 是从内存写到硬盘    ofstream fout(outfilename);
    /*    1.读取毎一幅图像,从中提取出角点,然后对角点进行亚像素精确化、获取每个角点在像素坐标系中的坐标    像素坐标系的原点位于图像的左上角    */    std::cout << "开始提取角点......" << std::endl;;    //图像数量    int imageCount = 0;    //图像尺寸    cv::Size imageSize;    //标定板上每行每列的角点数    cv::Size boardSize = cv::Size(9, 6);    //缓存每幅图像上检测到的角点    std::vector<Point2f>  imagePointsBuf;    //保存检测到的所有角点    std::vector<std::vector<Point2f>> imagePointsSeq;    char filename[100];    if (fin.is_open())    {
   
           //读取完毕?        while (!fin.eof())        {
   
               //一次读取一行            fin.getline(filename, sizeof(filename) / sizeof(char));            //保存文件名            filenames.push_back(filename);            //读取图片            Mat imageInput = cv::imread(filename);            //读入第一张图片时获取图宽高信息            if (imageCount == 0)            {
   
                   imageSize.width = imageInput.cols;                imageSize.height = imageInput.rows;                std::cout << "imageSize.width = " << imageSize.width << std::endl;                std::cout << "imageSize.height = " << imageSize.height << std::endl;            }
            std::cout << "imageCount = " << imageCount << std::endl;            imageCount++;
            //提取每一张图片的角点            if (cv::findChessboardCorners(imageInput, boardSize, imagePointsBuf) == 0)            {
   
                   //找不到角点                std::cout << "Can not find chessboard corners!" << std::endl;                exit(1);            }            else            {
   
                   Mat viewGray;                //转换为灰度图片                cv::cvtColor(imageInput, viewGray, cv::COLOR_BGR2GRAY);                //亚像素精确化   对粗提取的角点进行精确化                cv::find4QuadCornerSubpix(viewGray, imagePointsBuf, cv::Size(5, 5));                //保存亚像素点                imagePointsSeq.push_back(imagePointsBuf);                //在图像上显示角点位置                cv::drawChessboardCorners(viewGray, boardSize, imagePointsBuf, true);                //显示图片                //cv::imshow("Camera Calibration", viewGray);                cv::imwrite("test.jpg", viewGray);                //等待0.5s                //waitKey(500);            }        }                        //计算每张图片上的角点数 54        int cornerNum = boardSize.width * boardSize.height;
        //角点总数        int total = imagePointsSeq.size()*cornerNum;        std::cout << "total = " << total << std::endl;
        for (int i = 0; i < total; i++)        {
   
               int num = i / cornerNum;            int p = i%cornerNum;            //cornerNum是每幅图片的角点个数,此判断语句是为了输出,便于调试            if (p == 0)            {                                                        std::cout << "\n第 " << num+1 << "张图片的数据 -->: " << std::endl;            }            //输出所有的角点            std::cout<<p+1<<":("<< imagePointsSeq[num][p].x;            std::cout << imagePointsSeq[num][p].y<<")\t";            if ((p+1) % 3 == 0)            {
   
                   std::cout << std::endl;            }        }
        std::cout << "角点提取完成!" << std::endl;
        /*        2.摄像机标定 世界坐标系原点位于标定板左上角(第一个方格的左上角)        */        std::cout << "开始标定" << std::endl;        //棋盘三维信息,设置棋盘在世界坐标系的坐标        //实际测量得到标定板上每个棋盘格的大小        cv::Size squareSize = cv::Size(26, 26);        //毎幅图片角点数量        std::vector<int> pointCounts;        //保存标定板上角点的三维坐标        std::vector<std::vector<cv::Point3f>> objectPoints;        //摄像机内参数矩阵 M=[fx γ u0,0 fy v0,0 0 1]        cv::Mat cameraMatrix = cv::Mat(3, 3, CV_64F, Scalar::all(0));        //摄像机的5个畸变系数k1,k2,p1,p2,k3        cv::Mat distCoeffs = cv::Mat(1, 5, CV_64F, Scalar::all(0));        //每幅图片的旋转向量        std::vector<cv::Mat> tvecsMat;        //每幅图片的平移向量        std::vector<cv::Mat> rvecsMat;
        //初始化标定板上角点的三维坐标        int i, j, t;        for (t = 0; t < imageCount; t++)        {
   
               std::vector<cv::Point3f> tempPointSet;            //行数            for (i = 0; i < boardSize.height; i++)            {
   
                   //列数                for (j = 0; j < boardSize.width; j++)                {
   
                       cv::Point3f realPoint;                    //假设标定板放在世界坐标系中z=0的平面上。                    realPoint.x = i*squareSize.width;                    realPoint.y = j*squareSize.height;                    realPoint.z = 0;                    tempPointSet.push_back(realPoint);                }            }            objectPoints.push_back(tempPointSet);        }
        //初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板        for (i = 0; i < imageCount; i++)        {
   
               pointCounts.push_back(boardSize.width*boardSize.height);        }        //开始标定        cv::calibrateCamera(objectPoints, imagePointsSeq, imageSize, cameraMatrix, distCoeffs, rvecsMat, tvecsMat);        std::cout << "标定完成" << std::endl;        //对标定结果进行评价        std::cout << "开始评价标定结果......" << std::endl;        //所有图像的平均误差的总和        double totalErr = 0.0;        //每幅图像的平均误差        double err = 0.0;        //保存重新计算得到的投影点        std::vector<cv::Point2f> imagePoints2;        std::cout << "每幅图像的标定误差:" << std::endl;        fout << "每幅图像的标定误差:" << std::endl;        for (i = 0; i < imageCount; i++)        {
   
               std::vector<cv::Point3f> tempPointSet = objectPoints[i];            //通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点imagePoints2(在像素坐标系下的点坐标)            cv::projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, imagePoints2);            //计算新的投影点和旧的投影点之间的误差            std::vector<cv::Point2f> tempImagePoint = imagePointsSeq[i];            cv::Mat tempImagePointMat = cv::Mat(1, tempImagePoint.size(), CV_32FC2);            cv::Mat imagePoints2Mat = cv::Mat(1, imagePoints2.size(), CV_32FC2);            for (int j = 0; j < tempImagePoint.size(); j++)            {
   
                   imagePoints2Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(imagePoints2[j].x, imagePoints2[j].y);                tempImagePointMat.at<cv::Vec2f>(0, j) = cv::Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);            }            //Calculates an absolute difference norm or a relative difference norm.            err = cv::norm(imagePoints2Mat, tempImagePointMat, NORM_L2);            totalErr += err /= pointCounts[i];            std::cout << "  第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;            fout<<  "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
        }        //每张图像的平均总误差        std::cout << "  总体平均误差:" << totalErr / imageCount << "像素" << std::endl;        fout << "总体平均误差:" << totalErr / imageCount << "像素" << std::endl;        std::cout << "评价完成!" << std::endl;        //保存标定结果        std::cout << "开始保存标定结果....." << std::endl;        //保存每张图像的旋转矩阵        cv::Mat rotationMatrix = cv::Mat(3, 3, CV_32FC1, Scalar::all(0));        fout << "相机内参数矩阵:" << std::endl;        fout << cameraMatrix << std::endl << std::endl;        fout << "畸变系数:" << std::endl;        fout << distCoeffs << std::endl << std::endl;
        for (int i = 0; i < imageCount; i++)        {
   
               fout << "第" << i + 1 << "幅图像的旋转向量:" << std::endl;            fout << tvecsMat[i] << std::endl;            //将旋转向量转换为相对应的旋转矩阵            cv::Rodrigues(tvecsMat[i], rotationMatrix);            fout << "第" << i + 1 << "幅图像的旋转矩阵:" << std::endl;            fout << rotationMatrix << std::endl;            fout << "第" << i + 1 << "幅图像的平移向量:" << std::endl;            fout << rvecsMat[i] << std::endl;        }        std::cout << "保存完成" << std::endl;
        /************************************************************************        显示定标结果        *************************************************************************/        cv::Mat mapx = cv::Mat(imageSize, CV_32FC1);        cv::Mat mapy = cv::Mat(imageSize, CV_32FC1);        cv::Mat R = cv::Mat::eye(3, 3, CV_32F);        std::cout << "显示矫正图像" << endl;        for (int i = 0; i != imageCount; i++)        {
   
               std::cout << "Frame #" << i + 1 << "..." << endl;            //计算图片畸变矫正的映射矩阵mapx、mapy(不进行立体校正、立体校正需要使用双摄)            initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);            //读取一张图片            Mat imageSource = imread(filenames[i]);            Mat newimage = imageSource.clone();            //另一种不需要转换矩阵的方式            //undistort(imageSource,newimage,cameraMatrix,distCoeffs);            //进行校正            remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);            imshow("原始图像", imageSource);            imshow("矫正后图像", newimage);            waitKey();        }
        //释放资源        fin.close();        fout.close();        system("pause");            }}

上面有两个函数需要单独介绍一下:

CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,                           InputArray R, InputArray newCameraMatrix,                           Size size, int m1type, OutputArray map1, OutputArray map2 );

函数功能:该函数功能是计算畸变矫正和摄像机立体校正的映射变换矩阵。为了重映射,将结果以映射的形式表达。无畸变的图像看起来和原始的图像一样,就像这个图像是用内参为newCameraMatrix的且无畸变的相机采集得到的。该函数实际上为反向映射算法构建映射,供反向映射使用。也就是,对于已经修正畸变的图像中的每个像素$(u,v)$,该函数计算原来图像(从相机中获得的原始图像)中对应的坐标系。

参数说明:

  • cameraMatrix:输入相机内参矩阵

  • distCoeffs:输入参数,相机的畸变系数

有4,5,8,12或14个元素。如果这个向量是空的,就认为是零畸变系数。

  • RR:可选的立体修正变换矩阵,是个3×33×3的矩阵。

在单目相机例子中,$R$就设置为单位矩阵cv::Mat R = cv::Mat::eye(3, 3, CV_32F),表示不进行立体校正。

在双目相机例子中,newCameraMatrix一般是用cv::stereoRectify()计算而来的,设置为R1R1或R2R2(左右相机平面行对准的校正旋转矩阵)。此外,根据RR,新的相机在坐标空间中的取向是不同的。例如,它帮助配准双目相机的两个相机方向,从而使得两个图像的极线是水平的,且yy坐标相同(在双目相机的两个相机谁水平放置的情况下)。

  • newCameraMatrix:新的相机内参矩阵

在单目相机例子中,newCameraMatrix一般和cameraMatrix相等,或者可以用cv::getOptimalNewCameraMatrix()来计算,获得一个更好的有尺度的控制结果。

在双目相机例子中,newCameraMatrix一般是用cv::stereoRectify()计算而来的,设置为P1或P2(左右相机把空间3D点的坐标转换到图像的2D点的坐标的投影矩阵)。

  • size:未畸变的图像尺寸。

  • m1type:第一个输出的映射的类型,可以为 CV_32FC1, CV_32FC2或CV_16SC2,参见cv::convertMaps。

  • map1:第一个输出映射。

  • map2:第二个输出映射。

void remap(InputArray src, OutputArray dst, InputArray map1, InputArray       map2, int interpolation,int borderMode=BORDER_CONSTANT, const Scalar&       borderValue=Scalar())

函数功能:重映射:就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。

参数说明:

  • src:输入图像,即原图像,需要单通道8位或者浮点类型的图像

  • dst:输出图像,即目标图像,需和原图形一样的尺寸和类型

  • map1:它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等

  • map2:它有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示CV_16UC1,CV_32FC1类型的Y值

  • interpolation:插值方式,有四中插值方式:

(1)INTER_NEAREST——最近邻插值

(2)INTER_LINEAR——双线性插值(默认)

(3)INTER_CUBIC——双三样条插值(默认)

(4)INTER_LANCZOS4——lanczos插值(默认)

  • borderMode:边界模式,默认BORDER_CONSTANT

  • borderValue:边界颜色,默认Scalar()黑色

程序运行后,把相机内部参数和外部参数保存在caliberation_result.txt文件中,内容如下

每幅图像的标定误差:
第1幅图像的平均误差:0.0644823像素
第2幅图像的平均误差:0.0769712像素
第3幅图像的平均误差:0.057877像素
第4幅图像的平均误差:0.0596713像素
第5幅图像的平均误差:0.0625956像素
第6幅图像的平均误差:0.0658863像素
第7幅图像的平均误差:0.0568134像素
第8幅图像的平均误差:0.0643699像素
第9幅图像的平均误差:0.058048像素
第10幅图像的平均误差:0.0565483像素
第11幅图像的平均误差:0.0590138像素
第12幅图像的平均误差:0.0569968像素
第13幅图像的平均误差:0.0698826像素
总体平均误差:0.0622428像素
相机内参数矩阵:
[530.5277314196954, 0, 338.8371277433631;
0, 530.5883296858968, 231.5390118666163;
0, 0, 1]

畸变系数:
[-0.2581406917163123, -0.11124480187392, 0.0004630258905514519, -0.0009475605555950018, 0.413646790569884]

第1幅图像的旋转向量:
[-75.22204622827574;
-109.7328226714255;
412.7511174854986]
第1幅图像的旋转矩阵:
[0.9927105083879407, -0.1161407096490343, -0.03220531164846807;
0.1168004495051158, 0.9929655913965856, 0.01941621224214358;
0.02972375365863362, -0.02303627280285992, 0.999292664139887]
第1幅图像的平移向量:
[-1.985720132175791;
-2.010141521348128;
0.1175016759367312]
第2幅图像的旋转向量:
[-57.88571684656549;
88.73102475029921;
365.4767680110305]
第2幅图像的旋转矩阵:
[-0.880518198944593, 0.2965025784551226, -0.36982958548071;
-0.4330747951156081, -0.8203927789645991, 0.3733656519530371;
-0.192701642865192, 0.4889191233652108, 0.8507785655767596]
第2幅图像的平移向量:
[-2.431974050326802;
-0.2015324617416875;
0.2103186188188722]
第3幅图像的旋转向量:
[-38.96229403649615;
-101.619482335263;
328.7991741655258]
第3幅图像的旋转矩阵:
[0.7229826652152683, -0.6501194230369263, -0.2337537199455046;
0.6686409526220074, 0.7435854196067706, -1.49985835111166e-05;
0.1738256088007802, -0.1562864662674188, 0.9722958388199968]
第3幅图像的平移向量:
[1.726707502757928;
2.49410066154742;
-0.5169212442744683]
第4幅图像的旋转向量:
[-99.94408740929534;
-67.11904896100746;
341.7035262057663]
第4幅图像的旋转矩阵:
[-0.4166240767662854, 0.8762113538151707, -0.2422355095852507;
-0.7194830230098562, -0.4806860756468779, -0.5012834290895748;
-0.5556694685325433, -0.03456240912595265, 0.8306845861192869]
第4幅图像的平移向量:
[-2.144507828065959;
-2.137658756455213;
0.3861555312888436]
第5幅图像的旋转向量:
[63.1817601794685;
-117.2855578733511;
327.5340459209377]
第5幅图像的旋转矩阵:
[-0.1237680939389874, -0.9830519969136794, -0.1352413778646805;
0.8454470843144938, -0.03311262698003439, -0.5330316890754268;
0.5195196690663707, -0.1803117447603135, 0.8352167312468426]
第5幅图像的平移向量:
[-0.3394208745634724;
-2.941274925899604;
0.7239987875443074]
第6幅图像的旋转向量:
[176.6380486063267;
-65.02048705679623;
345.2669628180993]
第6幅图像的旋转矩阵:
[-0.4823787195065527, 0.3144101256594393, 0.8175922234525194;
-0.5902636261183672, -0.8063068742380883, -0.03818476447485269;
0.6472245534965549, -0.5010144682933011, 0.5745301383843724]
第6幅图像的平移向量:
[0.144403698794371;
-2.686413562533621;
-0.08279238304814077]
第7幅图像的旋转向量:
[23.37912628758978;
-71.28708027930361;
401.7783087659996]
第7幅图像的旋转矩阵:
[0.950756682549477, -0.3056521783663705, -0.05136610212392408;
0.3046663933949521, 0.9520979509442887, -0.02622747687825021;
0.05692204602107398, 0.009286423831555549, 0.9983354361181394]
第7幅图像的平移向量:
[0.4433620069430767;
-2.778035766165631;
0.1565310822654871]
第8幅图像的旋转向量:
[84.53413910746443;
-88.75268154189268;
326.4489757550855]
第8幅图像的旋转矩阵:
[-0.882333219506006, -0.1387045774185431, 0.4497211691251699;
-0.1080922696912742, -0.870309912144045, -0.4804963247068739;
0.4580438308602738, -0.4725692510383723, 0.7529104541603049]
第8幅图像的平移向量:
[0.3026042878663719;
-2.832559861959414;
0.5197600078874884]
第9幅图像的旋转向量:
[-66.87955552666558;
-81.79728232518671;
287.3798612501427]
第9幅图像的旋转矩阵:
[-0.06408698919457989, 0.997286705569611, 0.03622270986668297;
-0.8668814706204128, -0.03765202403427882, -0.4970903750638435;
-0.4943777641752957, -0.06325782149453277, 0.8669423708118097]
第9幅图像的平移向量:
[1.918018245182696;
2.198445482038513;
0.6398190872020209]
第10幅图像的旋转向量:
[51.38889872566385;
-112.4792732922813;
348.8614284720838]
第10幅图像的旋转矩阵:
[0.8410751829508221, 0.5075468667660225, 0.1870527055678015;
-0.521221221444936, 0.852916565973049, 0.0293559159998552;
-0.1446408481020841, -0.1221863720908967, 0.9819111546039054]
第10幅图像的平移向量:
[0.2388869800501047;
2.534868757127185;
0.05816455567725017]
第11幅图像的旋转向量:
[55.25157597573984;
-103.974863603741;
332.3331998859927]
第11幅图像的旋转矩阵:
[0.7603104175748064, -0.6302201082550355, -0.1573235013538499;
0.6075084686586226, 0.7756458925501082, -0.1711926104661106;
0.2299163531271294, 0.0345841657577196, 0.9725957053388442]
第11幅图像的平移向量:
[-0.02801590475009446;
-3.011578659457537;
0.5796308944847007]
第12幅图像的旋转向量:
[37.20265745451167;
-92.46700742075161;
299.3885458741333]
第12幅图像的旋转矩阵:
[0.1968247409885918, -0.9604756585987335, -0.1968413843024444;
0.9041946443200382, 0.2554459280495449, -0.3423148010616344;
0.3790673640894628, -0.1106069034112951, 0.9187350251296783]
第12幅图像的平移向量:
[-0.4442257873668548;
-2.891665626351126;
-0.7306268697464358]
第13幅图像的旋转向量:
[49.15686896201693;
-109.7597615043953;
322.2472823512488]
第13幅图像的旋转矩阵:
[-0.02527960043733595, 0.888126856668879, 0.4589026348422781;
-0.9835935284565535, 0.05992383782219021, -0.170155530145356;
-0.1786189031992861, -0.4556751256368033, 0.8720409779911538]
第13幅图像的平移向量:
[0.2685697410235677;
2.70549028727733;
0.2575020268614151]

 下面在附上一份来自于其他博客的源码:

/***************************************************************************************   Description:相机标定,张氏标定法  单目标定,一次只能标定一个相机                 OPENCV3.0 单目摄像头标定(使用官方自带的标定图片)                 https://blog.csdn.net/zc850463390zc/article/details/48946855*   Author     :JNU*   Data       :2018.7.22*************************************************************************************/#include <opencv2/opencv.hpp>#include <highgui.hpp>#include "cv.h"#include <cv.hpp>#include <iostream>
using namespace std;using namespace cv;
//程序运行之前需要更改的参数
//使用官方标定图片集?//#define   SAMPLE  #define  MY_DATA
#ifdef SAMPLE
/* 官方数据集  */const int imageWidth = 640;                                //摄像头的分辨率const int imageHeight = 480;const int boardWidth = 9;                                //横向的角点数目const int boardHeight = 6;                                //纵向的角点数据const int boardCorner = boardWidth * boardHeight;        //总的角点数据const int frameNumber = 13;                                //相机标定时需要采用的图像帧数const int squareSize = 20;                                //标定板黑白格子的大小 单位mmconst Size boardSize = Size(boardWidth, boardHeight);const char imageFilePathFormat[] = "sample/right%02d.jpg";          //用于标定的图片路径,格式化字符串sample/left%02d.bmp表明图片路径为 sample/left01.bmp - sample/leftxx.bmp
#elif defined  MY_DATA//自己的数据const int imageWidth = 1600;                                //摄像头的分辨率const int imageHeight = 1200;const int boardWidth = 9;                                    //横向的角点数目const int boardHeight = 6;                                    //纵向的角点数据const int boardCorner = boardWidth * boardHeight;           //总的角点数据const int frameNumber = 10;                                         //相机标定时需要采用的图像帧数const int squareSize = 30;                                   //标定板黑白格子的大小 单位mmconst Size boardSize = Size(boardWidth, boardHeight);Size imageSize = Size(imageWidth, imageHeight);const char imageFilePathFormat[] = "image/right/%d.bmp";
#endif // SAMPLE


Mat intrinsic;                                            //相机内参数Mat distortion_coeff;                                    //相机畸变参数vector<Mat> rvecs;                                        //旋转向量vector<Mat> tvecs;                                        //平移向量vector<vector<Point2f>> corners;                        //各个图像找到的角点的集合 和objRealPoint 一一对应vector<vector<Point3f>> objRealPoint;                    //各副图像的角点的实际物理坐标集合

vector<Point2f> corner;                                    //某一副图像找到的角点
Mat rgbImage, grayImage;
/*计算标定板上模块的实际物理坐标*/void calRealPoint(vector<vector<Point3f>>& obj, int boardwidth, int boardheight, int imgNumber, int squaresize){
   
       //    Mat imgpoint(boardheight, boardwidth, CV_32FC3,Scalar(0,0,0));    vector<Point3f> imgpoint;    for (int rowIndex = 0; rowIndex < boardheight; rowIndex++)    {
   
           for (int colIndex = 0; colIndex < boardwidth; colIndex++)        {
   
               //    imgpoint.at<Vec3f>(rowIndex, colIndex) = Vec3f(rowIndex * squaresize, colIndex*squaresize, 0);            imgpoint.push_back(Point3f(rowIndex * squaresize, colIndex * squaresize, 0));        }    }    for (int imgIndex = 0; imgIndex < imgNumber; imgIndex++)    {
   
           obj.push_back(imgpoint);    }}
/*设置相机的初始参数 也可以不估计*/void guessCameraParam(void){
   
       /*分配内存*/    intrinsic.create(3, 3, CV_64FC1);    distortion_coeff.create(5, 1, CV_64FC1);
    /*    fx 0 cx    0 fy cy    0 0  1    */    intrinsic.at<double>(0, 0) = 256.8093262;   //fx            intrinsic.at<double>(0, 2) = 160.2826538;   //cx    intrinsic.at<double>(1, 1) = 254.7511139;   //fy    intrinsic.at<double>(1, 2) = 127.6264572;   //cy
    intrinsic.at<double>(0, 1) = 0;    intrinsic.at<double>(1, 0) = 0;    intrinsic.at<double>(2, 0) = 0;    intrinsic.at<double>(2, 1) = 0;    intrinsic.at<double>(2, 2) = 1;
    /*    k1 k2 p1 p2 p3    */    distortion_coeff.at<double>(0, 0) = -0.193740;  //k1    distortion_coeff.at<double>(1, 0) = -0.378588;  //k2    distortion_coeff.at<double>(2, 0) = 0.028980;   //p1    distortion_coeff.at<double>(3, 0) = 0.008136;   //p2    distortion_coeff.at<double>(4, 0) = 0;          //p3}
void outputCameraParam(void){
   
       /*保存数据*/    //cvSave("cameraMatrix.xml", &intrinsic);    //cvSave("cameraDistoration.xml", &distortion_coeff);    //cvSave("rotatoVector.xml", &rvecs);    //cvSave("translationVector.xml", &tvecs);
    /*保存数据*/    /*输出数据*/    FileStorage fs("intrinsics.yml", FileStorage::WRITE);    if (fs.isOpened())    {
   
           fs << "intrinsic" << intrinsic << "distortion_coeff" << distortion_coeff ;        fs.release();    }    else    {
   
           cout << "Error: can not save the intrinsics!!!!!" << endl;    }
    fs.open("extrinsics.yml", FileStorage::WRITE);    if (fs.isOpened())    {
   
           fs << "rvecs" << rvecs << "tvecs" << tvecs;        fs.release();    }    else    {            cout << "Error: can not save the extrinsics parameters\n";    }
    /*输出数据*/    cout << "fx :" << intrinsic.at<double>(0, 0) << endl << "fy :" << intrinsic.at<double>(1, 1) << endl;    cout << "cx :" << intrinsic.at<double>(0, 2) << endl << "cy :" << intrinsic.at<double>(1, 2) << endl;
    cout << "k1 :" << distortion_coeff.at<double>(0, 0) << endl;    cout << "k2 :" << distortion_coeff.at<double>(1, 0) << endl;    cout << "p1 :" << distortion_coeff.at<double>(2, 0) << endl;    cout << "p2 :" << distortion_coeff.at<double>(3, 0) << endl;    cout << "p3 :" << distortion_coeff.at<double>(4, 0) << endl;}

void main(char *args){
   
       Mat img;    int goodFrameCount = 0;    namedWindow("chessboard");    cout << "按Q退出 ..." << endl;    while (goodFrameCount < frameNumber)    {
   
           char filename[100];        //sprintf_s(filename, "image/right/%d.bmp", goodFrameCount + 1);        sprintf_s(filename, imageFilePathFormat, goodFrameCount + 1);        //    cout << filename << endl;        rgbImage = imread(filename, CV_LOAD_IMAGE_COLOR);        cvtColor(rgbImage, grayImage, CV_BGR2GRAY);        imshow("Camera", grayImage);
        bool isFind = findChessboardCorners(rgbImage, boardSize, corner, 0);        if (isFind == true)    //所有角点都被找到 说明这幅图像是可行的        {
   
               /*            Size(5,5) 搜索窗口的一半大小            Size(-1,-1) 死区的一半尺寸            TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 0.1)迭代终止条件            */            cornerSubPix(grayImage, corner, Size(5, 5), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 0.1));            drawChessboardCorners(rgbImage, boardSize, corner, isFind);            imshow("chessboard", rgbImage);            corners.push_back(corner);            //string filename = "res\\image\\calibration";            //filename += goodFrameCount + ".jpg";            //cvSaveImage(filename.c_str(), &IplImage(rgbImage));        //把合格的图片保存起来            goodFrameCount++;            cout << "The image is good" << endl;        }        else        {
   
               cout << goodFrameCount+1 <<" The image is bad please try again" << endl;        }        //    cout << "Press any key to continue..." << endl;        //    waitKey(0);
        if (waitKey(10) == 'q')        {
   
               break;        }        //    imshow("chessboard", rgbImage);    }
    /*    图像采集完毕 接下来开始摄像头的校正    calibrateCamera()    输入参数 objectPoints  角点的实际物理坐标    imagePoints   角点的图像坐标    imageSize       图像的大小    输出参数    cameraMatrix   相机的内参矩阵    distCoeffs       相机的畸变参数    rvecs           旋转矢量(外参数)    tvecs           平移矢量(外参数)    */
    /*设置实际初始参数 根据calibrateCamera来 如果flag = 0 也可以不进行设置*/    guessCameraParam();    cout << "guess successful" << endl;    /*计算实际的校正点的三维坐标*/    calRealPoint(objRealPoint, boardWidth, boardHeight, frameNumber, squareSize);    cout << "cal real successful" << endl;    /*标定摄像头*/    calibrateCamera(objRealPoint, corners, Size(imageWidth, imageHeight), intrinsic, distortion_coeff, rvecs, tvecs, 0);    cout << "calibration successful" << endl;    /*保存并输出参数*/    outputCameraParam();    cout << "out successful" << endl;
    /*显示畸变校正效果*/    Mat cImage;    undistort(rgbImage, cImage, intrinsic, distortion_coeff);    imshow("Corret Image", cImage);    cout << "Correct Image" << endl;    cout << "Wait for Key" << endl;    waitKey(0);    system("pause");}

猜你喜欢

转载自blog.csdn.net/yuyanqiang1982/article/details/129519240