本文主要记录相机标定的代码实现,关于相机标定的原理可以参考:https://blog.csdn.net/weixin_43843780/article/details/89294131
本文相机标定的照片采用OpenCV提供的图片,位置:...\opencv\opencv\sources\samples\data中left01~left14.jpg
相机标定在OpenCV中实现的主要步骤为:
1. 查找每幅图像中的角点坐标和亚像素角点坐标
2. 构建每幅图像世界坐标系中的角点坐标
3. 调用相机标定算子计算相机的内参和外参
4. 计算此次相机标定的误差
关于详细的讲解注释在代码中
//棋盘格的大小,每行/列的角点个数
Size boardsize = Size(9, 6);
//记录每幅图中的角点
vector> corners_Seq;
//记录所有图像中的角点总和
int count = 0;
//棋盘格图像
Mat srcImg;
//图像的个数
int image_count = 0;
for (int i = 1; i <= 13; i++)
{
//记录每幅图像的名称
string Img_files = to_string(i) + ".jpg";
//读取图像,若读取失败则发送提示
srcImg = imread(Img_files);
if (!srcImg.data)
{
cout << "读取"< corners;
bool cornerfound = findChessboardCorners(grayImg, boardsize, corners, CALIB_CB_ADAPTIVE_THRESH);
if (!cornerfound)
{
cout << "角点查找失败" << endl;
continue;
}
//根据棋盘格图像角点寻找亚像素角点
cornerSubPix(grayImg, corners, Size(5, 5), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.001));
//累计角点的个数
count += corners.size();
//将本幅图像的角点添加到列表中
corners_Seq.push_back(corners);
//图像个数+1
image_count++;
}
//测量每幅图像中每个格子的大小
Size square_size = Size(26, 26);
//计算世界坐标系中角点的座标
vector> object_Points;
for (int i = 0; i < image_count; i++)
{
//记录当前图像中角点的世界坐标值
vector tempPointset;
for (int h = 0; h < boardsize.height; h++)
{
for (int w = 0; w < boardsize.width; w++)
{
Point3f tempPoint;
tempPoint.x = w * square_size.width;
tempPoint.y = h * square_size.height;
tempPoint.z = 0;
tempPointset.push_back(tempPoint);
}
}
object_Points.push_back(tempPointset);
}
//记录相机
// fx, 0, cx
// 0, fy, cy
// 0, 0, 1
//矩阵的参数
Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0));
//记录相机畸变的参数
Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));
//记录每幅图像的旋转矩阵
vector rotation_vectors;
//记录每幅图像的位移矩阵
vector translation_vectors;
//相机标定
calibrateCamera(object_Points, corners_Seq, srcImg.size(), intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, 0);
//对标定参数的评估
double total_err = 0.0;
double err = 0.0;
vector image_points2;
for (int i = 0; i < image_count; i++)
{
//获取每幅图像世界坐标系下的角点坐标
vector tempPointSet = object_Points[i];
//根据相机的内参和外参计算得出图像坐标系下的角点坐标
projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs, image_points2);
//记录真实图像坐标系下的角点坐标
vector tempImagePoint = corners_Seq[i];
//将计算得出的图像坐标系下的角点 和 真实图像坐标系下的角点坐标分别放入两个图像中
Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);
Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);
for (size_t i = 0; i != tempImagePoint.size(); i++)
{
image_points2Mat.at(0, i) = Vec2f(image_points2[i].x, image_points2[i].y);
tempImagePointMat.at(0, i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y);
}
//计算两个图像的像素误差
err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
//汇总像素误差
total_err += err /= 54;//point_counts[0]
cout << "第" << i << "幅图像的平均误差:" << err << "像素" << endl;
}
cout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;
//将相机标定参数写入xml文件中
FileStorage fs1("calibration.xml", FileStorage::WRITE);
fs1 << "intrinsic_matrix" << intrinsic_matrix;
fs1 << "distortion_coeffs" << distortion_coeffs;
fs1.release();
图像矫正代码
//矫正图像
//读取xml中的相机标定参数
FileStorage fs;
fs.open("calibration.xml", FileStorage::READ);
Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0));
Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));
fs["intrinsic_matrix"] >> intrinsic_matrix;
fs["distortion_coeffs"] >> distortion_coeffs;
//读取标定相机拍摄的图片
Mat src = imread("1.jpg");
imshow("src", src);
Mat mapx = Mat(src.size(), CV_32FC1);
Mat mapy = Mat(src.size(), CV_32FC1);
Mat R = Mat::eye(3, 3, CV_32F);
//采用initUndistortRectifyMap+remap进行图像矫正
initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, src.size(), 1, src.size(), 0), src.size(), CV_32FC1, mapx, mapy);
Mat dst = src.clone();
remap(src, dst, mapx, mapy, INTER_LINEAR);
imshow("dst", dst);
参考:https://blog.csdn.net/qq_40250862/article/details/82912698