一、基于棋盘格

#include <iostream>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <filesystem>
using namespace std;
using namespace cv;
Mat modifyImg(Mat src, Mat cameraMatrix, Mat distCoeffs, Mat R, cv::Size sz);
int main()
{
// 配置参数
int points_per_row = 11; // 标定版每行的内点数
int points_per_col = 8; // 标定版每列的内点数
Size block_size(10, 10); // 每个小方格实际大小,如10mm,(w,h)
String pattern = "E:\\temp\\img0822\\*.png";
string savePath = "E:\\temp\\calibration_result_chessboard_0822.txt";
std::cout << "0、搜寻图片……" << endl;
vector<string> img_paths;
cv::glob(pattern, img_paths, false);
size_t count = img_paths.size();
int image_nums = 0; // 有效图片数量统计
Size image_size; // 图片尺寸
Size corner_size(points_per_row, points_per_col); // 标定板每行每列角点个数,共9*6个角点
vector<Point2f> points_per_image; // 缓存每幅图检测到的角点
vector<vector<Point2f>> points_all_images; // 保存检测到的所有角点
vector<string> img_paths2; // 角点提取成功的图片的路径
Mat image_raw; // 彩色图
Mat image_gray; // 灰度图
std::cout << "1、提取角点……" << endl;
for (size_t i = 0; i < count; i++)
{
image_raw = imread(img_paths[i]); // 按照RGB图像读取数据
cvtColor(image_raw, image_gray, COLOR_BGR2GRAY); // 将BGR图转化为灰度图
if (image_nums == 0)
{
image_size.width = image_raw.cols; // 图像的宽,对应着列数(x)
image_size.height = image_raw.rows; // 图像的高,对应着行数(y)
std::cout << "channels = " << image_raw.channels() << endl; // 图像的通道数
std::cout << "image type = " << image_raw.type() << endl; // 数据类型,CV_8UC3
std::cout << "image width = " << image_size.width << endl; // 打印图像宽
std::cout << "image height = " << image_size.height << endl; // 打印图像高
}
bool success = findChessboardCorners(image_gray, corner_size, points_per_image); // 角点检测
if (!success)
{
std::cout << img_paths[i] << "角点提取失败" << endl;
exit(1); // 非正常执行导致退出程序
}
else
{
find4QuadCornerSubpix(image_gray, points_per_image, Size(5, 5)); // 亚像素角点,也可使用cornerSubPix()
points_all_images.push_back(points_per_image); // 保存亚像素角点
img_paths2.push_back(img_paths[i]);
drawChessboardCorners(image_raw, corner_size, points_per_image, 1);
namedWindow("Image_show", WINDOW_NORMAL);
resizeWindow("Image_show", 1000, 1000.0 * image_size.height / image_size.width);
imshow("Image_show", image_raw);
waitKey(1000);
}
image_nums++;
}
std::cout << "image_nums = " << image_nums << endl; // 输出有效图像数目
std::cout << "2、开始计算角点3D坐标……" << endl;
vector<Point3f> points3D_per_image; // 初始化角点三维坐标,从左到右,从上到下
Point3f point3D; // 3D点(x,y,z)
for (int i = 0; i < corner_size.height; i++) // 第i行---y
{
for (int j = 0; j < corner_size.width; j++) // 第j列---x
{
point3D = Point3f(block_size.width * j, block_size.height * i, 0);
points3D_per_image.push_back(point3D);
}
}
vector<vector<Point3f>> points3D_all_images(image_nums, points3D_per_image); // 保存所有图像角点的三维坐标
int point_counts = corner_size.area(); // 每张图片上角点个数
std::cout << "3、开始标定相机……" << endl;
Mat cameraMat(3, 3, CV_32FC1, Scalar::all(0)); // 内参矩阵3*3
Mat distCoeffs(1, 5, CV_32FC1, Scalar::all(0)); // 畸变矩阵1*5,既考虑径向畸变,又考虑切向
vector<Mat> rotationMat; // 旋转矩阵
vector<Mat> translationMat; // 平移矩阵
cv::calibrateCamera(points3D_all_images, points_all_images, image_size, cameraMat, distCoeffs, rotationMat, translationMat, 0); // 标定
ofstream fout(savePath);
fout << "相机标定" << endl;
// 打印标定数据
fout << "相机内参数矩阵:" << endl << cameraMat << endl << endl;
fout << "相机的畸变系数:" << endl << distCoeffs << endl << endl;
Mat rotateMatrix = Mat(3, 3, CV_64F, Scalar::all(0));
for (int i = 0; i < rotationMat.size(); i++)
{
Rodrigues(rotationMat[i], rotateMatrix);
fout << "第" << i << "张图片的旋转矩阵:" << endl << rotateMatrix << endl;
fout << "第" << i << "张图片的平移向量:" << endl << translationMat[i] << endl << endl;
}
std::cout << "4、标定评价……" << endl;
double total_err = 0.0; // 所有图像平均误差总和
double err = 0.0; // 每幅图像的平均误差
vector<Point2f> points_reproject; // 重投影点
Mat rotate_Mat = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 保存旋转矩阵
for (int i = 0; i < image_nums; i++)
{
points_per_image = points_all_images[i]; // 第i张图像提取角点
points3D_per_image = points3D_all_images[i]; // 第i张图像中角点的3D坐标
projectPoints(points3D_per_image, rotationMat[i], translationMat[i], cameraMat, distCoeffs, points_reproject); // 重投影
Rodrigues(rotationMat[i], rotate_Mat); // 将旋转向量通过罗德里格斯公式转换为旋转矩阵
Mat detect_points_Mat(1, points_per_image.size(), CV_32FC2); // 变为1*S的矩阵,2通道保存提取角点的像素坐标
Mat points_reproj_Mat(1, points_reproject.size(), CV_32FC2); // 变为1*S的矩阵,2通道保存投影角点的像素坐标
for (int j = 0; j < points_per_image.size(); j++)
{
detect_points_Mat.at<Vec2f>(0, j) = Vec2f(points_per_image[j].x, points_per_image[j].y);
points_reproj_Mat.at<Vec2f>(0, j) = Vec2f(points_reproject[j].x, points_reproject[j].y);
}
err = norm(points_reproj_Mat, detect_points_Mat, NormTypes::NORM_L2); // 计算两者之间的误差
total_err += err /= point_counts; // 总体平均误差为 = total_err / image_nums 像素
}
fout << "标定总体平均误差:" << endl << total_err << endl << endl;
fout.close();
// 相机内参数矩阵 ---- cameraMat
// 相机的畸变系数 ---- distCoeffs、
std::cout << "5、消除畸变……" << endl;
cv::Mat Rotate = cv::Mat::eye(3, 3, CV_64F); // 单位旋转矩阵
for (int i = 0; i < image_nums; i++)
{
image_gray = imread(img_paths2[i], IMREAD_GRAYSCALE);
Mat undistortedImg = modifyImg(image_gray, cameraMat, distCoeffs, Mat(), image_size);
imwrite("E:\\temp\\0822\\img_"+to_string(i)+".png", undistortedImg);
// 展示
Mat imgConcat;
cv::hconcat(image_gray, undistortedImg, imgConcat);
namedWindow("Image_show", WINDOW_NORMAL);
resizeWindow("Image_show", image_size.width*2, image_size.height);
imshow("Image_show", imgConcat);
waitKey(1000);
}
return 0;
}
Mat modifyImg(Mat src, Mat cameraMatrix, Mat distCoeffs, Mat R, Size sz)
{
// 消除畸变
cv::Mat P = cv::getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, sz, 1, sz, 0, false);
cv::Mat map1, map2;
cv::initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, sz, CV_16SC2, map1, map2);
cv::Mat undistortedImg;
cv::remap(src, undistortedImg, map1, map2, cv::INTER_LINEAR);
return undistortedImg;
}
二、基于对称圆形图案

#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstdio>
#include <fstream>
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace std;
bool runCalibration(Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,
vector<vector<Point2f> > imagePoints, float squareSize, Size boardSize, string savePath);
int main()
{
// 配置参数
float squareSize = 50;
Size boardSize = Size(7, 7);
float grid_width = squareSize * (boardSize.width - 1);
string pattern = "C:\\Users\\114592\\source\\repos\\cameraCalibrationByCircle\\images\\*.png";
string savePath = "E:\\temp\\calibration_result_circle.txt";
// 获取图片路径列表
vector<string> img_paths;
cv::glob(pattern, img_paths, false);
size_t count = img_paths.size();
// 提取角点
vector<vector<Point2f> > imagePoints;
vector<Point2f> pointBuf;
Mat view, cameraMatrix, distCoeffs;
Size imageSize;
for (size_t i = 0; i < count; i++)
{
view = imread(img_paths[i], IMREAD_COLOR);
imageSize = view.size();
bool found = findCirclesGrid(view, boardSize, pointBuf);
if (found)
{
imagePoints.push_back(pointBuf);
drawChessboardCorners(view, boardSize, Mat(pointBuf), found);
}
imshow("Image View", view);
waitKey(500);
}
runCalibration(imageSize, cameraMatrix, distCoeffs, imagePoints, squareSize, boardSize, savePath);
// 去除畸变
Mat rview, map1, map2;
Mat newCameraMatrix = getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0);
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), newCameraMatrix, imageSize, CV_16SC2, map1, map2);
const char ESC_KEY = 27;
for (size_t i = 0; i < count; i++)
{
view = imread(img_paths[i], IMREAD_COLOR);
remap(view, rview, map1, map2, INTER_LINEAR);
imshow("Image View", rview);
char c = (char)waitKey();
if (c == ESC_KEY || c == 'q' || c == 'Q')
break;
}
return 0;
}
static double computeReprojectionErrors(const vector<vector<Point3f> >& objectPoints,
const vector<vector<Point2f> >& imagePoints, const vector<Mat>& rvecs, const vector<Mat>& tvecs,
const Mat& cameraMatrix, const Mat& distCoeffs, vector<float>& perViewErrors)
{
vector<Point2f> imagePoints2;
size_t totalPoints = 0;
double totalErr = 0, err;
perViewErrors.resize(objectPoints.size());
for (size_t i = 0; i < objectPoints.size(); ++i)
{
projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
err = norm(imagePoints[i], imagePoints2, NORM_L2);
size_t n = objectPoints[i].size();
perViewErrors[i] = (float)std::sqrt(err * err / n);
totalErr += err * err;
totalPoints += n;
}
return std::sqrt(totalErr / totalPoints);
}
bool runCalibration(Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,
vector<vector<Point2f> > imagePoints, float squareSize, Size boardSize, string savePath)
{
vector<Mat> rvecs, tvecs;
vector<float> reprojErrs;
double totalAvgErr = 0;
vector<Point3f> newObjPoints;
cameraMatrix = Mat::eye(3, 3, CV_64F);
distCoeffs = Mat::zeros(8, 1, CV_64F);
ofstream fout(savePath);
fout << "相机标定" << endl;
// 计算标定板角点的物理坐标
vector<vector<Point3f> > objectPoints(1);
objectPoints[0].clear();
for (int i = 0; i < boardSize.height; ++i)
for (int j = 0; j < boardSize.width; ++j)
objectPoints[0].push_back(Point3f(j * squareSize, i * squareSize, 0));
newObjPoints = objectPoints[0];
objectPoints.resize(imagePoints.size(), objectPoints[0]);
// Find intrinsic and extrinsic camera parameters
double rms = calibrateCameraRO(objectPoints, imagePoints, imageSize, -1,
cameraMatrix, distCoeffs, rvecs, tvecs, newObjPoints, 0);
cout << "Re-projection error reported by calibrateCamera: " << rms << endl;
// 打印标定数据
fout << "相机内参数矩阵:" << endl << cameraMatrix << endl << endl;
fout << "相机的畸变系数:" << endl << distCoeffs << endl << endl;
Mat rotateMatrix = Mat(3, 3, CV_64F, Scalar::all(0));
for (int i = 0; i < rvecs.size(); i++)
{
Rodrigues(rvecs[i], rotateMatrix);
fout << "第" << i << "张图片的旋转矩阵:" << endl << rotateMatrix << endl;
fout << "第" << i << "张图片的平移向量:" << endl << tvecs[i] << endl << endl;
}
objectPoints.clear();
objectPoints.resize(imagePoints.size(), newObjPoints);
totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints,
rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs);
fout << "标定总体平均误差:" << endl << totalAvgErr << endl << endl;
fout.close();
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
cout << (ok ? "Calibration succeeded" : "Calibration failed")
<< ". avg re projection error = " << totalAvgErr << endl;
return ok;
}
三、基于非对称圆形图案

#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstdio>
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace std;
bool runCalibration(Size imageSize, Mat& cameraMatrix, Mat& distCoeffs,
vector<vector<Point2f> > imagePoints, Size boardSize, float squareSize);
int main()
{
float squareSize = 10;
Size boardSize(4, 11); // 行数,列数
string pattern = "E:\\temp\\imgCircle4";
vector<string> img_paths;
cv::glob(pattern, img_paths, false);
size_t count = img_paths.size();
vector<vector<Point2f> > imagePoints;
Mat cameraMatrix, distCoeffs, view;
Size imageSize;
const char ESC_KEY = 27;
for (size_t i = 0; i < count; i++)
{
view = imread(img_paths[i], IMREAD_COLOR);
imageSize = view.size();
vector<Point2f> pointBuf;
bool found = findCirclesGrid(view, boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID);
if (found)
{
imagePoints.push_back(pointBuf);
drawChessboardCorners(view, boardSize, Mat(pointBuf), found);
}
else
{
cout << "cannot find corner!" << endl;
continue;
}
cv::imshow("Image View", view);
char key = (char)waitKey(500);
if (key == ESC_KEY)
break;
}
runCalibration(imageSize, cameraMatrix, distCoeffs, imagePoints, boardSize, squareSize);
Mat rview, map1, map2;
Mat newCameraMatrix = getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0);
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), cameraMatrix, imageSize, CV_16SC2, map1, map2);
for (size_t i = 0; i < count; i++)
{
view = imread(img_paths[i], IMREAD_COLOR);
cv::remap(view, rview, map1, map2, INTER_LINEAR);
cv::imshow("Image View", rview);
char c = (char)waitKey();
if (c == ESC_KEY || c == 'q' || c == 'Q')
break;
}
return 0;
}
static double computeReprojectionErrors(const vector<vector<Point3f> > & objectPoints,
const vector<vector<Point2f> > & imagePoints, const vector<Mat> & rvecs, const vector<Mat> & tvecs,
const Mat & cameraMatrix, const Mat & distCoeffs, vector<float> & perViewErrors)
{
vector<Point2f> imagePoints2;
size_t totalPoints = 0;
double totalErr = 0, err;
perViewErrors.resize(objectPoints.size());
for (size_t i = 0; i < objectPoints.size(); ++i)
{
projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
err = norm(imagePoints[i], imagePoints2, NORM_L2);
size_t n = objectPoints[i].size();
perViewErrors[i] = (float)std::sqrt(err * err / n);
totalErr += err * err;
totalPoints += n;
}
return std::sqrt(totalErr / totalPoints);
}
bool runCalibration(Size imageSize, Mat & cameraMatrix, Mat & distCoeffs,
vector<vector<Point2f> > imagePoints, Size boardSize, float squareSize)
{
vector<Mat> rvecs, tvecs;
vector<float> reprojErrs;
double totalAvgErr = 0;
vector<Point3f> newObjPoints;
cameraMatrix = Mat::eye(3, 3, CV_64F);
distCoeffs = Mat::zeros(8, 1, CV_64F);
vector<vector<Point3f> > objectPoints(1);
objectPoints[0].clear();
for (int i = 0; i < boardSize.height; i++)
for (int j = 0; j < boardSize.width; j++)
objectPoints[0].push_back(Point3f((2 * j + i % 2) * squareSize, i * squareSize, 0));
newObjPoints = objectPoints[0];
objectPoints.resize(imagePoints.size(), objectPoints[0]);
//Find intrinsic and extrinsic camera parameters
double rms;
rms = calibrateCameraRO(objectPoints, imagePoints, imageSize, -1,
cameraMatrix, distCoeffs, rvecs, tvecs, newObjPoints);
cout << "Re-projection error reported by calibrateCamera: " << rms << endl;
objectPoints.clear();
objectPoints.resize(imagePoints.size(), newObjPoints);
totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs,
cameraMatrix, distCoeffs, reprojErrs);
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
cout << (ok ? "Calibration succeeded" : "Calibration failed")
<< ". avg re projection error = " << totalAvgErr << endl;
return ok;
}