此文系第16届智能车智能视觉组-上海交通大学AuTop战队开源算法讲解第三部分--标定与透视变换,专栏及开源方案链接:

llo:第16届智能车智能视觉组-上海交通大学AuTop战队开源汇总


补充:有收到一些反馈说这部分看的不是很懂。建议看不懂的小伙伴先去了解一下相机成像模型坐标变换等基础知识。文章限于篇幅没法从最基础的地方开始介绍。


本次比赛我们使用了一个150°的广角镜头以获得更大的视野范围,然而广角镜头通常会有一定的畸变,所以需要对镜头进行标定和去畸变。

1. 相机标定

标定是对特定的相机成像模型中的参数进行拟合的过程。而常用的相机包括针孔相机模型(Pinhole Camera Model)、鱼眼相机模型(Omnidirectional Camera Model)。为了方便,我们使用MatLab对相机进行标定,而MatLab对上述两种相机模型都有支持。

为了对相机进行标定,最简单的方法是使用张氏标定法,即打印一张印有特定标定图案的标定纸,并将其摊平。使用待标定相机对其进行拍照,通常在不同角度采集10-20张图片即可得出较好的标定效果。注意标定图案需要在图像中占据足够的画幅,否则标定效果不佳。

标准棋盘格标定图案
使用待标定相机采集得到的图像

随后打开MatLab中的Camera Calibrator工具箱(我使用的是MatLab R2021a版本)并点击左上角的Add Images按键。

使用MatLab标定相机

选好自己拍摄的图片后,MatLab会提示你给定棋盘格的物理间距大小。这一步只会影响相机的外参(相机到标定纸的相对位姿)标定结果,不影响内参的标定结果。而我们只需要相机的内参,所以保持默认值即可。较新的MatLab版本还会让你选择图片的畸变大小,如果有这个选项,则根据实际情况选取即可。

给定棋盘格的物理间距

稍等一会儿,MatLab会自动识别图像中的棋盘格角点,并显示在界面上。这时首先浏览一下角点的识别结果,如果存在某些图片识别不准,则将这些图片删除。

随后在窗口上方选择相机模型,并点击开始标定。由于我们使用的150°广角镜头,所以采用fisheye模型。

<br> (二维码自动识别)

相机参数计算完成后,可以在窗口右侧看见每张图片的平均标定误差,通常当这个误差小于0.5可以认为是一次不错的标定结果。如果标定误差较大,则需要考虑更换相机模型,或者重新采集图片。

同时窗口上方可以查看图像去畸变之后的结果,一次好的标定在去畸变之后,实际是直线的地方,在图像中也应当呈现为直线。

去畸变结果

<br> (二维码自动识别)

上图展示了当相机模型选择错误时的情况。可以看出右上角的标定误差比之前要大,同时去畸变之后的图像中的直线仍有弯曲的情况,并不完全是一条直线。在上一步如果不知道自己的镜头应该使用哪种相机模型,也可以随便先选一个,标定完成之后查看去畸变的结果,如果去畸变后的图像不正常,说明模型选错了。

标定结束后,点击右上角的Export Camera Parameters即可将标定好的参数导出到Workspace中使用。

由于我们进行相机标定主要是为了去畸变,为了加快计算速度,我们直接将去畸变这一过程进行打表加速优化,即保存每个像素点去畸变之后的位置。使用undistortFisheyePoints这一MatLab函数即可完成这个功能,该函数的用法在这里就不多做介绍了。

2.1 透视变换基础

透视变换——严格来说应该叫做射影变换,由于不少人采用透视变换的称呼,所以我们也保留了这个称呼方式。

透视变换主要用于将一个平面从视角A变换的视角B。即从平面在视角A的图片中,生成一张“假”的图片,这张“假”图片中的平面正好和视角B下的图片相同。注意,这里提到了“一个平面”,这意味着不在同一个平面上的点是不满足这个变换的。使用透视变换,我们可以将相机斜向下得到的赛道图像变换成完全的俯视图。由于赛道正好在地平面上,满足同一平面的要求,所以可以使用透视变换完成。(这也同时也代表着该算法对于坡道地形无能为力)

一张去畸变之后的测试图像
对上方测试图像进行透视变换

熟悉OpenCV的小伙伴可能比较清楚,在OpenCV中,我们通常会使用getPerspectiveTransform函数来计算一个透视变换矩阵。该函数使用4对点来计算一个透视变换矩阵,即透视变换前的4个点和透视变换后的4个点。对于这种方法,我们通常会在小车的正前方地平面摆放一个矩形物体,通过将这个矩形物体在图像中的梯形4个顶点对应成矩形的4个顶点的方式来完成透视变换矩阵的计算。

小车前方水平面放置矩形标定纸,并去畸变

但实际上并不推荐这种做法。因为实际情况下很难保证矩形物体正正好好放在小车的正前方,但设定透视变换后的4个点时,却是直接假设矩形物体放置的位置没有任何偏移。这显然不是一个合理的做法,会产生一定程度的偏差。如果采用这种方法进行计算,后续车辆控制时,你可能会发现小车在赛道上前进时,总喜欢偏向一个方向,靠右(左)行驶。

为了从理论层面上保证透视变换矩阵的精度,而非靠人力保证矩形物体摆放正确,我们首先需要了解一下透视变换矩阵的来源。(以下内容需要一定程度的相机模型和坐标变换的知识)

参考文献:《视觉SLAM14讲》第七章 视觉里程计

2.2 PnP算法

为了测量当前相机坐标系和俯视情况下的相机坐标系之间的相对位姿,PnP算法是一种常于计算机视觉中测定坐标系之间变换关系的方法,OpenCV里有对应的函数实现solvePnP。由于PnP算法原理相对复杂,且算法种类繁多,在这里不多做介绍,有兴趣可以参考《视觉SLAM14讲》第七章的内容。

为了使用PnP算法,我们需要确定空间中至少3个点的空间坐标,以及它对应在相机图像上的成像位置,实际考虑测量误差,对应点数是越多越好的。相机图像上的成像点容易确定,可以使用角点检测算法,或者人工的方法确定。但空间坐标则需要首先建立坐标系。最简易的坐标轴建立方式是将坐标轴直接建立在标定纸上方,这样就是虚拟了一个在标定纸正上方俯视的相机坐标系。

在使用PnP算法计算得出当前相机坐标系和俯视相机坐标系的相对位姿关系后,由于之前提到过,很难通过人工保证标定纸的方式是正好在小车的正前方,所以我们需要对其进行矫正,使得俯视相机坐标系位于正前方。

已知PnP计算得出的旋转矩阵为

 [公式] ,该旋转矩阵可以分解为

 [公式] ,即俯视相机坐标系先绕x轴旋转变成斜视,再绕旋转后的坐标轴的z轴旋转(对应安装误差),最后绕一开始的z轴旋转(对应标定纸摆放倾斜)。在完成分解之后,只需令

 [公式] ,重新计算

 [公式] ,即可得到矫正后的相对旋转。

2.3 得出透视变换

注意到当坐标系只有旋转关系时,

 [公式] 。公式较为简单。我们计算透视变换时,可以首先忽略平移关系,得到

 [公式],然后手动平移缩放图像,使得变换后的图像比较符合需求。

仅通过旋转计算得出的透视变换
对上图进行手动平移和缩放

到此我们得到了一个合适的透视变换。

由于先前标定畸变时,采用打表的方式减少了单片机的计算量。对于透视变换,我们同样可以采用打表的方式。此外,也可以采用复合函数的思想,直接打表得到原图坐标到透视变换后的坐标(一次打表同时包含了去畸变和透视变换两个过程)。

3. 透视变换在边线提取中的应用

在前一篇文章(第16届智能车智能视觉组-上海交通大学AuTop战队开源算法讲解(二)边线提取)中,我们从原图中提取到了两条赛道边界。利用去畸变和透视变换,我们可以得到两条处于俯视视角下的赛道边线。

俯视视角下的赛道边线,以及赛道中线

上图是环岛的边线俯视图,部分位置有异常的凸起是路肩导致的。而中间的一条线则是由右边线计算得到的赛道中线。

下一篇文章我们将讲述在得到两条俯视的赛道边线后,如何对其进行妥善的处理,以用于小车的方向控制。


SJTU-AuTop完整开源方案链接,如果觉得我们的方案对您有帮助,请在github上帮忙点个star吧:)

llo:第16届智能车智能视觉组-上海交通大学AuTop战队开源汇总