4. IPM逆透视变换:从地面视角看世界

各位同学,今天我们聊一个在BEV感知里非常基础、但又特别容易踩坑的话题——IPM逆透视变换。

我个人习惯把IPM叫做“上帝视角的开关”。你想想看,车载摄像头装在车顶或挡风玻璃上,拍到的画面是带有透视效果的——近处的路宽,远处的路窄,电线杆都是斜的。但做感知规划的时候,我们需要的是俯视图,是鸟瞰图,是BEV。怎么把透视图像变成俯视图?这就是IPM要干的事。

4.1 IPM原理:透视到俯视的数学魔法

IPM的全称是Inverse Perspective Mapping,逆透视映射。说白了,就是找到图像坐标系和世界坐标系之间的映射关系,然后把图像中的每个像素点“投影”到地面上。

这里有个关键假设——地面是平的。嗯,这个假设后面会坑很多人,我先提一嘴。

透视变换的数学表达其实不复杂。一个三维世界点(X, Y, Z)投影到图像平面(u, v),可以用一个3x4的投影矩阵P来描述:

s * [u, v, 1]^T = P * [X, Y, Z, 1]^T

其中s是尺度因子。IPM要做的是反过来——给定图像坐标(u, v),求它对应的地面坐标(X, Y, 0)。注意这里Z=0,因为我们假设所有点都在地面上。

我在项目中遇到过一个问题:一开始直接用OpenCV的getPerspectiveTransform函数,随便选了四个点就做变换。结果呢?近处的车道线倒是挺准,远处直接歪到隔壁车道去了。后来才意识到,IPM本质上是一个单应性变换,而单应性矩阵的求解质量,直接决定了IPM的效果。

4.2 单应性矩阵求解:四个点就够了?

单应性矩阵H是一个3x3的矩阵,有8个自由度(最后一个元素通常归一化为1)。理论上,4对匹配点就能解出H。但实际工程中,我建议至少用6-8对点,而且这些点要分布在整个视野范围内。

求解方法通常有两种:

  • 直接线性变换(DLT):构建线性方程组,用SVD分解求解。简单粗暴,但容易受噪声影响。
  • RANSAC + DLT:先随机采样,剔除外点,再用内点求解。我个人更推荐这种方法,尤其是标定板上有反光或者遮挡的时候。

来看一段我常用的求解代码:

import cv2
import numpy as np

def solve_homography(src_points, dst_points):
    """
    src_points: 图像中的点 (N, 2)
    dst_points: 地面上的点 (N, 2)
    """
    # 至少需要4个点
    assert len(src_points) >= 4
    
    # 使用RANSAC提高鲁棒性
    H, mask = cv2.findHomography(
        src_points, dst_points,
        method=cv2.RANSAC,
        ransacReprojThreshold=3.0
    )
    
    return H, mask

# 使用示例
img_pts = np.array([[100, 200], [300, 200], [100, 400], [300, 400]], dtype=np.float32)
ground_pts = np.array([[0, 0], [3.7, 0], [0, 10], [3.7, 10]], dtype=np.float32)

H, mask = solve_homography(img_pts, ground_pts)
print(f"单应性矩阵:\n{H}")

这里有个细节:地面坐标的单位要和实际物理单位一致。我见过有人用像素当单位,结果IPM出来的距离全是错的。建议用米,这样后续做目标检测、距离估计都方便。

我的小技巧:标定的时候,在路面上贴几个反光标记点,用RTK测出精确的GPS坐标,再转成以车为中心的局部坐标。这样标出来的H矩阵,精度比随便选车道线角点高一个数量级。

4.3 IPM的局限性:地面假设与远处畸变

好了,现在来说说IPM的坑。我踩过的坑,希望你们别再踩了。

4.3.1 地面假设:平坦世界的谎言

IPM的核心假设是“地面是平的”。但现实世界呢?有坡度、有减速带、有路拱。一旦地面不平,IPM出来的结果就会变形。

我曾经在一个项目中做车道线检测,IPM之后车道线在近处是直的,到了远处突然拐了个弯。排查了半天,发现是路面有个缓坡。IPM把坡上的点强行投影到平面上,自然就歪了。

怎么解决?

  • 局部IPM:只对近处区域(比如0-30米)做IPM,远处用其他方法。
  • 分段IPM:把地面分成多个平面区域,每个区域单独求解H矩阵。
  • 引入高程信息:如果有激光雷达或立体视觉,可以获取地面高程,做非平面IPM。

4.3.2 远处畸变:像素不够用

另一个问题是远处畸变。你想想看,透视图像中,近处一个像素可能对应地面上的1厘米,远处一个像素可能对应1米。IPM之后,远处的图像会被严重拉伸,分辨率极低。

我记得有一次做远距离目标检测,IPM之后远处的车变成了几个像素的模糊块,根本没法做识别。后来我做了个实验:

距离(米) IPM前像素密度(pix/m) IPM后像素密度(pix/m)
5 50 48
20 12 10
50 4 1.5
100 1 0.3

看到没?100米处IPM之后,每米只有0.3个像素。这数据基本没法用。

避坑指南:我曾经在项目里直接对整个图像做IPM,然后送到神经网络里做检测。结果近处检测精度还行,远处几乎全漏检。后来改成只对近处做IPM,远处用原始透视图像+距离自适应检测头,效果好了很多。

4.3.3 相机外参标定误差

IPM的精度还高度依赖相机外参(俯仰角、偏航角、高度)的标定精度。哪怕俯仰角偏差0.5度,IPM之后30米处的横向误差可能达到0.5米。

我建议的做法是:

  1. 先用棋盘格做内参标定
  2. 用车道线或路沿做外参在线标定
  3. 定期做自检,因为车辆载重变化、路面颠簸都会导致外参漂移

4.4 总结与工程建议

IPM是个好工具,但不是万能的。我个人总结了几条经验:

  • 近处用IPM,远处用其他:30米以内IPM效果不错,超过50米建议结合雷达或直接使用透视特征。
  • 多点标定,定期校准:单应性矩阵不是标一次就完事的,建议每次启动车辆时做快速自检。
  • 考虑地面不平:如果路况复杂,别死磕平面IPM,试试分段或带高程的IPM。

下一章我们会聊BEV感知中的特征提取与融合,到时候会用到IPM的输出作为输入。所以这一章的内容,建议你动手跑一遍代码,感受一下IPM的精度和局限性。

好,今天就到这里。有问题随时交流。