张正友标定相机内参
- 拍摄棋盘图像,并按照形式如下命名,放置在工程的images目录下
- 编写代码,首先读取图像,然后提取棋盘角点,然后利用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;
}
- 因为我是在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]