张正友标定相机内参
- 拍摄棋盘图像,并按照形式如下命名,放置在工程的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]
如果想要源码小伙伴,网盘链接如下:
链接: https://pan.baidu.com/s/1K9s73lWri_Ijr6Zjs-KQGw 密码: s01v
--来自百度网盘超级会员V4的分享