一、基于棋盘格
![]()
#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; }