栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > C/C++/C#

基于opencv的相机标定C++代码

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于opencv的相机标定C++代码

基于棋盘格标定版的相机标定代码实现

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	string dir = "E:\myProgame\CameraCalibration\CameraCalibration\";  // 标定图片所在文件夹
	ifstream fin(dir + "calibdata.txt");  // 读取标定图片的路径,与cpp程序在同一路径下
	if (!fin)
	{
		cerr << "没有找到文件" << endl;  // 检测是否读取到文件,以输入方式打开文件
		return -1;
	}
	ofstream fout(dir + "calibration_result.txt"); // 输出结果保存在此文本文件下,以输出方式打开文件
	
	std::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); // 标定板每行每列角点个数,共11 * 8个角点
	vector points_per_image;                            // 缓存每幅图检测到的角点
	points_per_image.clear();
	vector> points_all_images;                   // 用一个二维数组保存检测到的所有角点
	string image_file_name;											 // 声明一个文件名的字符串

	while (getline(fin, image_file_name)) // 逐行读取,将行读入字符串,第三个参数在不设置的情况下系统默认该字符为'n'
	{
		image_nums++;
		cv::Mat image_raw = cv::imread(image_file_name);  // image_file_name是读取的.txt文件里面的一行
		if (image_nums == 1)
		{
			std::cout << "channels = " << image_raw.channels() << endl;  // 图像的通道数
			std::cout << "image type = " << image_raw.type() << endl;  // 数据类型,CV_8UC3
			image_size.width = image_raw.cols;  // 图像的宽,对应着列数(x)
			image_size.height = image_raw.rows; // 图像的高,对应着行数(y)
			std::cout << "image_size.width = " << image_size.width << endl;
			std::cout << "image_size.height = " << image_size.height << endl;
		}
		
		cv::Mat image_gray; // 存储灰度图的矩阵
		cv::cvtColor(image_raw, image_gray, COLOR_BGR2GRAY); // 将BGR图转化为灰度图
		bool success = cv::findChessboardCorners(image_gray, corner_size, points_per_image); // 角点检测
		if (!success)
		{
			std::cout << "can not find the corners " << endl;
			exit(1);
		}
		else
		{
			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); // 保存亚像素角点
			cv::drawChessboardCorners(image_raw, corner_size, points_per_image, success); // 将角点连线
			cv::imshow("Camera calibration", image_raw);  // 显示
			cv::waitKey(0); // 等待按键输入
		}
	}
	cv::destroyAllWindows();
	std::cout << "image_sum_nums = " << points_all_images.size() << 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 rotationMat;                            // 旋转矩阵
	vector translationMat;                         // 平移矩阵
					
	vector 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> points3D_all_images(image_nums, points3D_per_image); // 保存所有图像角点的三维坐标, z=0
	int point_counts = corner_size.area(); // 每张图片上角点个数 (width*height)

	
	
	// 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 points_reproject; // 重投影点
	fout << "每幅图像的标定误差:n";
	for (int i = 0; i < image_nums; i++)
	{
		vector points3D_per_image = points3D_all_images[i]; // 第i张图像中角点的真实世界坐标
		// 通过之前标定得到的相机内外参,对三维点进行重投影,三维到二维图像
		cv::projectPoints(points3D_per_image, rotationMat[i], translationMat[i], camera_K, distCoeffs, points_reproject);
		// 计算两者之间的误差
		vector 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(0, j) = cv::Vec2f(detect_points[j].x, detect_points[j].y);
			points_reproject_Mat.at(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;
		fout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
	}
	fout << "总体平均误差为: " << total_err / image_nums << "像素" << endl << endl;

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

	return 0;
}

拍摄图像示例

 

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1037440.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号