原博客地址
先简单回顾一下计算机视觉的知识。这里研究生的摄像机模型都是针孔摄像机,摄像机的标定问题是CV领域的一个入门级的问题,初学摄像机标定时会被各种坐标系弄晕,这里再介绍一下,
常提到的坐标系有四个:
摄像头定标一般都需要一个放在摄像头前的特制的标定参照物(棋盘纸),摄像头获取该物体的图像,并由此计算摄像头的内外参数。标定参照物上的每一个特征点相对于世界坐标系的位置在制作时应精确测定,世界坐标系可选为参照物的物体坐标系。在得到这些已知点在图像上的投影位置后,可计算出摄像头的内外参数。
如上图所示,摄像头由于光学透镜的特性使得成像存在着径向畸变,可由三个参数k1,k2,k3确定;由于装配方面的误差,传感器与光学镜头之间并非完全平行,因此成像存在切向畸变,可由两个参数p1,p2确定。单个摄像头的定标主要是计算出摄像头的内参(焦距f和成像原点cx,cy、五个畸变参数(一般只需要计算出k1,k2,p1,p2,对于鱼眼镜头等径向畸变特别大的才需要计算k3))以及外参(标定物的世界坐标)。 OpenCV 中使用的求解焦距和成像原点的算法是基于张正友的方法( pdf ),而求解畸变参数是基于 Brown 的方法( pdf )。
1. 图像坐标系、摄像头坐标系和世界坐标系的关系
摄像头成像几何关系,其中Oc 点称为摄像头(透镜)的光心,Xc 轴和Yc 轴与图像的x轴和Y轴平行,Zc 轴为摄像头的光轴,它与图像平面垂直。光轴与图像平面的交点O1 ,即为图像坐标系的原点。由点Oc 与Xc 、Yc 、Zc 轴组成的坐标系称为摄像头坐标系,Oc 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标定工具箱使用的一些注意事项
这样再看相关资料的时候就不会混了吧,这里再介绍一篇张正友的摄像机定标办法的相关资料
其参数分为内参数和外参数:
通过calibrateCamera函数可以得到这些内外参数,其他一些有用的函数如下:
calibrationMatrixValues | 可以得到摄像机投影透视方程的投影矩阵 |
composeRT | 可以合并两个旋转平移变换 |
computeCorrespondEpilines | 为一幅图像中的点计算其在另一幅图像中对应的对极线 |
convertPointsToHomogeneous | 把点从欧式空间转换到齐次空间 |
convertPointsFromHomogeneous | 把点从齐次空间变换到欧式空间 |
convertPointsHomogeneous | 把上述两个函数功能综合到一起了 |
decomposeProjectionMatrix | 可以将矩阵分解 |
drawChessboardCorners |
获得检测棋盘的角 |
findChessboardCorners |
获得棋盘的内角点位置 |
findCirclesGrid |
得到圆圈光栅的中心 |
solvePnP |
实现物体位置的3维坐标和2维坐标之间的转换 |
solvePnPRansac |
利用RANSAC实现上述功能 |
findFundamentalMat |
计算两幅图像关联点的基础矩阵 |
findHomography |
找出两个平面的透视变换 |
estimateAffine3D |
计算两个3维点集的理想仿射变换 |
filterSpeckles |
可以过滤不同块的小斑点 |
getOptimalNewCameraMatrix |
得到自由比例参数的新摄像机矩阵 |
initCameraMatrix2D |
得到3D到2D的初始化的摄像机矩阵 |
matMulDeriv |
计算矩阵的偏导数 |
projectPoints |
将3D坐标投影到图像平面上 |
reprojectImageTo3D |
根据一组差异图像重建3D空间 |
RQDecomp3x3 |
计算3x3矩阵的RQ分解 |
Rodrigues |
实现旋转矩阵和旋转向量之间的转换 |
steroCalibrate |
校准立体摄像机 |
steroRectify |
是对校准过的摄像机计算修正变换 |
stereoRectifyUncalibrated |
是对未校准过的摄像机计算修正变换 |
还包括了BM块匹配算法类StereoBM、SGBM块匹配算法类StereoSGBM类
其他资料 其他资料1