OpenCV 4.10.0
开源计算机视觉
|
本模块包括全景摄像机的校准、整平和立体重建。摄像机模型在此技术文件中进行了说明
C. Mei 和 P. Rives,2007 年国际机器人和自动化会议上所述的平面的单视点全景摄像机校准。
该模型能够对具有超大视野的对透射摄像机和鱼眼摄像机进行建模。
校准部分的实现基于 Li 的校准工具箱
B. Li、L. Heng、K. Kevin 和 M. Pollefeys, 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
对于 RECTIFY_CYLINDRICAL, RECTIFY_STEREOGRAPHIC, RECTIFY_LONGLATI
可能需要更改 (u0, v0)
以获得更好的视角。
立体重建是从校准的立体相机对重建 3D 点。这是一个计算机视觉的基本问题。不过,对于全景相机来说,它并不是很受欢迎,这是因为大幅畸变使之有些困难。传统方法将图像校正为透视图像并在透视图像中进行立体重建。不过,最后一节显示校正为透视图像会丢失过多的视野,这浪费了全景相机的优势,即大视野。
立体重建的第一步是立体校正,以便极线为水平线。这里,我们使用经纬校正来保留所有视场,或虽可用但没有推荐的透视校正。第二步是立体匹配以获取视差图。最后,可以从视差图生成 3D 点。
全景相机的立体重建的 API 为 omnidir::stereoReconstruct
。我们在此使用一个示例来说明其如何工作。
首先,如上所述校准一对立体相机并获取诸如 K1
、D1
、xi1
、K2
、D2
、xi2
、rvec
、tvec
的参数。然后分别从第一个和第二个相机读取两幅图像,例如 image1
和 image2
,如下所示。
其次,运行 omnidir::stereoReconstruct
,例如
在此处,变量 flag 表明了矫正类型,只有 RECTIFY_LONGLATI(推荐)和 RECTIFY_PERSPECTIVE 有意义。numDisparities 是最大视差值,SADWindowSize 是 cv::StereoSGBM 的窗口大小。pointType 是用于定义点云类型的标志,omnidir::XYZRGB 每个点都是一个六维向量,前三个元素是 xyz 坐标,后三个元素是 rgb 颜色信息。另一种类型 omnidir::XYZ 表示每个点都是三维的,并且只有 xyz 坐标。
此外,imageRec1 和 imagerec2 是第一幅和第二幅图像的校正版本。它们的极线具有相同的 y 坐标,以便立体匹配变得容易。以下是它们的示例
可以观察到它们对齐得很好。变量 disMap 是 cv::StereoSGBM 根据 imageRec1 和 imageRec2 计算出的视差图。以上两幅图像的视差图是
在获得视差后,我们可以为每个像素计算 3D 位置。点云存储在 pointCloud 中,这是一个 3 通道或 6 通道 cv::Mat。我们在下图中显示点云。