基于OpenCV的相机标定流程

1、根据真实世界与图像坐标角点坐标对应关系计算相机内参矩阵与相机外参矩阵的积,即矩阵H;
2、根据图像的单应性矩阵构建点对应关系求解相机内参(理论至少需要三张图,因为内参矩阵构建的对称矩阵B有6个自由度,一张图只能提供两个方程);此处可参考:https://blog.csdn.net/weixin_41480034/article/details/121759128中(三,2)
3、求解相机外参
4、求解相机畸变因子

#include <iostream>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc, char **argv)
{
	string dir = "C:\\Users\\15734\\Desktop\\Reconstruction\\calibration\\calibration\\calibration\\";  //标定图片所在文件夹
	ifstream fin(dir + "calibdata.txt");	//读取标定图片的路径,与cpp程序在同一路径下
	if (!fin)								//检测是否读取到文件,以输入方式打开文件
	{
		cerr << "没有找到文件" << endl;
		return -1;
	}
	ofstream fout(dir + "calibration_result.txt"); //输出结果保存在此文本文件下,,以输出方式打开文件
	//依次读取每一幅图片,从中提取角点
	cout << "开始提取角点……" << endl;
	int image_nums = 0;  //图片数量
	cv::Size image_size; //图片尺寸
	int points_per_row = 11;  //每行的内点数
	int points_per_col = 8;   //每列的内点数
	cv::Size corner_size = cv::Size(points_per_row, points_per_col); //标定板每行每列角点个数,共points_per_row * points_per_col个角点
	vector<cv::Point2f> points_per_image;                            //缓存每幅图检测到的角点
	points_per_image.clear();
	vector<vector<cv::Point2f>> points_all_images;                   //用一个二维数组保存检测到的所有角点
	string image_file_name;											 //声明一个文件名的字符串

	while (getline(fin, image_file_name)) //逐行读取,将行读入字符串,第三个参数在不设置的情况下系统默认该字符为'\n'
	{
		image_nums++;
		//读入图片
		string tmp = dir + image_file_name;
		cv::Mat image_raw = cv::imread(image_file_name);  //image_file_name是读取的.txt文件里面的一行
		if (image_nums == 1)
		{
			// cout<<"channels = "<<image_raw.channels()<<endl;
			// cout<<image_raw.type()<<endl;  //CV_8UC3
			image_size.width = image_raw.cols;  //图像的宽对应着列数(x)
			image_size.height = image_raw.rows; //图像的高对应着行数(y)
			cout << "image_size.width = " << image_size.width << endl;
			cout << "image_size.height = " << image_size.height << endl;
		}
		//角点检测
		cv::Mat image_gray;                               //存储灰度图的矩阵
		cv::cvtColor(image_raw, image_gray, COLOR_BGR2GRAY); //将BGR图转化为灰度图
		// cout<<"image_gray.type() = "<<image_gray.type()<<endl;  //CV_8UC1
		//step1 提取角点
		bool success = cv::findChessboardCorners(image_gray, corner_size, points_per_image);	//corner_size = points_per_row * points_per_col,每一行内点数乘以每一列内点数
		if (!success)
		{
			cout << "can not find the corners " << endl;
			exit(1);
		}
		else
		{
			//亚像素精确化(两种方法)
			//step2 亚像素角点
			cv::find4QuadCornerSubpix(image_gray, points_per_image, cv::Size(5, 5));
			 //cornerSubPix(image_gray,points_per_image,Size(5,5));
			points_all_images.push_back(points_per_image); //保存亚像素角点
			//在图中画出角点位置
			//step3 角点可视化
			cv::drawChessboardCorners(image_raw, corner_size, points_per_image, success); //将角点连线
			//cv::imshow("Camera calibration", image_raw);
			cv::waitKey(0); //等待按键输入
		}
	}
	cv::destroyAllWindows();
	//输出图像数目
	int image_sum_nums = points_all_images.size();
	cout << "image_sum_nums = " << image_sum_nums << endl;

	//开始相机标定
	cv::Size block_size(45, 45);                            //每个小方格实际大小, 只会影响最后求解的平移向量t
	cv::Mat camera_K(3, 3, CV_32FC1, cv::Scalar::all(0));   //内参矩阵3*3
	cv::Mat distCoeffs(1, 5, CV_32FC1, cv::Scalar::all(0)); //畸变矩阵1*5,既考虑径向畸变,又考虑切向
	vector<cv::Mat> rotationMat;                            //旋转矩阵
	vector<cv::Mat> translationMat;                         //平移矩阵
	//初始化角点三维坐标,从左到右,从上到下!!!
	vector<cv::Point3f> points3D_per_image;
	for (int i = 0; i < corner_size.height; i++)
	{
		for (int j = 0; j < corner_size.width; j++)
		{
			points3D_per_image.push_back(cv::Point3f(block_size.width * j, block_size.height * i, 0));
		}
	}
	//都是以第一个角点作为真实世界原点,创建Vector容器points3D_all_images时就进行初始化,image_nums个points3D_per_image元素
	vector<vector<cv::Point3f>> points3D_all_images(image_nums, points3D_per_image);        //保存所有图像角点的三维坐标, z=0

	int point_counts = corner_size.area(); //每张图片上角点个数,(width*height)
	//!标定
	/**
	* points3D_all_images: 真实三维坐标
	* points_all_images: 提取的角点
	* image_size: 图像尺寸
	* camera_K : 内参矩阵K
	* distCoeffs: 畸变参数,径向畸变k1,k2,切向畸变p1,p2,径向畸变k3
	* rotationMat: 每个图片的旋转向量
	* translationMat: 每个图片的平移向量
	* */
	//step4 标定,points3D_all_images:角点对应的3D点坐标,points_all_images从图像中提取的角点坐标
	cv::calibrateCamera(points3D_all_images, points_all_images, image_size, camera_K, distCoeffs, rotationMat, translationMat, 0);

	//step5 对标定结果进行评价
	double total_err = 0.0;               //所有图像平均误差总和
	double err = 0.0;                     //每幅图像的平均误差
	vector<cv::Point2f> points_reproject; //重投影点
	cout << "\n\t每幅图像的标定误差:\n";
	fout << "每幅图像的标定误差:\n";
	for (int i = 0; i < image_nums; i++)
	{
		vector<cv::Point3f> points3D_per_image = points3D_all_images[i]; //第i张图像中角点的真实世界坐标
		//通过之前标定得到的相机内外参,对三维点进行重投影,三维到二维图像
		cv::projectPoints(points3D_per_image, rotationMat[i], translationMat[i], camera_K, distCoeffs, points_reproject);
		//计算两者之间的误差
		vector<cv::Point2f> detect_points = points_all_images[i];  //提取到的图像角点
		cv::Mat detect_points_Mat = cv::Mat(1, detect_points.size(), CV_32FC2); //变为1*detect_points.size()的矩阵,2通道保存提取角点的像素坐标
		cv::Mat points_reproject_Mat = cv::Mat(1, points_reproject.size(), CV_32FC2);  //2通道保存投影角点的像素坐标
		for (int j = 0; j < detect_points.size(); j++)
		{
			detect_points_Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(detect_points[j].x, detect_points[j].y);
			points_reproject_Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(points_reproject[j].x, points_reproject[j].y);
		}
		err = cv::norm(points_reproject_Mat, detect_points_Mat, cv::NormTypes::NORM_L2);
		total_err += err /= point_counts;
		cout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
		fout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
	}
	cout << "总体平均误差为: " << total_err / image_nums << "像素" << endl;
	fout << "总体平均误差为: " << total_err / image_nums << "像素" << endl;
	cout << "评价完成!" << endl;

	//将标定结果写入txt文件
	cv::Mat rotate_Mat = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); //保存旋转矩阵
	cout << "\n相机内参数矩阵:" << endl;
	cout << camera_K << endl << endl;
	fout << "\n相机内参数矩阵:" << endl;
	fout << camera_K << endl << endl;
	cout << "畸变系数:\n";
	cout << distCoeffs << endl << endl << endl;
	fout << "畸变系数:\n";
	fout << distCoeffs << endl << endl << endl;
	//cout << "第一张图像法旋转向量 = " << rotationMat[0] << endl;
	//cv::Mat tmp = rotationMat[0].clone();
	for (int i = 0; i < image_nums; i++)
	{
		cv::Rodrigues(rotationMat[i], rotate_Mat); //将旋转向量通过罗德里格斯公式转换为旋转矩阵,旋转方向与旋转轴一致,而长度等于旋转角
		fout << "第" << i + 1 << "幅图像的旋转矩阵为:" << endl;
		fout << rotate_Mat << endl;
		fout << "第" << i + 1 << "幅图像的平移向量为:" << endl;
		fout << translationMat[i] << endl
			<< endl;
	}
	fout << endl;
	fout.close();

	return 0;
}

参考:
https://blog.csdn.net/qq_41253960/article/details/124928140
《OpenCV4.3快速入门》

### 回答1: OpenCV是一种开源的计算机视觉库,通过使用OpenCV库可以实现图像处理、目标检测和跟踪等功能。张正是一种摄像机标定技术,它是通过对摄像机进行标定,获取摄像机的内参和外参参数,从而实现对摄像机成像的准确控制和测量。 张正的标定流程主要包括以下几个步骤: 1. 准备标定板:标定板是一张已知尺寸和布局的特殊图片,一般使用棋盘格作为标定板。 2. 采集标定图像:需要将标定板放置在不同的位置和角度,并通过摄像机进行拍摄。 3. 提取角点:通过对标定图像进行处理,提取出标定板上的角点坐标。OpenCV提供了一些函数来实现角点的自动提取。 4. 标定参数计算:通过已知的标定板尺寸和角点坐标,结合摄像机内外参的数学模型,计算出摄像机的内参和外参参数。 5. 重投影误差评估:将标定参数应用到标定图像上,生成从摄像机成像平面到标定板上对应点的映射关系。通过计算映射点与实际角点的误差,评估标定的准确性。 重投影误差是衡量张正标定结果的一种指标,它表示通过标定参数计算得到的映射点与实际角点之间的差异。通常使用平均重投影误差来评估标定的准确性,即计算所有标定图像上映射点与实际角点之间的欧氏距离平均值。 综上所述,OpenCV中的张正标定流程主要包括准备标定板、采集标定图像、提取角点、标定参数计算和重投影误差评估等步骤,通过这一过程可以获取到摄像机的准确内参和外参参数,从而实现精确的图像测量和相机控制。 ### 回答2: OpenCV中的相机标定,可以通过对相机内外参数进行估计和校准来提高图像处理和计算机视觉算法的准确性。下面我将回答您关于opencv张正有标定流程、误差和重投影的问题。 1. 张正有标定流程:张正有标定是一个基于多个摄像机位置观察到的特定物体上特征点的方法,实现相机的准确定位和定向。标定流程包括以下步骤: - 确定特征点:选择物体上的特征点,并在每个特征点中标记一致的坐标。 - 拍摄多张图片:将摄像机从不同的位置拍摄多张图片,确保特征点能够在每个图像中被观察到。 - 检测特征点:使用特征检测算法,如SIFT或SURF,检测每张图片中的特征点。 - 匹配特征点:对于每个特征点,在所有图像中找到其对应的匹配点。 - 估计相机参数:通过解算几何约束和最小二乘法,估计相机的内外参数,例如相机的焦距、畸变系数和坐标系变换。 - 评估标定结果:通过计算重投影误差和观测点与估计平面之间的距离来评估标定的准确性。 2. 误差:在相机标定中,误差通常分为重投影误差和观测误差。 - 重投影误差:是指通过将标定得到的内外参数应用于标定图像中的特征点,并计算它们在图像平面上的投影点与实际观测到的特征点之间的距离。 - 观测误差:是指标定图像中特征点的观测坐标与其在物体上标记的真实坐标之间的差异。 3. 重投影:重投影是指根据标定的相机内外参数,将物体上的三维坐标投影到图像平面上的过程。通过比较重投影点与实际观测到的特征点之间的距离,可以评估相机标定的准确性。如果重投影误差较小,则表示相机标定较为准确。 总结:在OpenCV中,张正有标定是一个常用的相机校准方法,可以通过检测特征点、拍摄多张图像、估计相机参数来提高图像处理和计算机视觉算法的准确性。标定误差评估和重投影过程可以帮助我们评估标定的准确性,并进一步优化相机的使用效果。 ### 回答3: OpenCV张正有标定流程是用于相机校准的方法,目的是确定相机的内外参数,以消除图像中的畸变。其中,内参数包括焦距、主点坐标等,外参数包括相机的位姿和旋转矩阵。 张正有标定流程包括以下几个步骤: 1. 准备:采集一组已知位置的标定板图像。标定板是一个包含已知物理尺寸的棋盘格,其角点应当在图像中明确可见。 2. 检测角点:使用OpenCV函数在标定板图像中检测棋盘格的角点,即通过像素坐标来定位棋盘格角点的位置。 3. 生成三维-二维对应点集:将已知的物理世界点和相应的图像点建立对应关系。这样的对应关系用于后续的标定计算。 4. 计算相机矩阵:使用已知的标定板图像和对应点集,通过最小二乘法计算相机矩阵。相机矩阵包括内参数和外参数。 5. 评估标定结果:通过重投影误差来评估标定的准确性。重投影误差是指将计算得到的相机矩阵应用到标定板图像上,得到的重投影点与真实图像点之间的距离。越小的重投影误差表示标定结果越准确。 6. 畸变校正:使用计算得到的相机矩阵对图像进行畸变校正,以去除图像中的畸变。 注:重投影是指将物体在三维空间中的坐标投影到二维图像上,再通过逆变换将二维图像坐标映射回三维空间中,与其原始三维坐标对比,产生重投影误差。重投影误差表示了相机矩阵的准确性。通过最小化重投影误差来优化相机矩阵的计算结果。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值