参考文章:
https://blog.csdn.net/Yep_Ying/article/details/105372287
https://blog.csdn.net/LuohenYJ/article/details/104697062
https://blog.csdn.net/u010801994/article/details/84563208
标定板图片:
https://gitee.com/chengwen12333/opencv-camera-calibration_retest/tree/master/
//calibration.cpp
#include <iostream>
#include <sstream>
#include <time.h>
#include <stdio.h>
#include <fstream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
#define calibration
int CHECKERBOARD[2]{ 15,19 };
int main()
{
ifstream fin("right_img.txt"); /* 标定所用图像文件的路径 */
ofstream fout("caliberation_result_right.txt"); /* 保存标定结果的文件 */
// 读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化
Size image_size; /* 图像的尺寸 */
Size board_size = Size(19, 15); /* 标定板上每行、列的角点数 */
vector<Point2f> image_points_buf; /* 缓存每幅图像上检测到的角点 */
vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */
string filename; // 图片名
vector<string> filenames;
std::string path = "./image/*.bmp";
std::vector<cv::String> images;
cv::glob(path, images);
cv::Mat frame, gray;
double Kernelsize_cali[2]{3.0, 3.0};//标定板的宽度例如我们使用的是3.0mm*3.0mm
std::vector<std::vector<cv::Point3f> > objpoints; //真实的坐标点,通过横轴第一个点相对应起来的
std::vector<cv::Point3f> objp;
for (int i{ 0 }; i < CHECKERBOARD[1]; i++)
{
for (int j{ 0 }; j < CHECKERBOARD[0]; j++)
{
objp.push_back(cv::Point3f(j * Kernelsize_cali[0], i * Kernelsize_cali[1], 0));
cout << "(x,y,z):(" << j << "," << i << "," << 0 << ")" << endl;
}
cout << "one success!" << endl;
}
for (int i{ 0 }; i < images.size(); i++)
{
bool success;
frame = cv::imread(images[i]);
image_size = cv::Size(frame.rows,frame.cols);
if (frame.empty())
{
continue;
}
if (i == 40)
{
int b = 1;
}
cout << "the current image is " << i << "th" << endl;
cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
//交点提取
success = cv::findChessboardCorners(gray, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), image_points_buf, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
if (success)
{
// 如果是OpenCV4以下版本,第一个参数为CV_TERMCRIT_EPS | CV_TERMCRIT_ITER
cv::TermCriteria criteria(TermCriteria::EPS | TermCriteria::Type::MAX_ITER, 30, 0.001);
// refining pixel coordinates for given 2d points.
// 为给定的二维点细化像素坐标
cv::cornerSubPix(gray, image_points_buf, cv::Size(11, 11), cv::Size(-1, -1), criteria);
// Displaying the detected corner points on the checker board
// 在棋盘上显示检测到的角点
cv::drawChessboardCorners(frame, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), image_points_buf, success);
objpoints.push_back(objp);
image_points_seq.push_back(image_points_buf);
}
else
{
cout << "some errors happened in cornerSubPix" << endl;
}
}
int CornerNum = board_size.width * board_size.height; // 每张图片上总的角点数
//-------------以下是摄像机标定------------------
/*棋盘三维信息*/
/*内外参数*/
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; /* 每幅图像的平移向量 */
/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
for (int16_t i = 0; i<images.size(); i++)
{
point_counts.push_back(board_size.width * board_size.height);
}
/* 开始标定 */
// object_points 世界坐标系中的角点的三维坐标
// image_points_seq 每一个内角点对应的图像坐标点
// image_size 图像的像素尺寸大小
// cameraMatrix 输出,内参矩阵
// distCoeffs 输出,畸变系数
// rvecsMat 输出,旋转向量
// tvecsMat 输出,位移向量
// 0 标定时所采用的算法
double error = calibrateCamera(objpoints, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat);
//------------------------标定完成------------------------------------
// -------------------对标定结果进行评价------------------------------
double total_err = 0.0; /* 所有图像的平均误差的总和 */
double err = 0.0; /* 每幅图像的平均误差 */
vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
fout << "每幅图像的标定误差:\n";
for (int i = 0; i<images.size(); i++)
{
vector<Point3f> tempPointSet = objpoints[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);
}
//求真实角点像素和重投影像素坐标两者之间的L2范数
err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
total_err += err /= point_counts[i];
fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
}
fout << "总体平均误差:" << total_err / images.size() << "像素" << endl << 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<images.size(); 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;
}
fout << endl;
//--------------------标定结果保存结束-------------------------------
//----------------------显示定标结果--------------------------------
Mat mapx = Mat(image_size, CV_32FC1);
Mat mapy = Mat(image_size, CV_32FC1);
Mat R = Mat::eye(3, 3, CV_32F);
string imageFileName;
std::stringstream StrStm;
for (int i = 0; i <images.size(); i++)
{
initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);
Mat imageSource = imread(images[i]);
Mat newimage = imageSource.clone();
remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);
StrStm.clear();
imageFileName.clear();
StrStm << i + 1;
StrStm >> imageFileName;
imageFileName += "_d.jpg";
imwrite(imageFileName, newimage);
}
fin.close();
fout.close();
}
结果:
每幅图像的标定误差:
第1幅图像的平均误差:0.00901338像素
第2幅图像的平均误差:0.00533954像素
第3幅图像的平均误差:0.00476242像素
第4幅图像的平均误差:0.00374578像素
第5幅图像的平均误差:0.00879981像素
第6幅图像的平均误差:0.00560517像素
第7幅图像的平均误差:0.0048039像素
第8幅图像的平均误差:0.00378456像素
第9幅图像的平均误差:0.0072627像素
第10幅图像的平均误差:0.0209396像素
第11幅图像的平均误差:0.00830864像素
第12幅图像的平均误差:0.0147205像素
第13幅图像的平均误差:0.00571387像素
第14幅图像的平均误差:0.00401392像素
第15幅图像的平均误差:0.0033231像素
第16幅图像的平均误差:0.00789049像素
第17幅图像的平均误差:0.00564339像素
第18幅图像的平均误差:0.0322107像素
第19幅图像的平均误差:0.0158545像素
总体平均误差:0.00903874像素
相机内参数矩阵:
[7790.06882421712, 0, 1895.040592745665;
0, 7788.143662816939, 1323.443925848088;
0, 0, 1]
畸变系数:
[-0.2092971729778142, 0.336140706232359, 0.001085659642471452, 9.149787693839918e-05, -1.611656058352493]
第1幅图像的旋转向量:
[40.8918494376741;
-53.12622768474755;
367.5214753662501]
第1幅图像的旋转矩阵:
[-0.9419550181705912, -0.2867565577707702, 0.1746179266819421;
0.2255682887967951, -0.9257561113539244, -0.3034708707275642;
0.2486758750512941, -0.246467642635538, 0.936703800729533]
第1幅图像的平移向量:
[0.03433162173006149;
0.02104546720082031;
-0.006740858378163532]
第2幅图像的旋转向量:
[49.38790343037014;
58.79629337775193;
373.380359556444]
第2幅图像的旋转矩阵:
[-0.462552768099424, 0.8849393913807501, 0.05410369956138322;
-0.8254867236753823, -0.4521331944980534, 0.337856838718219;
0.323444903745976, 0.1116147302807895, 0.9396411795068925]
第2幅图像的平移向量:
[0.06478033897060728;
-0.03537319776132106;
3.135868025933476]
第3幅图像的旋转向量:
[-40.72924223441478;
4.219756463606606;
372.4960595356939]
第3幅图像的旋转矩阵:
[-0.6098774883460968, 0.770658466513109, -0.184756534932651;
-0.7746461202946803, -0.6289154071185618, -0.06624801130066736;
-0.1672508221834984, 0.1027177622446918, 0.9805489400322588]
第3幅图像的平移向量:
[0.04960036043663845;
0.0380938754767744;
0.01236001521062122]
第4幅图像的旋转向量:
[-81.3082068969731;
5.047258616446007;
373.0192910876365]
第4幅图像的旋转矩阵:
[0.1480818586780773, 0.9687872873034982, -0.1988038105509432;
-0.9738115890528618, 0.1077686090391296, -0.2001921974805996;
-0.1725188458018025, 0.223242287358154, 0.9593748636368963]
第4幅图像的平移向量:
[0.03589759585524415;
-0.009008555690961151;
0.03899724009689464]
第5幅图像的旋转向量:
[4.186823721461959;
21.21229449626194;
356.6987145589036]
第5幅图像的旋转矩阵:
[0.7052011835105583, 0.707953526543744, -0.03864058785246931;
-0.7075434246271584, 0.7061995896621888, 0.0257767691503226;
0.04553672190859445, 0.009162085746856759, 0.9989206490720806]
第5幅图像的平移向量:
[-0.05449702750792344;
0.438658356217181;
3.02101358594354]
第6幅图像的旋转向量:
[-28.77422347503968;
9.457474027420234;
365.3001293971984]
第6幅图像的旋转矩阵:
[-0.520161777522312, -0.8484525650055816, -0.09777509979681961;
0.8422566147263324, -0.528569082209244, 0.1059175164088352;
-0.1415468832386192, -0.0272574809534302, 0.9895562185027722]
第6幅图像的平移向量:
[-0.06780459578860312;
0.1727267622396768;
3.115861816364419]
第7幅图像的旋转向量:
[73.21306420941193;
9.894926498059107;
372.8154105260532]
第7幅图像的旋转矩阵:
[-0.9235775645189886, -0.05636482692167775, 0.3792459473788254;
0.07640250160168259, -0.9963533807298017, 0.03798155417172588;
0.3757221580718636, 0.06405425040061097, 0.9245160425538531]
第7幅图像的平移向量:
[-0.01785152736152136;
-0.1034868066811129;
3.087003907106642]
第8幅图像的旋转向量:
[78.69464425820962;
37.79976832018529;
372.3706951835734]
第8幅图像的旋转矩阵:
[0.7050520522746134, 0.7090899243324834, -0.009647942449488911;
-0.6965640974065993, 0.6950217022820915, 0.1781664714839282;
0.1330415791678876, -0.1188962260421109, 0.983953060692154]
第8幅图像的平移向量:
[-0.0653629413630966;
-0.2084500992684071;
3.052201756483122]
第9幅图像的旋转向量:
[41.880408908027;
21.99217504844562;
361.4664557735976]
第9幅图像的旋转矩阵:
[0.9924510244240694, -0.1223591258812917, 0.008319160605586932;
0.1224651626426396, 0.9923779007396059, -0.01372538023389374;
-0.006576325609880401, 0.01464057503035115, 0.9998711944566927]
第9幅图像的平移向量:
[0.003609429338698677;
0.237128680259337;
2.963687627172932]
第10幅图像的旋转向量:
[40.99623783961949;
23.01326647613388;
372.5362487629638]
第10幅图像的旋转矩阵:
[0.0817862111431345, 0.9958657757830904, 0.03952685531121414;
-0.9834290847235396, 0.07419941356458071, 0.1654136703736819;
0.161796943687671, -0.05240041651184994, 0.9854318572903521]
第10幅图像的平移向量:
[-0.2564790587356458;
-0.1943438525491485;
2.992207613804558]
第11幅图像的旋转向量:
[1.543402240315645;
-23.28828645378324;
357.6316661977772]
第11幅图像的旋转矩阵:
[0.9686976144290699, -0.2477277636046903, -0.01599646644880712;
0.2477102443959335, 0.9688292069436177, -0.003098805303290942;
0.01626550401112861, -0.0009606833086272723, 0.9998672464216659]
第11幅图像的平移向量:
[0.2624364670677614;
0.09361841325521592;
0.01666918497552899]
第12幅图像的旋转向量:
[7.764363810557446;
-54.5231805402692;
369.6353968062355]
第12幅图像的旋转矩阵:
[-0.9901929530292285, -0.1380513398579433, 0.02144162621357531;
0.1259810648712392, -0.9486722372298325, -0.2900857762856584;
0.06038780559984505, -0.2845396525491149, 0.9567604188416574]
第12幅图像的平移向量:
[0.02339711249059918;
0.01758921411642734;
0.01668696521933025]
第13幅图像的旋转向量:
[-23.14943860211223;
-55.79609570733787;
374.8883623442727]
第13幅图像的旋转矩阵:
[-0.9105426680900612, -0.3751052385965438, -0.1738048030546408;
0.4094619677060464, -0.8762655873686597, -0.2539675518560402;
-0.05703460870142898, -0.3024147489308149, 0.9514685349707512]
第13幅图像的平移向量:
[0.01672892216971765;
0.05051227020869364;
0.01639301274470696]
第14幅图像的旋转向量:
[-65.93936763239849;
-55.3727394136339;
374.7237937527545]
第14幅图像的旋转矩阵:
[0.367459244831695, -0.8974815827567413, -0.2439272678487892;
0.9296741516128715, 0.3618082433210063, 0.0692875666180813;
0.02607058132470423, -0.2522332326982747, 0.9673152129021698]
第14幅图像的平移向量:
[-0.001139151875071493;
0.06435273518035893;
0.03260463159706262]
第15幅图像的旋转向量:
[-79.07956895548273;
-56.42907586094473;
375.1819088729843]
第15幅图像的旋转矩阵:
[-0.3588975990209751, 0.9215988169397343, -0.1478111431261583;
-0.8373458417000329, -0.3878732153291962, -0.3852353958527887;
-0.4123644684070672, -0.0144910125758437, 0.9109037027863507]
第15幅图像的平移向量:
[0.0008842283217561599;
0.05175967201470351;
0.05350211095324433]
第16幅图像的旋转向量:
[-29.32468181664178;
-15.73852789835761;
371.149509602206]
第16幅图像的旋转矩阵:
[-0.3438673702225552, -0.927674466896225, -0.145517405024379;
0.9366633515190242, -0.3498294398829387, 0.01676689930603002;
-0.06646049666812134, -0.1305352307233351, 0.9892136048006182]
第16幅图像的平移向量:
[0.07041492869789512;
-0.02847961794634448;
1.608262252099988]
第17幅图像的旋转向量:
[18.04923159125307;
-9.675169259273686;
370.7762376837953]
第17幅图像的旋转矩阵:
[0.8064649824927742, -0.591251339522583, -0.006007122912088088;
0.5907599935091675, 0.8061383656992961, -0.03381664407826974;
0.0248367083563409, 0.02372317138199226, 0.999410000479084]
第17幅图像的平移向量:
[0.0635535276346698;
-0.02364556431953348;
1.572249817738989]
第18幅图像的旋转向量:
[84.10349533298945;
-14.09704306246227;
370.4070104255048]
第18幅图像的旋转矩阵:
[-0.9014626833866448, -0.05152301504180909, 0.4297794892527461;
0.01870797877284083, -0.996600407802577, -0.08023489700854643;
0.4324523580599194, -0.06428845999663259, 0.8993619693536511]
第18幅图像的平移向量:
[0.07736738691408115;
0.02528168231666154;
1.565945065085318]
第19幅图像的旋转向量:
[83.79642626826502;
57.62548513800356;
366.3211104174334]
第19幅图像的旋转矩阵:
[-0.9019485081745429, 0.1083629795875823, 0.4180267374901011;
0.02521310525661043, -0.9531396321958451, 0.3014782593503028;
0.4311069332834207, 0.2824576184004788, 0.8569507021308136]
第19幅图像的平移向量:
[0.1180513026060211;
0.02745343745277672;
3.12331841573161]