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

Post date: 2012/4/10 上午 08:25:43

chenyusiyuan » 2010-10-25 1:17

双目测距的基本原理

1.jpg (37.04 KiB) 被浏览 7248 次

图1

如上图所示,双目测距主要是利用了目标点在左右两幅视图上成像的横向坐标直接存在的差异(即视差 )与目标点到成像平面的距离Z存在着反比例的关系:Z=fT/d

“@scyscyao:在OpenCV中,f的量纲是像素点,T的量纲由定标板棋盘格的实际尺寸和用户输入值确定,一般是以毫米为单位(当然为了精度提高也可以设置为0.1毫米量级),d=xl-xr的量纲也是像素点。因此分子分母约去,Z的量纲与T相同。”

2.jpg (15.49 KiB) 被浏览 7248 次

图2

假设目标点在左视图中的坐标为(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 同时读取两个摄像头,不过后来一位研友来信讨论说只要把摄像头指针的创建代码按照摄像头序号降序执行,就可以顺利打开多个摄像头,例如:

代码: 全选

    CvCapture* capture2 = cvCreateCameraCapture( 1 );
    CvCapture* capture1 = cvCreateCameraCapture( 0 );

采用DirectShow的方式读入时:

代码: 全选

    camera2.OpenCamera(1, false, 640,480);
    camera1.OpenCamera(0, false, 640,480);

这样就可以同时采集两个摄像头。我也验证过这种方法确实有效,而且还解决了我遇到的cvSetCaptureProperty调整帧画面大小速度过慢的问题。当摄像头的打开或创建代码按照摄像头序号从0开始以升序编写执行时,使用cvSetCaptureProperty就会出现第一个摄像头(序号为0)的显示窗口为灰色(即无图像)、且程序运行速度缓慢的现象。而改为降序编写执行后,则能正常、实时地显示各摄像头的画面。具体原因有待分析讨论。

2. 如何实现多个摄像头帧画面的同步抓取?

在单摄像头情况下用 cvQueryFrame 即可抓取一帧画面,实际上这个函数是由两个routine组成的:cvGrabFrame和cvRetrieveFrame(详见Learning OpenCV第103页)。cvGrabFrame将摄像头帧画面即时复制到内部缓存中,然后通过cvRetrieveFrame把我们预定义的一个IplImage型空指针指向缓存内的帧数据。注意这时我们并没有真正把帧数据取出来,它还保存在OpenCV的内部缓存中,下一次读取操作就会被覆盖掉。所以一般我们要另外定义一个IplImage来复制所抓取的帧数据,然后对这个新IplImage进行操作。

由上面的解释也可以看出,cvGrabFrame的作用就是尽可能快的将摄像头画面数据复制到计算机缓存,这个功能就方便我们实现对多个摄像头的同步抓取,即首先用cvGrabFrame依次抓取各个CvCapture*,然后再用cvRetrieveFrame把帧数据取出来。例如:

代码: 全选

   cvGrabFrame( lfCam );
   cvGrabFrame( riCam );
   frame1 = cvRetrieveFrame( lfCam );
   frame2 = cvRetrieveFrame( riCam );
   if( !frame1|| !frame2) break;
   cvCopyImage(frame1, image1); 
   cvCopyImage(frame2, image2);

二、摄像头定标

摄像头定标一般都需要一个放在摄像头前的特制的标定参照物(棋盘纸),摄像头获取该物体的图像,并由此计算摄像头的内外参数。标定参照物上的每一个特征点相对于世界坐标系的位置在制作时应精确测定,世界坐标系可选为参照物的物体坐标系。在得到这些已知点在图像上的投影位置后,可计算出摄像头的内外参数。

3.jpg (32.17 KiB) 被浏览 7248 次

图3

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

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

4.jpg (27.09 KiB) 被浏览 7248 次

图4

5.jpg (36.99 KiB) 被浏览 7248 次

图5

摄像头成像几何关系,其中Oc点称为摄像头(透镜)的光心,Xc轴和Yc轴与图像的x轴和Y轴平行,Zc轴为摄像头的光轴,它与图像平面垂直。光轴与图像平面的交点O1,即为图像坐标系的原点。由点Oc与Xc、Yc、Zc轴组成的坐标系称为摄像头坐标系,OcO1的距离为摄像头焦距,用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),相当精确。”

补充一下:

《基于OpenCV的计算机视觉技术实现 》这本书上的相机标定程序中

代码: 全选

CvSize square_size = cvSize(10,10)

里的(10,10)是以什么为单位的呢?

这个问题也是初学者比较容易混淆的,实际上这里的 square_size 是 CvSize结构体,是用于cvFindChessboardCorners 中指定棋盘方格的角点数,上面的(10,10)就是指棋盘上的角点数是 10*10 个。这与棋盘方格的大小(double型数据变量)是不同的。

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标定工具箱使用的一些注意事项

.

.

chenyusiyuan

OpenCV本科生

帖子: 144

注册: 2009-09-23 10:40

Aliced3645

OpenCV幼儿园宝宝

帖子: 1

注册: 2010-11-10 15:54

puppy0415

OpenCV初中生

帖子: 28

注册: 2008-10-26 11:00

hiker2046

OpenCV小学生

帖子: 6

注册: 2009-04-23 13:29

chriswoo

OpenCV小学生

帖子: 18

注册: 2010-09-04 16:12

chriswoo » 2010-12-08 16:52

牛人,你好,我刚开始搞立体视觉,我想问一下,视差 d=xl-xr,那么xl和xr在途中分别代表哪一段呢,可能问题有点弱智,但我确实有点不清楚

chriswoo

OpenCV小学生

帖子: 18

注册: 2010-09-04 16:12

chriswoo » 2010-12-18 11:51

牛人,你好,我想问你一个问题,就是视差图到底是什么,视差图的像素结构是什么,问题可能不专业。我正在看cvFindStereoCorresPondenceBM的源码,但是还是想请教一下你,这样效率更高,是吧,你有时间就回答我一下,万分感谢

chriswoo

OpenCV小学生

帖子: 18

注册: 2010-09-04 16:12

dnegel » 2011-01-18 21:32

多谢楼主分享。我用楼主的方法尝试同时显示两个摄像头帧图像,我定义了IplImage* frame2; IplImage* img2;IplImage* frame1; IplImage* img1;可是vc++提示说img2没有初始化,程序不能运行。请楼主指教。

dnegel

OpenCV幼儿园宝宝

帖子: 3

注册: 2010-11-11 20:41

dnegel » 2011-01-18 21:35

初次登陆,还不太清楚网站使用规则,唯恐遗漏楼主回复,特留邮箱方便联系:dnegel1987@gmail.com。

dnegel

OpenCV幼儿园宝宝

帖子: 3

注册: 2010-11-11 20:41

dnegel » 2011-01-18 21:55

另外,我在另一篇大作 http://www.opencv.org.cn/index.php/Open ... 1.E6.81.AF: 中看到说:若要从多个摄像头中同步捕捉画面,则须首先从每个摄像头中抓取一帧,紧接着要将被捕捉的帧画面恢复到一个IplImage*型图像中。(译注:这一过程其实可以用 cvQueryFrame() 函数一步完成),不知这个译注怎么讲?

dnegel

OpenCV幼儿园宝宝

帖子: 3

注册: 2010-11-11 20:41

biluocyndi » 2011-07-06 14:03

对于楼主在摄像机定标部分的第二个问题的补充部分,我发现很多人都是这么解释,可是我在做标定的时候发现,

代码: 全选

CvSize square_size=cvsize(10,10);

这一句并不是用于提取角点的,而是用于摄像机定标用的;所以我个人意见以及通过和matlab标定结果对比发现这个地方应该就是你的标定板棋盘格的实际大小。

用于提取角点的时候指定棋盘方格角点数的是

代码: 全选

CvSize board_size=cvsize(7,7);

这个地方。这里应该是棋盘格的角点数。不知道我的理解对不对,楼主有空的话探讨一下啊

biluocyndi

OpenCV小学生

帖子: 9

注册: 2010-12-24 1:01

chiefhe

OpenCV初中生

帖子: 22

注册: 2011-07-14 11:09

snowbird13 » 2011-07-25 11:25

楼主,请教个问题,最近在研究自己导入角点数据和世界坐标。做标定。我使用的zhang_data的方式。其它的也有试,但都出错。请看。我是取得36个图像点,并根据实际尺寸也推出了世界坐标。本来按照要求做了五个data,不行,尝试一组data也不行。

下面是一组data,左面三列是世界坐标单位是MM,右面两列是图像角点单位Pixel。我查过坐标对应没错。请高手指教,除了数据,是否其它地方需修正。

0 169.35 0 400.38 760.46

33.87 169.35 0 517.73 754.22

67.74 169.35 0 632.04 748.11

101.61 169.35 0 744.01 742.06

135.48 169.35 0 853.12 736.22

169.35 169.35 0 959.62 730.33

0 0 0 388.45 646.36

33.87 135.48 0 504.05 641.5

67.74 135.48 0 616.99 637.05

101.61 135.48 0 727.32 632.46

135.48 135.48 0 835.23 627.88

169.35 135.48 0 940.57 623.38

0 101.61 0 376.8 535.88

33.87 101.61 0 490.86 532.37

67.74 101.61 0 601.7 529.19

101.61 101.61 0 711.34 525.69

135.48 101.61 0 817.71 522.18

169.35 101.61 0 921.85 519.09

0 67.74 0 365.16 428.23

33.87 67.74 0 478.16 426.03

67.74 67.74 0 588.16 423.93

101.61 67.74 0 695.63 421.74

135.48 67.74 0 800.88 419.87

169.35 67.74 0 903.84 417.46

0 33.87 0 465.91 322.98

33.87 33.87 0 354.61 323.9

67.74 33.87 0 574.24 321.8

101.61 33.87 0 680.72 320.89

135.48 33.87 0 886.15 318.98

169.35 33.87 0 784.45 319.89

0 0 0 869.25 222.95

33.87 0 0 768.32 223.18

67.74 0 0 666.07 222.66

101.61 0 0 560.82 222.51

135.48 0 0 453.74 222.35

169.35 0 0 344.23 222.58

下面是出错信息。

WARNING: Principal point rejected from the optimization when using one image and planar rig (center_optim = 1).

Aspect ratio optimized (est_aspect_ratio = 1) -> both components of fc are estimated (DEFAULT).

Principal point not optimized (center_optim=0). It is kept at the center of the image.

Skew not optimized (est_alpha=0) - (DEFAULT)

Distortion not fully estimated (defined by the variable est_dist):

Sixth order distortion not estimated (est_dist(5)=0) - (DEFAULT) .

Initialization of the intrinsic parameters using the vanishing points of planar patterns.

Initialization of the intrinsic parameters - Number of images: 1

Calibration parameters after initialization:

Focal Length: fc = [ 331.23638 372.29355 ]

Principal point: cc = [ 639.50000 511.50000 ]

Skew: alpha_c = [ 0.00000 ] => angle of pixel = 90.00000 degrees

Distortion: kc = [ 0.00000 0.00000 0.00000 0.00000 0.00000 ]

Main calibration optimization procedure - Number of images: 1

Gradient descent iterations: 1...2...

Warning: View #1 ill-conditioned. This image is now set inactive. (note: to disactivate this option, set check_cond=0)

3...Intrinsic parameters at frame 1 do not exist

New optimization including the images that have been deactivated during the previous optimization.

Aspect ratio optimized (est_aspect_ratio = 1) -> both components of fc are estimated (DEFAULT).

Principal point not optimized (center_optim=0). Note: to set it in the middle of the image, clear variable cc, and run calibration again.

Skew not optimized (est_alpha=0) - (DEFAULT)

Distortion not fully estimated (defined by the variable est_dist):

Sixth order distortion not estimated (est_dist(5)=0) - (DEFAULT) .

Main calibration optimization procedure - Number of images: 1

Gradient descent iterations: 1...2...

Warning: View #1 ill-conditioned. This image is now set inactive. (note: to disactivate this option, set check_cond=0)

3...Intrinsic parameters at frame 1 do not exist

List of images left desactivated: 1

Error: There is no active image. Run Add/Suppress images to add images

snowbird13

OpenCV小学生

帖子: 7

注册: 2011-07-25 11:10

chenyusiyuan » 2011-08-21 23:56

你这儿出错的原因是图片不够,只有1幅图,一般都要10幅以上图片的。

snowbird13 写道:

楼主,请教个问题,最近在研究自己导入角点数据和世界坐标。做标定。我使用的zhang_data的方式。其它的也有试,但都出错。请看。我是取得36个图像点,并根据实际尺寸也推出了世界坐标。本来按照要求做了五个data,不行,尝试一组data也不行。

下面是一组data,左面三列是世界坐标单位是MM,右面两列是图像角点单位Pixel。我查过坐标对应没错。请高手指教,除了数据,是否其它地方需修正。

0 169.35 0 400.38 760.46

33.87 169.35 0 517.73 754.22

67.74 169.35 0 632.04 748.11

101.61 169.35 0 744.01 742.06

135.48 169.35 0 853.12 736.22

169.35 169.35 0 959.62 730.33

0 0 0 388.45 646.36

33.87 135.48 0 504.05 641.5

67.74 135.48 0 616.99 637.05

101.61 135.48 0 727.32 632.46

135.48 135.48 0 835.23 627.88

169.35 135.48 0 940.57 623.38

0 101.61 0 376.8 535.88

33.87 101.61 0 490.86 532.37

67.74 101.61 0 601.7 529.19

101.61 101.61 0 711.34 525.69

135.48 101.61 0 817.71 522.18

169.35 101.61 0 921.85 519.09

0 67.74 0 365.16 428.23

33.87 67.74 0 478.16 426.03

67.74 67.74 0 588.16 423.93

101.61 67.74 0 695.63 421.74

135.48 67.74 0 800.88 419.87

169.35 67.74 0 903.84 417.46

0 33.87 0 465.91 322.98

33.87 33.87 0 354.61 323.9

67.74 33.87 0 574.24 321.8

101.61 33.87 0 680.72 320.89

135.48 33.87 0 886.15 318.98

169.35 33.87 0 784.45 319.89

0 0 0 869.25 222.95

33.87 0 0 768.32 223.18

67.74 0 0 666.07 222.66

101.61 0 0 560.82 222.51

135.48 0 0 453.74 222.35

169.35 0 0 344.23 222.58

下面是出错信息。

WARNING: Principal point rejected from the optimization when using one image and planar rig (center_optim = 1).

Aspect ratio optimized (est_aspect_ratio = 1) -> both components of fc are estimated (DEFAULT).

Principal point not optimized (center_optim=0). It is kept at the center of the image.

Skew not optimized (est_alpha=0) - (DEFAULT)

Distortion not fully estimated (defined by the variable est_dist):

Sixth order distortion not estimated (est_dist(5)=0) - (DEFAULT) .

Initialization of the intrinsic parameters using the vanishing points of planar patterns.

Initialization of the intrinsic parameters - Number of images: 1

Calibration parameters after initialization:

Focal Length: fc = [ 331.23638 372.29355 ]

Principal point: cc = [ 639.50000 511.50000 ]

Skew: alpha_c = [ 0.00000 ] => angle of pixel = 90.00000 degrees

Distortion: kc = [ 0.00000 0.00000 0.00000 0.00000 0.00000 ]

Main calibration optimization procedure - Number of images: 1

Gradient descent iterations: 1...2...

Warning: View #1 ill-conditioned. This image is now set inactive. (note: to disactivate this option, set check_cond=0)

3...Intrinsic parameters at frame 1 do not exist

New optimization including the images that have been deactivated during the previous optimization.

Aspect ratio optimized (est_aspect_ratio = 1) -> both components of fc are estimated (DEFAULT).

Principal point not optimized (center_optim=0). Note: to set it in the middle of the image, clear variable cc, and run calibration again.

Skew not optimized (est_alpha=0) - (DEFAULT)

Distortion not fully estimated (defined by the variable est_dist):

Sixth order distortion not estimated (est_dist(5)=0) - (DEFAULT) .

Main calibration optimization procedure - Number of images: 1

Gradient descent iterations: 1...2...

Warning: View #1 ill-conditioned. This image is now set inactive. (note: to disactivate this option, set check_cond=0)

3...Intrinsic parameters at frame 1 do not exist

List of images left desactivated: 1

Error: There is no active image. Run Add/Suppress images to add images

chenyusiyuan

OpenCV本科生

帖子: 144

注册: 2009-09-23 10:40

qm1004

OpenCV小学生

帖子: 8

注册: 2010-01-18 9:47

xlqstar » 2012-01-16 14:27

图2部分就开始看不懂了呀,

那个Q矩阵是怎么来的?

xlqstar

OpenCV幼儿园宝宝

帖子: 1

注册: 2012-01-16 14:25

nettxs20052 » 2012-03-24 21:40

楼主,我根据你文章中提到的打开两个摄像头读取图像的方法,把摄像头指针的创建代码按照摄像头序号降序执行,可最终运行的时候仍然是图像均不显示,窗口是灰色的,而且一闪就过,根本不停留,不知是怎么回事啊