文档库 最新最全的文档下载
当前位置:文档库 › OpenCV学习笔记(16)双目测距与三维重建的OpenCV实现问题集锦(一)图像获取与单目定标

OpenCV学习笔记(16)双目测距与三维重建的OpenCV实现问题集锦(一)图像获取与单目定标

OpenCV学习笔记(16)双目测距与三维重建的OpenCV实现问题集锦(一)图像获取与单目定标
OpenCV学习笔记(16)双目测距与三维重建的OpenCV实现问题集锦(一)图像获取与单目定标

异(即视差

假设目标点在左视图中的坐标为(x,y),在左右视图上形成的视差为d,目标点在以左摄像头光心为原点的世界坐标系中的坐标为(X,Y,Z),则存在上图所示的变换矩阵Q,使得Q*[x y d 1]’ = [X Y Z W]’。

“@scyscyao:为了精确地求得某个点在三维空间里的距离Z,我们需要获得的参数有焦距f、视差d、摄像头中心距Tx。如果还需要获得X坐标和Y坐标的话,那么还需要额外知道左右像平面的坐标系与立体坐标系中原点的偏移cx和cy。其中f, Tx, cx和cy可以通过立体标定获得初始值,并通过立体校准优化,使得两个摄像头在数学上完全平行放置,并且左右摄像头的cx, cy和f相同(也就是实现图2中左右视图完全平行对准的理想形式)。而立体匹配所做的工作,就是在之前的基础上,求取最后一个变量:视差d(这个d一般需要达到亚像素精度)。从而最终完成求一个点三维坐标所需要的准备工作。在清楚了上述原理之后,我们也就知道了,所有的这几步:标定、校准和匹配,都是围绕着如何更精确地获得f, d, Tx, cx和cy而设计的。”

一、图像的获取

1.如何打开两个或多个摄像头?

可以通过OpenCV的capture类函数或者结合DirectShow来实现双摄像头的捕获,具体可见我的读书笔记《OpenCV学习笔记(6)基于VC+OpenCV+DirectShow 的多个摄像头同步工作》。文中曾提及不能用cvCreateCameraCapture 同时读取两个摄像头,不过后来一位研友来信讨论说只要把摄像头指针的创建代码按照摄像头序号降序执行,就可以顺利打开多个摄像头,例如:

[c-sharp]view plaincopyprint?

1.CvCapture* capture2 = cvCreateCameraCapture( 1 );

2.CvCapture* capture1 = cvCreateCameraCapture( 0 );

1.camera

2.OpenCamera(1, false, 640,480);

2.camera1.OpenCamera(0, false, 640,480);

取出来,它还保存在OpenCV的内部缓存中,下一次读取操作就会被覆盖掉。所以一般我们要另外定义一个IplImage来复制所抓取的帧数据,然后对这个新IplImage进行操作。由上面的解释也可以看出,cvGrabFrame的作用就是尽可能快的将摄像头画面数据复制到计算机缓存,这个功能就方便我们实现对多个摄像头的同步抓取,即首先用cvGrabFrame 依次抓取各个CvCapture*,然后再用cvRetrieveFrame把帧数据取出来。例如:

[c-sharp]view plaincopyprint?

1.cvGrabFrame( lfCam );

2.cvGrabFrame( riCam );

3.frame1 = cvRetrieveFrame( lfCam );

4.frame2 = cvRetrieveFrame( riCam );

5.if( !frame1|| !frame2) break;

6.cvCopyImage(frame1, image1);

7.cvCopyImage(frame2, image2);

如上图所示,摄像头由于光学透镜的特性使得成像存在着径向畸变,可由三个参数k1,k2,k3确定;由于装配方面的误差,传感器与光学镜头之间并非完全平行,因此成像存在切向畸变,可由两个参数p1,p2确定。单个摄像头的定标主要是计算出摄像头的内参(焦距f和成像原点cx,cy、五个畸变参数(一般只需要计算出k1,k2,p1,p2,对于鱼眼镜头等径向畸变特别大的才需要计算k3))以及外参(标定物的世界坐标)。OpenCV中使用的求解焦距和成像原点的算法是基于张正友的方法(pdf),而求解畸变参数是基于Brown的方法(pdf)。

1.图像坐标系、摄像头坐标系和世界坐标系的关系

摄像头成像几何关系,其中O c点称为摄像头(透镜)的光心,X c轴和Y c轴与图像的x 轴和Y轴平行,Z c轴为摄像头的光轴,它与图像平面垂直。光轴与图像平面的交点O1,即为图像坐标系的原点。由点O c与X c、Y c、Z c轴组成的坐标系称为摄像头坐标系,O c O1的距离为摄像头焦距,用f表示。

图像坐标系是一个二维平面,又称为像平面,“@scyscyao:实际上就是摄像头的CCD传感器的表面。每个CCD传感器都有一定的尺寸,也有一定的分辨率,这个就确定了毫米与像素点之间的转换关系。举个例子,CCD的尺寸是8mm X 6mm,帧画面的分辨率设置为640X480,那么毫米与像素点之间的转换关系就是80pixel/mm。”设CCD传感器每个像素点的物理大小为dx*dy,相应地,就有dx=dy=1/80。

2.进行摄像头定标时,棋盘方格的实际大小square_size (默认为1.0f )的设置对定标参数是否有影响?

“@scyscyao:当然有。在标定时,需要指定一个棋盘方格的长度,这个长度(一般以毫米为单位,如果需要更精确可以设为0.1毫米量级)与实际长度相同,标定得出的结果才能用于实际距离测量。一般如果尺寸设定准确的话,通过立体标定得出的Translation向量的第

一个分量Tx的绝对值就是左右摄像头的中心距。一般可以用这个来验证立体标定的准确度。比如我设定的棋盘格大小为270 (27mm),最终得出的Tx大小就是602.8 (60.28mm),相当精确。”

3.定标所得的摄像头内参数,即焦距和原点坐标,其数值单位都是一致的吗?怎么把焦距数值换算为实际的物理量?

“@wobject:是的,都是以像素为单位。假设像素点的大小为k x l,单位为mm,则fx = f / k,fy = f / (l * sinA),A一般假设为90°,是指摄像头坐标系的偏斜度(就是镜头坐标和CCD是否垂直)。摄像头矩阵(内参)的目的是把图像的点从图像坐标转换成实际物理的三维坐标。因此其中的fx, fy, cx, cy 都是使用类似上面的纲量。同样,Q 中的变量f,cx, cy 也应该是一样的。”

4.棋盘图像数目应该取多少对摄像头定标比较适宜?

OpenCV中文论坛上piao的帖子《在OpenCV中用cvCalibrateCamera2进行相机标定(附程序)》中指出影响摄像头定标结果的准确性和稳定性的因素主要有三个:

(1)标定板所在平面与成像平面(image plane)之间的夹角;

(2)标定时拍摄的图片数目(棋盘图像数目);

(3)图像上角点提取的不准确。

感觉OpenCV1.2以后对图像角点的提取准确度是比较高的,cvFindChessboardCorners 和cvFindCornerSubPix结合可以获得很好的角点检测效果(hqhuang1在《[HQ]角点检测(Corner Detection) cvFindCornerSubPix 使用范例》中给出了相关的应用范例)。因此,影响定标结果较大的就是标定板与镜头的夹角和棋盘图像数目,在实际定标过程中,我感觉棋盘图像数目应该大于20张,每成功检测一次完整的棋盘角点就要变换一下标定板的姿态(包括角度、距离)。

5.单目定标函数cvCalibrateCamera2采用怎样的flags 比较合适?

由于一般镜头只需要计算k1,k2,p1,p2四个参数,所以我们首先要设置

CV_CALIB_FIX_K3;其次,如果所用的摄像头不是高端的、切向畸变系数非常少的,则不要设置CV_CALIB_ZERO_TANGENT_DIST,否则单目校正误差会很大;如果事先知道摄像头内参的大概数值,并且cvCalibrateCamera2函数的第五个参数intrinsic_matrix非空,则也可设置CV_CALIB_USE_INTRINSIC_GUESS ,以输入的intrinsic_matrix为初始估计值来加快内参的计算;其它的flag 一般都不需要设置,对单目定标的影响不大。

P.S. 使用OpenCV进行摄像机定标虽然方便,但是定标结果往往不够准确和稳定,最好是使用Matlab标定工具箱来进行定标,再将定标结果取回来用于立体匹配和视差计算。工具箱的使用官方主页有图文并茂的详细说明,此外,有两篇博文也进行了不错的总结,推荐阅读:

(1)分享一些OpenCV实现立体视觉的经验

(2)Matlab标定工具箱使用的一些注意事项

相关文档