本文基于:张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)
这篇博客写得虽然很详细,但是其代码和结果有些明显的不当之处,所以本文进行了更为详细准确的介绍,若有问题欢迎指正。
一.本程序基于以下配置:
- Visual Studio 2015
- OpenCV 3.1.0
开发环境配置参考:OpenCV3.1.0+VS2013开发环境配置
二.本程序准备工作:
在不同视角拍摄这样的照片14张,依次命名为chess1、chess2、…、chess14,并将其放在当前.cpp文件目录下面,如下图所示:
三.完整源程序如下:
#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;
}
四.运行结果:
图片太多,中间省略几张。。。
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]
第一次写博客~