相机标定(2)opencv2实现

article/2025/9/25 23:22:18

相机标定步骤

        OpenCV使用棋盘格板进行标定,如下图所示。为了标定相机,我们需要输入一系列三维点和它们对应的二维图像点

1、在黑白相间的棋盘格上,二维图像点很容易通过角点检测找到。

2、而对于真实世界中的三维点呢?由于我们采集中,是将相机放在一个地方,而将棋盘格定标板进行移动变换不同的位置,然后对其进行拍摄。所以我们需要知道(X,Y,Z)的值。但是简单来说,我们定义棋盘格所在平面为XY平面,即Z=0。对于定标板来说,我们可以知道棋盘格的方块尺寸,例如30mm,这样我们就可以把棋盘格上的角点坐标定义为(0,0,0),(30,0,0),(60,0,0),···,这个结果的单位是mm。 3D点称为object points,2D图像点称为image points。

                              

      为了找到棋盘格模板,我们使用openCV中的函数findChessboardCorners()。我们也需要告诉程序我们使用的模板是什么规格的,例如8*8的棋盘格或者5*5棋盘格等对应函数参数 Size patternSize,建议使用x方向和y方向个数不相等的棋盘格模板。下面实验中,我们使用的是10*7的棋盘格,每个方格边长是20mm,即含有9*6的内部角点。这个函数如果检测到模板,会返回对应的角点,并返回true。当然不一定所有的图像都能找到需要的模板,所以我们可以使用多幅图像进行定标。除了使用棋盘格,我们还可以使用圆点阵,对应的函数为findCirclesGrid()。 

     找到角点后,我们可以使用cornerSubPix()可以得到更为准确的角点像素坐标。我们也可以使用drawChessboardCorners()将角点绘制到图像上显示。

      通过上面的步骤,我们得到了用于标定的三维点和与其对应的图像上的二维点的点对。我们使用calibrateCamera()进行标定,这个函数会返回标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量。然后我们就可以使用新得到的内参数矩阵和畸变系数对图像进行去畸变了。


findChessboardCorners()函数

      我们需要使用findChessboardCorners函数提取角点,这里的角点专指的是标定板上的内角点,这些角点与标定板的边缘不接触。其函数原型如下:

bool findChessboardCorners( InputArray image, Size patternSize,OutputArray corners,int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );

第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;

第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;

第三个参数corners,用于存储检测到的内角点图像坐标位置,一般是数组形式;

第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。

cornerSubPix()函数:

      为了提高标定精度,需要在初步提取的角点信息上进一步提取亚像素信息,降低相机标定偏差,常用的方法是cornerSubPix函数,其函数原型如下:

void cornerSubPix( InputArray image, InputOutputArray corners,Size winSize, Size zeroZone,TermCriteria criteria );

第一个参数image,输入图像的像素矩阵,最好是8位灰度图像,检测效率更高;

第二个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据;

第三个参数winSize,大小为搜索窗口的一半;

第四个参数zeroZone,死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现某些可能的奇异性。当值为(-1,-1)时表示没有死区;

第五个参数criteria,定义求角点的迭代过程的终止条件,可以为迭代次数和角点精度两者的组合;

drawChessboardCorners函数:

   drawChessboardCorners函数用于绘制被成功标定的角点,函数原型:

void drawChessboardCorners( InputOutputArray image, Size patternSize,InputArray corners, bool patternWasFound );

第一个参数image,8位灰度或者彩色图像;

第二个参数patternSize,每张标定棋盘上内角点的行列数;

第三个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据;

第四个参数patternWasFound,标志位,用来指示定义的棋盘内角点是否被完整的探测到,true表示别完整的探测到,函数会用直线依次连接所有的内角点,作为一个整体,false表示有未被探测到的内角点,这时候函数会以(红色)圆圈标记处检测到的内角点;

calibrateCamera函数:

      获取到棋盘标定图的内角点图像坐标之后,就可以使用calibrateCamera函数进行标定,计算相机内参和外参系数,其calibrateCamera函数原型如下:

double calibrateCamera( InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints,Size imageSize,CV_OUT InputOutputArray cameraMatrix,CV_OUT InputOutputArray distCoeffs,OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,int flags=0, TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) );

第一个参数objectPoints,为世界坐标系中的三维点。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标;

第二个参数imagePoints,为每一个内角点对应的图像坐标点;

第三个参数imageSize,为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;

第四个参数cameraMatrix为相机的内参矩阵;

第五个参数distCoeffs为畸变矩阵;

第六个参数rvecs为旋转向量;

第七个参数tvecs为位移向量;

第八个参数flags为标定时所采用的算法。有如下几个参数:

        CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fx,fy。 
      CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。 
  CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。 
        CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。 
        CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。 
        CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。

第九个参数criteria是最优迭代终止条件设定。

      在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。

undistort()函数

     利用求得的相机的内参和外参数据,可以对图像进行畸变的矫正,使用undistort函数实现,其函数原型如下:

void undistort( InputArray src, OutputArray dst,InputArray cameraMatrix,InputArray distCoeffs,InputArray newCameraMatrix=noArray() );

第一个参数src,输入参数,代表畸变的原始图像;

第二个参数cameraMatrix,为之前求得的相机的内参矩阵;

第三个参数distCoeffs,为之前求得的相机畸变矩阵;

第四个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;

第五个参数newCameraMatrix,默认跟cameraMatrix保持一致;
实例:

#include <iostream>
#include <sstream>
#include <time.h>
#include <stdio.h>
#include <fstream>#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>using namespace cv;
using namespace std;void main() 
{ifstream fin("calibdata.txt");             /* 标定所用图像文件的路径 */ofstream fout("caliberation_result.txt");  /* 保存标定结果的文件 */  // 读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化int image_count = 0;  /* 图像数量 */Size image_size;      /* 图像的尺寸 */Size board_size = Size(9, 6);             /* 标定板上每行、列的角点数 */vector<Point2f> image_points_buf;         /* 缓存每幅图像上检测到的角点 */vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */string filename;      // 图片名vector<string> filenames;while (getline(fin, filename)){++image_count;Mat imageInput = imread(filename);filenames.push_back(filename);// 读入第一张图片时获取图片大小if(image_count == 1){image_size.width = imageInput.cols;image_size.height = imageInput.rows;}/* 提取角点 */if (0 == findChessboardCorners(imageInput, board_size, image_points_buf)){           cout << "can not find chessboard corners!\n";  // 找不到角点exit(1);} else {Mat view_gray;cvtColor(imageInput, view_gray, CV_RGB2GRAY);  // 转灰度图/* 亚像素精确化 */// image_points_buf 初始的角点坐标向量,同时作为亚像素坐标位置的输出// Size(5,5) 搜索窗口大小// (-1,-1)表示没有死区// TermCriteria 角点的迭代过程的终止条件, 可以为迭代次数和角点精度两者的组合cornerSubPix(view_gray, image_points_buf, Size(5,5), Size(-1,-1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));image_points_seq.push_back(image_points_buf);  // 保存亚像素角点/* 在图像上显示角点位置 */drawChessboardCorners(view_gray, board_size, image_points_buf, false); // 用于在图片中标记角点imshow("Camera Calibration", view_gray);       // 显示图片waitKey(500); //暂停0.5S      }}int CornerNum = board_size.width * board_size.height;  // 每张图片上总的角点数//-------------以下是摄像机标定------------------/*棋盘三维信息*/Size square_size = Size(10, 10);         /* 实际测量得到的标定板上每个棋盘格的大小 */vector<vector<Point3f>> object_points;   /* 保存标定板上角点的三维坐标 *//*内外参数*/Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));  /* 摄像机内参数矩阵 */vector<int> point_counts;   // 每幅图像中角点的数量Mat distCoeffs=Mat(1, 5, CV_32FC1,Scalar::all(0));       /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */vector<Mat> tvecsMat;      /* 每幅图像的旋转向量 */vector<Mat> rvecsMat;      /* 每幅图像的平移向量 *//* 初始化标定板上角点的三维坐标 */int i, j, t;for (t=0; t<image_count; t++) {vector<Point3f> tempPointSet;for (i=0; i<board_size.height; i++) {for (j=0; j<board_size.width; j++) {Point3f realPoint;/* 假设标定板放在世界坐标系中z=0的平面上 */realPoint.x = i * square_size.width;realPoint.y = j * square_size.height;realPoint.z = 0;tempPointSet.push_back(realPoint);}}object_points.push_back(tempPointSet);}/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */for (i=0; i<image_count; i++){point_counts.push_back(board_size.width * board_size.height);}   /* 开始标定 */// object_points 世界坐标系中的角点的三维坐标// image_points_seq 每一个内角点对应的图像坐标点// image_size 图像的像素尺寸大小// cameraMatrix 输出,内参矩阵// distCoeffs 输出,畸变系数// rvecsMat 输出,旋转向量// tvecsMat 输出,位移向量// 0 标定时所采用的算法calibrateCamera(object_points, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, 0);//------------------------标定完成------------------------------------// -------------------对标定结果进行评价------------------------------double total_err = 0.0;         /* 所有图像的平均误差的总和 */double err = 0.0;               /* 每幅图像的平均误差 */vector<Point2f> image_points2;  /* 保存重新计算得到的投影点 */fout<<"每幅图像的标定误差:\n";for (i=0;i<image_count;i++){vector<Point3f> tempPointSet = object_points[i];/* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points2);/* 计算新的投影点和旧的投影点之间的误差*/vector<Point2f> tempImagePoint = image_points_seq[i];Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);for (int j = 0 ; j < tempImagePoint.size(); j++){image_points2Mat.at<Vec2f>(0,j) = Vec2f(image_points2[j].x, image_points2[j].y);tempImagePointMat.at<Vec2f>(0,j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);}err = norm(image_points2Mat, tempImagePointMat, NORM_L2);total_err += err/= point_counts[i];    fout << "第" << i+1 << "幅图像的平均误差:" << err<< "像素" << endl;   }      fout << "总体平均误差:" << total_err/image_count << "像素" <<endl <<endl;   //-------------------------评价完成---------------------------------------------//-----------------------保存定标结果------------------------------------------- Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0));  /* 保存每幅图像的旋转矩阵 */fout << "相机内参数矩阵:" << endl;   fout << cameraMatrix << endl << endl;   fout << "畸变系数:\n";   fout << distCoeffs << endl << endl << endl;   for (int i=0; i<image_count; i++) { fout << "第" << i+1 << "幅图像的旋转向量:" << endl;   fout << tvecsMat[i] << endl;/* 将旋转向量转换为相对应的旋转矩阵 */   Rodrigues(tvecsMat[i], rotation_matrix);   fout << "第" << i+1 << "幅图像的旋转矩阵:" << endl;   fout << rotation_matrix << endl;   fout << "第" << i+1 << "幅图像的平移向量:" << endl;   fout << rvecsMat[i] << endl << endl;   }   fout<<endl;//--------------------标定结果保存结束-------------------------------//----------------------显示定标结果--------------------------------Mat mapx = Mat(image_size, CV_32FC1);Mat mapy = Mat(image_size, CV_32FC1);Mat R = Mat::eye(3, 3, CV_32F);string imageFileName;std::stringstream StrStm;for (int i = 0 ; i != image_count ; i++){initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);Mat imageSource = imread(filenames[i]);Mat newimage = imageSource.clone();remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);     StrStm.clear();imageFileName.clear();StrStm << i+1;StrStm >> imageFileName;imageFileName += "_d.jpg";imwrite(imageFileName, newimage);}fin.close();fout.close();return ;

from:https://blog.csdn.net/weixin_41695564/article/details/80422329

from:https://blog.csdn.net/u012319493/article/details/77622053


http://chatgpt.dhexx.cn/article/ElGhg4bg.shtml

相关文章

C++的文档操作心得

先提出一个问题&#xff1a; 假如我们玩贪吃蛇&#xff0c;玩到最后游戏结束时&#xff0c;显示出游戏的最高纪录&#xff0c;那我们想想&#xff1a;当我们关闭游戏进程时&#xff0c;这个游戏最高纪录是如何保存下来的呢&#xff1f; 由此引出文件操作 1.将maxscore数据保…

文件输入输出(文本文件、二进制文件)(ifstream、ofstream)

一.写入文件流程 创建一个ofstream对象来管理输出流。将该对象与特定文件关联起来。以使用cout方式使用该对象&#xff0c;唯一区别是输出进文件&#xff0c;不是屏幕。关闭文件流. ofstream fout; fout.open(filename); //上面两步可以合并为&#xff1a; ofstream fout(fil…

【IC7】FPGA最高工作频率的计算方法;FPGA最大输出频率;查看handbook的PLL最高频率;Fout_ext;Fout两个参数;FPGA输出1ns脉冲

目录 1 时钟周期 T Tco Tlogic Troute Tsu 2&#xff0c; 故 Tlogic 4 * Tlut   3&#xff0c;  Tco Tsu 12 * Tlut 4&#xff0c;搜索handbook&#xff0c;不要搜索datasheet 4-1 时钟树的特性 4-2 PLL特性&#xff1b;Fout_ext;Fout两个参数 5&#xff0…

CNN与RNN的详细介绍

CNN与RNN的介绍 本文主要总结我对李宏毅老师讲的CNN和RNN的理解&#xff0c;通过对比总结各自的优势&#xff0c;同时加深自己对这方面知识的理解。 1、CNN介绍 CNN是一种利用卷积计算的神经网络。它可以通过卷积计算将原像素很大的图片保留主要特征变成很小的像素图片。本文介…

CNN简介

CNN 卷积神经网络是含有卷积层的神经网络&#xff0c;而卷积层则得名于卷积运算。 Filter 在卷积层中&#xff0c;我们会用一个叫做filter的东西扫过一张图片。这些 filter 啊 它们的大小是,3 3 Channel 的 Size。如果今天是彩色图片的话,那就是 RGB 三个 Channel,如果是…

深度学习(六):CNN介绍

深度学习&#xff08;六&#xff09;&#xff1a;CNN介绍 CNN架构 首先input一张image以后&#xff0c;这张image会通过convolution layer&#xff0c;接下里做max pooling这件事&#xff0c;然后在做convolution&#xff0c;再做max pooling这件事。这个process可以反复无数次…

cnn算法

机器学习算法完整版见fenghaootong-github 卷积神经网络原理(CNN) 卷积神经网络CNN的结构一般包含这几个层&#xff1a; 输入层&#xff1a;用于数据的输入卷积层&#xff1a;使用卷积核进行特征提取和特征映射激励层&#xff1a;由于卷积也是一种线性运算&#xff0c;因此需…

cnn-lstm介绍(2)

1. CNN-LSTM模型。CNN具有注意最明显的特征&#xff0c;因此在特征工程中得到了广泛的应用。LSTM有&#xff0c;按时间顺序扩张的特性&#xff0c;广泛应用于时间序列中。 根据CNN和LSTM股票预测模型的特点 建立了基于CNN的LSTM模型-e模型结构 示意图如图1所示&#xff0c;主…

实例分割模型Mask R-CNN详解:从R-CNN,Fast R-CNN,Faster R-CNN再到Mask R-CNN

Mask R-CNN是ICCV 2017的best paper&#xff0c;彰显了机器学习计算机视觉领域在2017年的最新成果。在机器学习2017年的最新发展中&#xff0c;单任务的网络结构已经逐渐不再引人瞩目&#xff0c;取而代之的是集成&#xff0c;复杂&#xff0c;一石多鸟的多任务网络模型。Mask …

CNN与RNN的区别

从应用方面上来看&#xff0c;我了解到的CNN用到做图像识别比较多&#xff0c;而RNN在做到语言处理多一点&#xff0c;如果拿来比喻的话&#xff0c;CNN如同眼睛一样&#xff0c;正是目前机器用来识别对象的图像处理器。相应地&#xff0c;RNN则是用于解析语言模式的数学引擎&a…

卷积神经网络(CNN)结构详解

一、CNN的基本结构&#xff1a; 1.图像就是输入层 2.接着是CNN特有的卷积层&#xff08;convolution&#xff09;&#xff0c;卷积层的自带激活函数使用的是ReLU 3.接着是CNN特有的池化层&#xff08;pooling&#xff09;&#xff0c; 4.卷积层池化层的组合可以在隐藏层中出…

CNN的实现(附代码)

前言 前文已经单独实现了卷积层和池化层&#xff0c;现在来组合这些层&#xff0c;搭建进行手写数字识别的CNN。 这个简单的CNN网络构成如下。 网络的构成是“Convolution - ReLU - Pooling -Affine - ReLU - Affine - Softmax”&#xff0c;我们将它实现为名为SimpleConvNet…

CNN卷积网络简介

CNN卷积网络 CNN卷积网络的结构 输入层&#xff1a; 输入层是3232 RGB图像。 注&#xff1a;有必要计算每一层输出的图片大小。 卷积层&#xff1a; 卷积层的核心在于卷积核与激活函数。   卷积层最主要的作用是寻找与卷积核匹配的特征,因为与卷积核符合&#xff08;卷积核…

CNN的通俗理解

Agenda 1 卷积神经网络Convolutional Neural Networks,CNN1.1 前言1.2 图像转化成矩阵1.3 卷积核1.4 特征图feature map1.5 激活函数1.6 池化1.7 训练 1 卷积神经网络Convolutional Neural Networks,CNN 1.1 前言 卷积神经网络是针对图像的深度学习框架。 1.2 图像转化成矩阵…

CNN网络详解

分割线----------------------------------   这里更新过一次&#xff0c;在朋友的提醒下&#xff0c;我发现这份代码不是很容易懂。我使用了Pytorch给的官方demo重新实现了LeNet&#xff0c;并做出了详细解释&#xff0c;如果理解下面代码有问题&#xff0c;可以先看我的这篇…

卷积神经网络(CNN)基本概念

一、卷积神经网络基本概念 卷积神经网络包含了一个由卷积层和子采样层构成的特征抽取器。在卷积神经网络的卷积层中&#xff0c;一个神经元只与部分邻层神经元相连接。在CNN的一个卷积层中&#xff0c;通常包含若干个特征平面&#xff0c;每个特征平面都由一些矩形排列的神经元…

CNN简单介绍及基础知识

文章目录 一&#xff09;卷积神经网络历史沿革 二&#xff09;CNN简单介绍 三&#xff09;CNN相关基础知识 前言 在过去的几年里&#xff0c;卷积神经网络(CNN)引起了人们的广泛关注&#xff0c;尤其是因为它彻底改变了计算机视觉领域&#xff0c;它是近年来深度学习能在计算机…

一文读懂目标检测:R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD

一文读懂目标检测&#xff1a;R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD 前言 之前我所在的公司七月在线开设的深度学习等一系列课程经常会讲目标检测&#xff0c;包括R-CNN、Fast R-CNN、Faster R-CNN&#xff0c;但一直没有比较好的机会深入&#xff08;但当你对目标检测…

理解 CNN

理解 CNN 注意&#xff1a;下面提到的图像指位图 目录 理解 CNNCNN人类的视觉原理几个关键层卷积层(fliter、kernel)池化层 (pooling) 激活层(activate)全连接层(Linear) pytorch实现TextCNN卷积传播图解不同视角看CNN 参考 CNN 卷积神经网络-CNN 最擅长的就是图片的处理。它…

【深度学习】CNN算法

一.定义&#xff1a; 卷积神经网络&#xff08;CNN&#xff09;&#xff0c;是一类包含卷积计算且具有深度结构前馈神经网络&#xff0c;是深度学习&#xff08;deep learning&#xff09;的代表算法之一。 卷积神经网络具有表征能力&#xff0c;能够按其阶层结构对输入信息进…