之后的方式有两种:自动标定和手动标定 标定完后则进行导出XML文件
注意:队内的标定板是30mm
可以预览成功检测出棋盘格的图像,然后开始标定,点击Calibrate
注意:标定前记得把Radial distortion coefficients下的3 Coefficients
和Tangential Distortion
勾上,这两个是为了设置参数:径向畸变参数的个数为3;是否计算切向畸变:是
通过编写MATLAB函数向相机标定的内参和畸变参数保存为opencv可以读取的XML文件
将下列函数保存至自定义的文件目录下,然后将主界面的地址切换到包含上述已保存函数的文件夹(即命令行的地址)
function writeXML(cameraParams,file)
%writeXML(cameraParams,file)
%功能:将相机校正的参数保存为xml文件
%输入:
%cameraParams:相机校正数据结构
%file:xml文件名
%说明在xml文件是由一层层的节点组成的。
%首先创建父节点 fatherNode,
%然后创建子节点 childNode=docNode.createElement(childNodeName),
%再将子节点添加到父节点 fatherNode.appendChild(childNode)
docNode = com.mathworks.xml.XMLUtils.createDocument('opencv_storage'); %创建xml文件对象
docRootNode = docNode.getDocumentElement; %获取根节点
IntrinsicMatrix = (cameraParams.IntrinsicMatrix)'; %相机内参矩阵
RadialDistortion = cameraParams.RadialDistortion; %相机径向畸变参数向量1*3
TangentialDistortion =cameraParams.TangentialDistortion; %相机切向畸变向量1*2
Distortion = [RadialDistortion(1:2),TangentialDistortion,RadialDistortion(3)]; %构成opencv中的畸变系数向量[k1,k2,p1,p2,k3]
camera_matrix = docNode.createElement('camera-matrix'); %创建mat节点
camera_matrix.setAttribute('type_id','opencv-matrix'); %设置mat节点属性
rows = docNode.createElement('rows'); %创建行节点
rows.appendChild(docNode.createTextNode(sprintf('%d',3))); %创建文本节点,并作为行的子节点
camera_matrix.appendChild(rows); %将行节点作为mat子节点
cols = docNode.createElement('cols');
cols.appendChild(docNode.createTextNode(sprintf('%d',3)));
camera_matrix.appendChild(cols);
dt = docNode.createElement('dt');
dt.appendChild(docNode.createTextNode('d'));
camera_matrix.appendChild(dt);
data = docNode.createElement('data');
for i=1:3
for j=1:3
data.appendChild(docNode.createTextNode(sprintf('%.16f ',IntrinsicMatrix(i,j))));
end
data.appendChild(docNode.createTextNode(sprintf('\\n')));
end
camera_matrix.appendChild(data);
docRootNode.appendChild(camera_matrix);
distortion = docNode.createElement('distortion');
distortion.setAttribute('type_id','opencv-matrix');
rows = docNode.createElement('rows');
rows.appendChild(docNode.createTextNode(sprintf('%d',5)));
distortion.appendChild(rows);
cols = docNode.createElement('cols');
cols.appendChild(docNode.createTextNode(sprintf('%d',1)));
distortion.appendChild(cols);
dt = docNode.createElement('dt');
dt.appendChild(docNode.createTextNode('d'));
distortion.appendChild(dt);
data = docNode.createElement('data');
for i=1:5
data.appendChild(docNode.createTextNode(sprintf('%.16f ',Distortion(i))));
end
distortion.appendChild(data);
docRootNode.appendChild(distortion);
xmlFileName = file;
xmlwrite(xmlFileName,docNode);
end
在命令行输入:
writeXML(cameraParams,'cameraParams.xml');
就完成了将相机内参和畸变参数保存问XML文件。
http://www.vision.caltech.edu/bouguetj/calib_doc/download/index.html
将其解压到Matlab的工作目录下,然后将其包含到matlab的工作路径下。
文件其实放哪都行,只要告诉Matlab你下载的工具箱的位置就可以了。具体操作如下:
将下载解压后的文件放置到/usr/local/MATLAB/R2016b/toolbox
(工作目录,Matlab的一些其他工具箱也是放置在此,所以为了统一管理也将标定工具箱放置于此)
打开Matlab(不会吧不会吧不会有人不会打开Matlab吧),在命令行窗口输入pathtool
,看到如下画面:
选择添加文件夹
,选择解压好的toolbox_calib
,设置路径的画面就会多出一个新的文件路径:
点击该路径并选择添加并包含子文件夹
,点击打开
,设置路径的画面就会再添加两个路径:
最后点击保存
,然后验证一下是否成功,将左侧当前文件夹的窗口里的路径切换至其他路径,即切换到除设定好的Matlab工作路径之外的其他路径
并在命令行窗口输入calib_gui
,若出现以下窗口,即为添加路径成功。
将标定用的图像单独存放于一个文件夹中。
在MATLAB中,进入到该文件夹中。
点击Standard
,画面会变成如下所示:
点击标定工具窗口中的Image names
按钮。输入标定图像的基名和图像的格式(例如标定图像的名称分别是 Img1.jpg,Img2.jpg,Img3.jpg...基名就是Img,而且基名一定是为 图像格式就是jpg) 然后所有的标定图像都将加载进来,其对应的变量名分别是I_1,I_2,...
图像数量对应的变量是n_ima
(这两个可以在右边的工作区找到)。 载入完图像之后的图像如下:
之后载入的图像将以缩略图的形式显示出来:
注意: 1.图片名字不易过长,否则有可能报错内存不够 2.一定要进入到标定图片所在的路径下再进行读取图片
点击标定工具箱上的Extract grid corners
按钮。在matlab命令行会出现如下提示信息:
直接按“Enter”(没有参数)选择所有的图像,否则就需要输入图像索引如[2 5 8 10 12]来提取这些图像中的角点。然后通过直接输入“Enter”来选择默认的角点寻找窗口尺寸:wintx=winty=5。这就产生了一个11X11个像素有效的窗口尺寸:
角点提取引擎有一个对网格中的方格个数进行计数的自动机制。这个工具尤其对于图像数量大的时候非常便利,因为用户不需要手工地输入X和Y方向方格的数量。然而对于极少数场合,这个工具可能不能得到正确的方格数量,这种情况只有当镜头有很大的畸变。在这种情况下,角点提取这个过程,工具箱提供了一个可选项可供用户关闭自动计数方格。在这个特殊的模式下,使用者优先对每张图像使用方格自动计数功能,因此,直接按“Enter”键默认。(通常情况下都使用默认形式,然后如果确实有需要,再重新处理几张有问题的图像。)
如果发现wintx
和winty
的默认数据不正确且不想每次都写入参数的话,可以在右侧的工作区找到wintx_default
和winty_default
并编辑他的值,就可以把自己想要的参数进行默认出来
然后第一张标定图像就会显示出来:
然后点击长方形棋盘格的四个边角点。选择的位置在下图中显示出来(注意:尽量精确地点击这四个角点,控制在实际角点的5个像素范围内,否则一些角点可能会被检测器丢失掉)
点击的顺序规则:第一个点被用来作为棋盘格坐标系的原点。其他三个点可以以任何顺序点击。第一个点击的的点非常重要,尤其是对于多相机的情况(例如当计算几个相机在空间之间的相互关系的时候)。当处理多相机系统时对于不同的相机标定图像需要总是选择同一个棋盘格坐标系。三维标定可以运行stereo_gui.m.
第一个点的选择非常重要,一定要注意 经过上面的步骤之后,标定棋盘的边界就显示出来了:
输入网格中每个方格在X和Y方向上的尺寸dX和dY(在这里,dX=dY=30mm=default values),同理,如果不想每次都赋值的话,建议到右边的工作区进行默认值的修改:
注意:你可以直接输入“Enter”直接用默认参数,程序会自动对各个方向的方格数进行计数,然后在显示出没有畸变的预估角点。
如果预估的角点很接近实际的图像角点,则下面的步骤就可以略过了(如果没有那么大的图像畸变)。在现在的图像中:预估的角点足够接近实际的图像角点。因此,没有必要通过输入一个猜测的径向畸变系数去帮助软件去检测图像角点。直接输入“Enter”,会使用这些初始的预估角点作为提取的角点:
然后图像角点就被自动地提取出来了,然后显示出来,如下图所示:
角点以大约0.1个像素的精度被提取出来。 对第2、3、4...图像采用上述同样的步骤。如下图是将图像2、3、4..图像的角点提取出来:
从上面观察到方格尺寸dX、dY总是被保持在它们的初始值(30mm). 有时,预估的角点不是那么足够地接近实际图像角点,在这种情况下,就有必要通过输入一个镜头畸变系数来调整 预估角点。第15张图像就是这种情况,在这张图像中,预估角点的图像如下图所示:
可以看到一些预估角点和实际图像的角点的差距很大,这样就会导致错误的角点提取。而这产生的原因就是因为图像畸变。为了帮助系统更好地判断角点的位置,用户可以手动地输入一个镜头畸变系数Kc.为了输入镜头畸变系数,我们需要在那个问题“Need of an initial guess for distortion?”这里输入一个非空参数,这里我们输入畸变系数Kc=-0.3(一般这个系数在-1到1之间,我们的镜头畸变系数在镜头的文档中有提及,这里应该输入为0.1)。如下图所示:
根据这个畸变系数,新的预估角点位置如下图所示:
如果新的预估角点足够接近实际的图像角点(如上图所示),则输入任何不为空的字符(如1)作为问题“Satisfied with distortion?”的回答。然后亚像素角点的位置就会使用新的带有图像畸变的预估位置进行计算。
如果我们还不满意,我们可以输入一个空的字符串作为问题"Satisfied with distortion?"(直接输入“Enter”)然后尝试一个新的畸变系数Kc。你可能会重复很多次这样的过程直到对结果满意为止。
注意: 1、上面用到的畸变的值仅仅是用来帮助提取角点,其不会影响下面主要的标定过程。换句话说,这里的畸变系数并不会作为最终的畸变结果,也不会用于优化畸变系数的初始值。 2、使用到这种特殊情况建议先确保选择4个边界点的时候有没有确保在5个像素点内的误差,因为一般不太会发生这种特殊情况。
最后检测到的角点如下图所示:
对剩下的几张图片重复上述过程(图片16-20).然而对这些图像,不要使用预先的畸变系数这个选项,尽管提取的角点不是很正确。在下面的步骤中,我们将纠正它们(在这个例子中,我们不会使用畸变系数这个选项用于图像15但是这样对于我们证明很有用)。
在角点提取之后,就会自动产生一个calib_data.mat的matlab数据文件。这个文件包含了整个角点提取过程中的所有信息(图像坐标,对应的3D网格坐标,网格尺寸....)。这个文件是为了防止matlab突然崩溃而创立的。载入这个文件可以避免你又重复一遍上面的过程。(载入文件的话先选择左侧对于的.mat文件,然后点击load)
在你自己标定的过程中,当图像中有大的畸变的时候,这个程序可能不能够在网格中自动对方格进行计数,在这种情况下,X和Y方向的方格数量必须手动输入(根据所选的4个点所包含的矩形内的格子数)。这在这个例子中没有出现这种情况。 在你自己做标定的时候,还有一种情况会出现。如果镜头畸变非常严重(像鱼眼镜头),这个基于单个畸变系数的简化的指导工具对角点的初始预估可能就不够用。对于这几种麻烦的情况,在工具箱中的一个脚本程序支持完全手动的角点提取(例如每次点击一个角点)。脚本文件叫做“manual_corner_exteaction.m”(在内存优化模式下,你可以使用"manual_corner_extraction_no_read.m"),并且应该在传统的角点提取代码运行之后才能执行这个代码。(因为它基于传统的角点提取的一些数据)。
显然,这种角点提取的方法当图像数量多的时候是非常耗时的。因为它作为当前面的尝试都失败的情况下的一种救命稻草。但完全可以不用太担心这个,在这个例子中不会出现这种情况。
在角点提取完之后,就可以单击标定工具箱面板上的Calibration
来运行主要的相机标定程序。
标定主要通过两个步骤来完成:初次初始化以及非线性优化。
初始化步骤中对标定参数进行闭环计算,这个过程不包括任何镜头畸变(程序名:init_calib_param.m) 非线性优化过程中将对所有的标定参数最小化总体映射误差(从最小二乘的角度出发)(9个内参以及6X20=129、个外参)。优化是对特定雅可比矩阵进行计算然后往梯度下降的方向进行的。
标定参数存储在一系列变量中。注意切向畸变系数和第6个径向畸变系数没有没有被估算(这是默认的模式)。因此在像素坐标中X和Y之间是90°。在大多数实际情况中,这是一个很理想化的设想。然而,接下来,将会讲述一种介绍在优化中切向畸变alpha_c的方法。
从上图中我们可以注意到:为了达到最小值,只用了11次梯度迭代。这就意味着只有11次对映射函数、雅可比计算 以及求逆的评估。快速收敛的一个原因就是初始化程序所要计算的参数的有一个好的初始预估值。 现在,忽略推荐的可以减少畸变的模型的系统。对一个模型的复杂性进行判断的映射误差仍然很大。这主要是因为对于一些图像一些网格角点并没有被精确地提取。
单击面板上的Reproject on images
来将网格角点映射到原始图像中。这些映射是基于当前的内参和外参计算
出来的。输入一个空字符(直接按"Enter")作为Number(s) of image(s) to show([]=all images)
来
表示你想查看所有图像:
下面的图像显示了最初的四张检测到的角点的图像(红色的叉)以及映射的网格角点(圆)。
并且在命令行窗口会显示以下参数
映射误差也以有颜色的叉叉显示在图中:
为了退出误差分析工具,在图像上的任何位置右击(稍后你将会理解这个选项的使用)。
在面板中单击“Show Extrinsic”。外参(棋盘格相对于相机的相对位置)就以3D的形式显示出来了:
在上图中,坐标系(Oc,Xc,Yc,Zc)是相机的参考坐标系。红色的金字塔状的就是由图像平面定义的相机的有效视场。可以在相机坐标系视角或者世界坐标系视角之间切换: