本文参考、鸣谢:
相机标定与矫正opencv+MATLAB - 当年煮酒论英雄 - 博客园
Matlab标定_opencv立体校正_u011393762的博客-优快云博客
本文测试matlab版本为2020a
双目标定:
最新版本标定工具是以app方式出现的。打开matlab后,APP展开搜索 calibrator,会出现单目摄像机标定APP 和 立体摄像机标定APP,双目标定使用第二个。
打开app后,第一步加载标定图像
需要将两个摄像机的标定图片分开两个文件夹存放,并保证文件数量相同并且拍摄时刻一一对应。选择文件夹选项下面,可以设置棋盘格边长和单位,如果需要后续视差数据生成点云图,需要按实际长度填写。如果只是看效果,使用默认即可。
加载完成后,标定图像会成对出现在工具左侧列表
matlab会自动配对,可以逐一点击查看一下,如果有配对错误,请调整标定图片后重新加载。
加载完图片后,就可以直接点击标定了,为了取得较好的效果,需要对标定进行一点设置。
INTRINSICS:内参设置
单选框 分别是 计算内参矩阵 和 使用固定内参矩阵 。
OPTIONS:
Radial Distortion: "径向畸变",可以指定2(k1,k2)或3个(k1,k2,k3)径向失真系数。当光线在透镜边缘的弯曲程度大于在透镜光学中心的弯曲程度时,就会发生径向畸变。镜头越小,失真越大。通常,两个系数足以进行校准。对于严重失真,例如在广角镜头中,可以选择3个系数来包括k3。
Skew:“计算扭曲”,选中“计算扭曲”复选框时,校准器将估计图像轴扭曲。一些相机传感器存在缺陷,导致图像的x轴和y轴不垂直。可以使用skew参数对此缺陷建模。如果未选中该复选框,则假定图像轴垂直,现代相机大多数摄像机是这样。
Tangential Distortion:当透镜和图像平面不平行时,会发生切向失真。切向畸变系数模拟了这种畸变。选中“计算切向失真”复选框时,校准器将估计切向失真系数。否则,校准器将切向畸变系数设置为零。切向畸变有D1,D2两个参数。
设置好以后,点击标定:
标定完成后,校正结果满意的话,就可以将标定结果导出到 matlab命令行主界面了。
工作区里的stereoParams_opencv结构,是我们导出的标定参数信息。里面的信息很丰富。
opencv立体校正需要6个矩阵,相机1内参M1、畸变系数D1、相机2内参M2,畸变系数D2,旋转矩阵R,平移矩阵T。
标定参数导出为xml格式。
我们通过脚本将这6个矩阵存储到opencv可以读取的xml格式文件里。
脚本文件export_stereoParams_to_Opencv.m如下:
function ExportStereoParamsOpenCV(stereoParams, file)
% ExportStereoParams2OpenCV(cameraParams, file)
% 功能:将双目相机标定的参数保存为OpenCV可读取的xml文件
% 输入:
% stereoParams:双目相机标定数据的对象
% file:输出的xml文件名
% xml文件是由一层层的节点组成的。
% 首先创建父节点 fatherNode,
% 然后创建子节点 childNode=docNode.createElement(childNodeName),
% 再将子节点添加到父节点 fatherNode.appendChild(childNode)
%创建xml文件对象,添加opencv存储标识
doc = com.mathworks.xml.XMLUtils.createDocument('opencv_storage');
docRootNode = doc.getDocumentElement; %获取根节点
M1 = (stereoParams.CameraParameters1.IntrinsicMatrix).'; % 右相机内参矩阵,需要装置为左乘的矩阵
RadialDistortion1 = stereoParams.CameraParameters1.RadialDistortion; % 右相机径向畸变参数向量1*3
TangentialDistortion1 =stereoParams.CameraParameters1.TangentialDistortion; % 右相机切向畸变向量1*2
D1 = zeros(1,5); % OpenCV的畸变参数矩阵最多可以有14个参数
D1(1:4) = [RadialDistortion1(1:2), TangentialDistortion1]; %构成opencv中的畸变系数向量[k1,k2,p1,p2,k3]
if (size(RadialDistortion1,2)>2 )
D1(5) = RadialDistortion1(3);
end
M2 = (stereoParams.CameraParameters2.IntrinsicMatrix).'; % 右相机内参矩阵,需要装置为左乘的矩阵
RadialDistortion2 = stereoParams.CameraParameters2.RadialDistortion; % 右相机径向畸变参数向量1*3
TangentialDistortion2 =stereoParams.CameraParameters2.TangentialDistortion; % 右相机切向畸变向量1*2
D2 = zeros(1,5);
D2(1:4) = [RadialDistortion2(1:2), TangentialDistortion2]; %构成opencv中的畸变系数向量[k1,k2,p1,p2,k3]
if (size(RadialDistortion1,2)>2 )
D2(5) = RadialDistortion2(3);
end
R = (stereoParams.RotationOfCamera2).';
T = (stereoParams.TranslationOfCamera2).';
sizeImage = stereoParams.CameraParameters1.ImageSize;
AddMat(sizeImage, 'size', doc, docRootNode);
AddMat(M1, 'M1', doc, docRootNode);
AddMat(D1, 'D1', doc, docRootNode);
AddMat(M2, 'M2', doc, docRootNode);
AddMat(D2, 'D2', doc, docRootNode);
AddMat(R, 'R', doc, docRootNode);
AddMat(T, 'T', doc, docRootNode);
xmlwrite(file, doc);
end
function AddMat(A, name, doc, docRootNode)
mat = doc.createElement(name); %创建mat节点
mat.setAttribute('type_id','opencv-matrix'); %设置mat节点属性
rows = doc.createElement('rows'); %创建行节点
rows.appendChild(doc.createTextNode(sprintf('%d',size(A,1)))); %创建文本节点,并作为行的子节点
mat.appendChild(rows); %将行节点作为mat子节点
cols = doc.createElement('cols');
cols.appendChild(doc.createTextNode(sprintf('%d',size(A,2))));
mat.appendChild(cols);
dt = doc.createElement('dt');
dt.appendChild(doc.createTextNode('d'));
mat.appendChild(dt);
data = doc.createElement('data');
for i=1:size(A,1)
for j=1:size(A,2)
data.appendChild(doc.createTextNode(sprintf('%.15f ',A(i,j))));
end
data.appendChild(doc.createTextNode(sprintf('\n')));
end
mat.appendChild(data);
docRootNode.appendChild(mat);
end
切换matlab目录到export_stereoParams_to_Opencv.m所在路径,拷贝文件到当前目录。
在命令行执行
export_stereoParams_to_Opencv(stereoParams_opencv, "matlabStereoParam.yml")
得到matlabStereoParam.yml文件,可直接通过opencv读取。
<?xml version="1.0" encoding="utf-8"?>
<opencv_storage>
<size type_id="opencv-matrix">
<rows>1</rows>
<cols>2</cols>
<dt>d</dt>
<data>640.000000000000000 480.000000000000000
</data>
</size>
<M1 type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>247.436791595622651 0.000000000000000 206.269175103342491
0.000000000000000 252.130614650075444 331.280927054414292
0.000000000000000 0.000000000000000 1.000000000000000
</data>
</M1>
<D1 type_id="opencv-matrix">
<rows>1</rows>
<cols>5</cols>
<dt>d</dt>
<data>-0.239509548357768 0.034758655044619 -0.001295109814602 0.001841176318709 0.000000000000000
</data>
</D1>
<M2 type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>246.298543520481644 0.000000000000000 187.776278041563415
0.000000000000000 252.018371898686297 311.080030357984640
0.000000000000000 0.000000000000000 1.000000000000000
</data>
</M2>
<D2 type_id="opencv-matrix">
<rows>1</rows>
<cols>5</cols>
<dt>d</dt>
<data>-0.240170028528280 0.035205469383591 -0.000624101429000 0.003366535858464 0.000000000000000
</data>
</D2>
<R type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>0.996084341727677 0.009050071748308 0.087943620384255
-0.008791440516103 0.999955817302695 -0.003327764437357
-0.087969851304815 0.002541582941630 0.996119895202159
</data>
</R>
<T type_id="opencv-matrix">
<rows>3</rows>
<cols>1</cols>
<dt>d</dt>
<data>-24.416826406087150
0.371078172361293
1.036212681563079
</data>
</T>
</opencv_storage>