当前位置:网站首页>基于OpenCV的相机标定流程
基于OpenCV的相机标定流程
2022-07-30 05:44:00 【柠檬甜瓜】
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快速入门》
边栏推荐
猜你喜欢
随机推荐
Shardingsphere depots table and configuration example
Bubble sort, selection sort, insertion sort, quick sort
Xcode 建立 UIKit 项目(Hello World)
XMLBean的基础运用
sql中 exists的用法
正则表达式语法详解及实用实例
冒泡排序、选择排序、插入排序、快速排序
[Ten years of network security engineers finishing] - 100 penetration testing tools introduction
AAcell五号文档室——跨平台文件传输的小室一间一间的
sql concat()函数
Biome-BGC 生态系统模型与应用
MySQL - Function and Constraint Commands
MySQL 数据类型及占用空间
Online sql editing query tool sql-editor
R语言 生态环境领域应用
边境的悍匪—机器学习实战:第十二章 使用TensorFlow自定义模型和训练
mysql delete duplicate data in the table, (retain only one row)
Invalid bound statement (not found)出现的原因和解决方法
TDengineGUI cannot connect to TDengine
The types of data structures and MySQL index









