当前位置:首页 > 工业用地 >摄像机标定和立体标定的区别(摄像机标定和立体标定一样吗)

摄像机标定和立体标定的区别(摄像机标定和立体标定一样吗)

一段时间以来我一直在尝试使用OpenCV 来实现立体视觉。主要参考资料是《学习OpenCV》第11、12章以及OpenCV论坛上一些前辈的讨论。过程中也经历过很多坎坷和碰伤,也走了很多弯路。终于在不久前解决了最大的问题,整个标定、标定、匹配过程调试成功。 (虽然还有一些问题尚未明确)

我在这里写这篇文章。第一个方面是给自己一个总结。第二个方面是我感觉关于OpenCV立体视觉的资料还是比较分散和不完整。新手需要很长时间才能上手。第三个方面,也是我对过程中的一些问题还很困惑,希望能够给大家解答一下。

摄像机标定和立体标定的区别(摄像机标定和立体标定一样吗)

1.相机

我使用的相机是在淘宝上买的3D相机,两个USB相机加上一个可调节支架。实际照片如下

1.1 3D相机实拍图

双USB摄像头的OpenCV驱动请参考以下链接

http://www.opencv.org.cn/index.php/使用DirectShow采集图像

将上述代码复制到自己的项目中后,需要设置项目或编译环境。

VC6下的详细设置可以看代码的注释(修改工程的属性)

VS2008中的设置也可以参考代码注释中VC++2005的设置(修改编译环境)

2. 校准

由于OpenCV中的cvStereoCalibrate总是得到非常夸张的结果(参见下面5.1中的问题描述),我们最终决定使用Bouguet的Matlab标定工具箱进行立体标定,然后将标定结果读入OpenCV中进行后续的图像标定和匹配。

Matlab标定工具箱参考链接如下:

http://www.vision.caltech.edu/bouguetj/calib_doc/

上面有详细的使用步骤,使用起来还是蛮方便的。

以下是我个人使用Matlab工具箱进行立体标定的步骤,供参考。如果您需要更详细的步骤,请参考上面的链接。

将Matlab工具箱中的文件复制到相应目录下,将需要标定的棋盘图放到.m文件所在目录下,然后在Matlab命令行窗口中输入calib_gui,选择Standard,会出现如下窗口

2.1. calilb_gui 面板

我们首先校准右相机,因此首先将从右相机采集的棋盘图像复制到工具箱目录中。

单击图像名称。命令行窗口会提示你输入图像的basename和图像的格式(例如,如果你的图像文件名是right1,right2,…,right10,则basename就是right),然后Matlab会自动读取在这些图片中,如下图所示,可以看到右侧摄像头的10张棋盘格图像已经被读入。

收集棋盘图像时,请注意让棋盘尽可能占据屏幕,以便获得有关相机失真的更多信息。

2.2.读取图像基名

2.3.读棋盘图

然后返回主控制界面,点击提取网格角点,即可提取每张图片的角点。

2.4. calib_gui面板

点击后,命令行会出现如下提示,主要是要求你输入棋盘角搜索窗口的大小。如果窗口设置得大一些,提取角点会更方便(即使点有偏差也能找到),但也要注意不要让它大于正方形的大小。对于其余两个选项,只需按Enter 键选择默认设置即可。

2.5.选择窗口尺寸

然后开始角点提取工作。按照一定的顺序提取棋盘边缘的角点。程序会自动帮你找到所有对应的角点。

2.6。提取角点

2.7.提取角点2

提取第一张图片时,命令行窗口可能会提示您输入正方形大小。只需在此处输入正方形的实际大小即可。比如我的正方形是27mm,就输入27。这一步其实还是挺关键的。它定义了空间的尺度,如果要测量物体,它是必需的。

用同样的方法提取10张图像后,单击“校准”开始相机校准。

2.8. calib_gui面板

经过多次迭代,程序最终会得到相机的内参数和外参数,如下图所示(图中的符号由于字体关系没有完全显示出来,中间的问号是加号和外号)减号表示错误)

2.9.标定迭代过程及结果

您可以通过面板上的Show Extrinsic查看标定结果,验证外部参数标定的结果。

2.10.外部参数说明

确认校准结果正确后,点击面板上的“保存”按钮。该程序会将校准结果放入名为Calib_Result.mat 的文件中。为了方便后续立体标定,将文件名改为Calib_Result_right.mat。

左相机标定方法与右相机相同。生成Calib_Result.mat后,只需将其重命名为Calib_Result_left.mat即可。

左右摄像头校准完毕后,即可开始立体校准。

在Matlab命令行中输入stereo_gui启动立体标定面板,如下图

2.11.立体图形用户界面面板

单击“加载左右校准文件”,然后在命令行中选择默认文件名(Calib_Result_left.mat 和Calib_Result_right.mat)。然后您可以开始运行立体校准。运行后的结果如下图所示。左右摄像头参数全部设置完毕。进行校正,还得到两个相机之间的旋转和平移关系向量(om和T)。

2.12.立体校准结果

点击面板上的Show Extrinsics of Stereo rig,可以看到如下图的双机关系图。可以看到两个摄像头在前进方向上基本是平行的。

2.13.双摄像头与标定棋盘的位置关系

获得立体标定参数后,可以将参数放入xml文件中,然后使用cvLoad将其读入OpenCV中。具体方法可以参考学习OpenCV第11章的例子。上面是使用cvSave保存校准结果,然后使用cvLoad将之前的校准结果读入矩阵中。

2.14. xml 文件示例

这里需要注意的是Matlab标定结果中的om向量。该向量是旋转矩阵经过罗德里格斯变换的结果。如果你想在cvStereoRectify中使用它,你需要首先使用cvRodrigues将此向量转换为旋转矩阵。关于Rodrigues变换,《学习OpenCV》第11章也有解释。

2.15。罗德里格斯形式旋转矩阵的表示

3. 立体校准与匹配

有了标定参数,标定过程就非常简单了。

我在OpenCV 中使用cvStereoRectify。获得校准参数后,我使用cvRemap来校准输入的左右图像。这部分代码参考了《学习OpenCV》第十二章的例子。

校准后即可实现立体匹配。 OpenCV中立体匹配有两种方法,一种是Block Matching,另一种是Graph Cut。块匹配采用SAD方法,速度较快,但效果一般。 Graph Cut可以参考Kolmogrov03的博士论文。效果不错,但是运行速度确实慢得让人难以忍受。所以我还是选择BM。

以下是我使用BM进行立体匹配时使用的参数设置

[cpp:nogutter]

BMState=cvCreateStereoBMState(CV_STEREO_BM_BASIC,0);

断言(BMState!=0);

BMState-preFilterSize=13;

BMState-preFilterCap=13;

BMState-SADWindowSize=19;

BMState-minDisparity=0;

BMState-numberOfDisparities=unitDisparity*16;

BMState-textureThreshold=10;

BMState-uniquenessRatio=20;

BMState-speckleWindowSize=13;

其中,我将minDisparity参数设置为0,因为我的两个相机是向前平行放置的。左图中的同一物体一定比右图中更靠右,如下图3.1所示。所以不需要设置搜索参数。

如果为了追求更大的双目重叠区域而使两个相机向内偏转,则需要考虑该参数。

3.1.修正了左右视图

另一个需要提到的参数是uniquenessRatio。经过实验,感觉这个参数对最终的匹配结果影响很大。 UniquenessRatio主要可以防止不匹配。其主要作用可以从下面三张图的视差效果对比看出。在立体匹配中,我们宁愿区域不匹配也不愿不匹配。如果存在不匹配,在遇到障碍物检测等应用时就会非常麻烦。

3.2. UniquenessRatio为0时的匹配图,可以看到大面积的不匹配

3.3. UniquenessRatio为10时的视差图。可以看到不匹配大大减少,但仍然存在噪声点。

3.4. UniquenessRatio为20时的视差图。可以看到不匹配基本被消除,点云更加干净。

关于cvFindStereoCorrespondenceBM函数的源码,我做了比较详细的研究。过段时间我会把之前写的代码注释整理一下,发表一篇博文。

4. 实际距离的测量

使用cvFindStereoCorrespondenceBM获取视差图后,还需要使用cvReprojectImageTo3D函数将单通道Disparity Map转换为三通道实际坐标矩阵。

具体的数学原理可以参考下面的公式(来自chenyusiyuanhttp://blog.csdn.net/chenyusiyuan/archive/2009/12/25/5072597.aspx,本博文中也提到了一些关于实际深度的问题)

4.1 距离换算公式

然而在实际操作过程中,使用cvReprojectImageTo3D得到的数据并不符合预期。我一直无法弄清楚由生成的深度矩阵定义的世界坐标系。这将在下面的示例中详细解释。希望这方面的专家帮忙解答:

图4.2是测量时的实际场景图。场景中测量的三个主要物体是前面的利乐盒、中间的纸杯和最远的塑料瓶。

4.2.实际场景中三个待测物体的位置

图4.3 显示了校准后的左右图像以及匹配的视差图。视差窗口是实际的点云,物体窗口是视差图添加阈值后得到的二值图像,主要是分割前景和背景。可以看出,三个待测物体基本分割正确。

4.3.双目相机获取的视差图

图4.4是在视差窗口中选择一点后在实际坐标矩阵中得到的对应三维信息。这里,我在三个物体的点云上分别选择一个点来表示物体的实际坐标信息。 (这里通过鼠标获取一点坐标信息的方法参考了opencv示例中的watershed.cpp)

4.4.对应点的三维坐标

这里可以看到,(265, 156)表示利乐盒子的坐标是(13, 12, -157),(137, 142)纸杯的坐标是(77, 30, -312) , (95, 115) 塑料瓶的坐标为(144, 63, -482)。

补充一下:为了显示方便,视差图出来后进行了0-255的归一化,所以第一个值是归一化后点的灰度值,后面一个是归一化前点的实际视差图正常化。

cvFindStereoCorrespondenceBM算法源代码:

dptr[y*dstep]=(短)(((ndisp - mind - 1 + mindisp)*256 + (d !=0 ? (p-n)*128/d : 0) + 15) 4);

ndisp 是ndisp=状态-numberOfDisparities;

mindisp是mindisp=state-minDisparity;

心灵是悲伤获得的视差

实际视差约为(64-mind-1)*256=1163,基本正确。后者的修正值一般情况下可以忽略。

目前我还不是很清楚三维坐标系的原点和尺度,但是从这三个点的z坐标可以大致看出,三个物体之间的距离差在13左右,即与实际场景中物体的位置基本一致。因此,通过该方法可以确定物体的大概距离信息。

但如果从相机参数本身来测量距离,就不是很清楚了。想请这方面的专家来解答一下。

5.一些问题

5.1 关于立体校准

OpenCV自带的cvStereoCalibrate不太好用。利用该函数计算出的内外参数以及旋转和平移矩阵进行校准往往无法实现行对齐,有时甚至会出现可怕的扭曲。看了piao的帖子http://www.opencv.org.cn/forum/viewtopic.php?f=1t=4603后,我也尝试使用cvCalibrateCamera2分别进行校准(左右各20张图片),结果基本一致与Matlab单独标定一样,然后将cvStereoCalibrate中的参数设置为CV_CALIB_USE_INTRINSIC_GUESS来细化内部参数和畸变参数。由此产生的校准结果再次变形。

不知道大家有没有这方面的成功经验,可以分享一下吗?毕竟使用Matlab工具箱还是有点麻烦。

5.2立体匹配推导出的平移向量和世界坐标系

学习OpenCV中平移和旋转的图示如下

5.1.在OpenCV 中学习图形

但实验过程中发现,如果Translation向量按照比例缩放,StereoRectify后的左右视图不会改变,例如T=[ -226.73817 -0.62302 8.93984 ] 变成T=[ -22.673817 -0.062302 0.893984],OpenCV中显示的结果不会发生变化。而如果我修改其中一个参数,左右视图的变化并不是图5.1所示的变化(比如x减小,那么视图的变化就不是x轴方向的平移) 。

那么回到老问题,这里这些坐标的比例尺到底是多少?通过ReprojectTo3D函数得到的三维坐标的原点是哪一点,x、y、z轴是哪三个方向?

补充: 这个问题的答案来自于与maxwellsdemon的讨论

他的解释是这样的:rotation是两者的旋转角度的关系,但是如果想要修正为平行,还需要一个平移矩阵。你可以想象一下,两个看似平行的相机,但是深度上有差距,那么校正时就会以平移矩阵对应的角度或者直线作为参考,将两个相机旋转一个小的距离。角度使它们完全平行。

审稿人:李茜

最新资讯

推荐资讯