单目相机标定C++代码记录

一、基于棋盘格

#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值