OpenCV 4.11.0
开源计算机视觉库
|
本模块包含全向相机的标定、校正和立体重建。相机模型在以下论文中描述
C. Mei 和 P. Rives,基于平面网格的全向相机单视点标定,ICRA 2007。
该模型能够模拟具有非常大视场的反射式相机和鱼眼相机。
标定部分的实现基于 Li 的标定工具箱
B. Li,L. Heng,K. Kevin 和 M. Pollefeys,“基于特征描述符的标定图案的多相机系统标定工具箱”,IROS 2013。
本教程将介绍全向相机标定模块的以下部分
相机标定的第一步是获取标定图案并拍摄一些照片。OpenCV 支持几种类型的图案,例如棋盘格和圆形网格。还可以使用名为随机图案的新图案,有关更多详细信息,请参阅 opencv_contrib/modules/ccalib。
下一步是从标定图案中提取角点。对于棋盘格,使用 OpenCV 函数 cv::findChessboardCorners
;对于圆形网格,使用 cv::findCirclesGrid
;对于随机图案,使用 opencv_contrib/modules/ccalib/src/randomPattern.hpp 中的 randomPatternCornerFinder
类。将图像中角点的坐标保存到类似于 imagePoints
的变量中。imagePoints
的类型可以是 std::vector<std::vector<cv::Vec2f>>
,第一个向量存储每一帧的角点,第二个向量存储单个帧中的角点。该类型也可以是 std::vector<cv::Mat>
,其中 cv::Mat
为 CV_32FC2
类型。
此外,还需要世界(图案)坐标系中的对应 3D 点。如果您知道图案的物理尺寸,您可以自己计算它们。将 3D 点保存在 objectPoints
中,类似于 imagePoints
,它可以是 std::vector<std::vector<Vec3f>>
或 std::vector<cv::Mat>
,其中 cv::Mat
的类型为 CV_32FC3
。请注意,objectPoints
和 imagePoints
的大小必须相同,因为它们是相互对应的。
您应该输入的另一个参数是图像的大小。文件 opencv_contrib/modules/ccalib/tutorial/data/omni_calib_data.xml 存储了 objectPoints、imagePoints 和 imageSize 的示例。使用以下代码加载它们:
然后定义一些变量来存储输出参数并运行标定函数,例如:
K
、xi
、D
是内参,rvecs
、tvecs
是存储图案位姿的外参。它们都具有 CV_64F
的深度。xi
是 Mei 模型的单值变量。idx
是一个 CV_32S
Mat,存储在标定中实际使用的图像索引。这是因为某些图像在初始化步骤中失败,因此未在最终优化中使用。返回值 *rms* 是重投影误差的均方根。
标定支持一些特性,flags 是枚举一些特性,包括:
您可以指定 flags
在标定过程中固定参数。使用“加号”运算符设置多个特性。例如,CALIB_FIX_SKEW+CALIB_FIX_K1
表示固定倾斜度和 K1。
criteria
是优化过程中的停止条件,例如将其设置为 cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 200, 0.0001),这意味着使用 200 次迭代,并在相对变化小于 0.0001 时停止。
立体标定是同时标定两个相机。输出参数包括两个相机的相机参数及其相对位姿。为了恢复相对位姿,两个相机必须同时观察相同的图案,因此两个相机的 objectPoints
是相同的。
现在如上所述检测两个相机的图像角点以获取 imagePoints1
和 imagePoints2
。然后计算共享的 objectPoints
。
立体标定数据的示例存储在 opencv_contrib/modules/ccalib/tutorial/data/omni_stereocalib_data.xml 中。通过以下方式加载数据:
然后进行立体标定:
这里rvec
和tvec
表示第一个相机和第二个相机之间的变换。rvecsL
和tvecsL
表示标定板和第一个相机之间的变换。
全向图像具有非常大的畸变,与人眼视觉不兼容。为了获得更好的视觉效果,如果已知相机参数,可以应用校正。这是一个水平视场角为360度的全向图像示例。
校正后,会生成类似透视的视图。以下是如何在此模块中运行图像校正的一个示例
变量distorted和undistorted分别表示原始图像和校正后的图像。K、D、xi是相机参数。KNew和new_size是校正后图像的相机矩阵和图像大小。flags是校正类型,可以是:
以下四幅图像是上面描述的四种校正图像
可以观察到,透视校正图像只保留了很少一部分视场,而且看起来不好。圆柱校正保留了所有视场,但场景在下半部分中间看起来不自然。立体投影在下半部分中间的畸变小于圆柱校正,但其他地方的畸变更大,并且它不能保留所有视场。对于畸变非常大的图像,经纬度校正不会给出好的结果,但它可以使极线约束在一条线上,以便可以在全向图像中应用立体匹配。
注意:为了获得更好的结果,应仔细选择Knew
,它与您的相机有关。一般来说,较短的焦距导致较小的视场,反之亦然。以下是推荐的设置。
对于RECTIFY_PERSPECTIVE
0, 0, 1);
0, new_size.height/3.1415, 0,
您可能需要更改(u0, v0)
以获得更好的视图。
立体重建是从已校准的立体相机对重建3D点。这是计算机视觉中的一个基本问题。然而,对于全向相机来说,由于较大的畸变使其略微困难,因此它并不十分流行。传统方法将图像校正为透视图像,并在透视图像中进行立体重建。然而,上一节显示将图像校正为透视图像会损失太多的视场,这浪费了全向相机的优势,即大的视场。
立体重建的第一步是立体校正,使极线成为水平线。在这里,我们使用经纬度校正来保留所有视场,或者使用可用的透视校正,但不推荐。第二步是立体匹配以获得视差图。最后,可以从视差图生成3D点。
全向相机的立体重建API是omnidir::stereoReconstruct
。这里我们用一个例子来展示它是如何工作的。
K1
、D1
、xi1
、K2
、D2
、xi2
、rvec
、tvec
之类的参数。然后分别从第一台和第二台相机读取两幅图像,例如image1
和image2
,如下所示。omnidir::stereoReconstruct
,例如:Mat imageRec1, imageRec2, pointCloud;
cv::omnidir::stereoReconstruct(img1, img2, K1, D1, xi1, K2, D2, xi2, R, T, flag, numDisparities, SADWindowSize, disMap, imageRec1, imageRec2, imgSize, KNew, pointCloud);
这里的变量flag
指示校正类型,只有RECTIFY_LONGLATI
(推荐)和RECTIFY_PERSPECTIVE
才有意义。numDisparities
是最大视差值,SADWindowSize
是cv::StereoSGBM
的窗口大小。pointType
是一个标志,用于定义点云的类型,omnidir::XYZRGB
每个点是一个6维向量,前三个元素是xyz坐标,后三个元素是rgb颜色信息。另一种类型omnidir::XYZ
表示每个点是3维的,只有xyz坐标。
此外,imageRec1
和imagerec2
是第一幅和第二幅图像的校正版本。它们的极线具有相同的y坐标,因此立体匹配变得容易。以下是一个例子:
disMap
是由cv::StereoSGBM
从imageRec1
和imageRec2
计算出的视差图。上面两幅图像的视差图是: