这篇论文提出的 GKT (Geometry-guided Kernel Transformer),一种利用粗几何投影把注意力限制在局部图像区域,然后通过 LUT 加速的 BEV 方法。

我认为 GKT 是在 BEV 范式下,针对图像分支的 LSS 深度估计方法做出的一项工程加速优化方案。

2D BEV方法

GTK-Transformation.webp

几何点对点投影

代表方法包括 LSS,BEVDet 。

利用相机的标定参数(内参外参)来确定 2D 位置和 BEV 网格之间的对应关系(Lift)。

然后将 2D 特征投影(Splat)到 3D 空间并形成 BEV 表征。

这类方法优点是几何关系明确,但是逐像素变换需要每帧都进行一次 2D 到 3D 的映射计算。

无几何全局变换

代表方法 CVT (Cross-view Transformers for real-time Map-view Semantic Segmentation),基于 Transformer 架构。

将多视图的图像特征 Flatten,每个 BEV 网格会与所有图像像素进行交互,这个过程不依赖 2D 到 BEV 投影的几何先验,因此对外参不敏感。

但是这也带来了问题,计算成本高;没有几何先验,那模型必须从所有视图中挖掘信息,收敛慢。

GKT

下图是 GKT 的框架。

GTK-Pipeline.webp

建立 BEV Query

环视相机的多视角图像经过共享的 CNN Backbone 之后得到多视图特征 ​F_{v}^s

BEV 空间被均匀的划分为网格,每个 BEV 网格对应一个坐标 ​P_i = (x_i,y_i,z) 和可学习的查询嵌入 ​q_i 。其中 ​z 是所有 Query 共享的 BEV 平面的预定义高度,​q_i 是从多视角特征中去查询的 Query 向量。

GTK 对 ​z 不敏感,因此没有显式地设置多个 Z 层,而是直接选定了一个参考平面。

粗投影

对每个 BEV 网格,使用相机内外参计算:

Q_i^{sv}=K^{sv}Rt^{sv}P_i

其中:

  • s:特征尺度
  • v:相机视角
  • Q_i^{sv}:BEV 网格在某相机、某尺度上的投影位置

随后把浮点位置取整得到像素坐标:

\bar Q_i^{sv}=\operatorname{round}(Q_i^{sv})

这里的投影只需要大致正确,不要求像点对点采样那样精确。

提取投影点周围 Kernel

这里的 Kernel 指的不是普通卷积核,而是每个 BEV Query 可以关注到的局部图像区域。

假设投影中心在 ​(u,v),GTK 会在该位置展开一个局部窗口。

一个 BEV Query 获得的局部特征形状类似:

(N_{view},N_{scale},C,K_h,K_w)

同时有多视角 + 多尺度 + 投影中心 ​(u,v) 附近的多个位置

局部注意力生成 BEV 特征

每个 BEV Query 只与自己的局部 Kernel 特征交互:

F_{\mathrm{BEV},i} = \operatorname{Attention} \left( q_i,\, F_{\mathrm{local},i} \right)

与全局 Transformer 相比,全局方法一个 BEV Query 需要关注所有图像位置,而 GKT 只关注几何投影附近的位置。这样几何先验带来的搜索范围更小。

此外,点对点取特征可能因为相机震动和标定误差导致取错特征,如果使用局部窗口,正确目标仍落在窗口里,比精确投影有更强的标定误差容忍度。

BEV-to-2D LUT

GKT 中还使用了 LUT,保存的是 BEV Query 索引 —— 每个相机每个尺度图像特征图中局部采样点的索引。

简单俩说,就是:某个 BEV Query,对应哪个相机的哪个特征图的哪个位置

要注意的是,索引最终都被展平为 1D 。

假设初始 BEV 网格大小为 ​(H_b\times W_b),BEV 位置 ​(y_b,x_b) 被展平为:

bev_{idx}=y_bW_b+x_b

论文 BEV Query 是 25 × 25,因此共有 625 个 ​bev_{idx}

对每个 ​bev_{idx},LUT 保存其在每一路相机、每个尺度的特征图上的对应位置:

(v,s,y_f+\Delta y,x_f+\Delta x)

其中:

  • ​v:相机
  • ​s:特征尺度
  • ​y_f,x_f:该 BEV 三维点投影到该尺度特征图后的中心位置
  • ​\Delta y,\Delta x:局部 kernel 偏移

把所有特征图空间展平成 1D 后,LUT 中存一个整数就行,跟 CUDA Kernel 的 idx 计算很相似:

\text{src}_\text{idx} = \text{scale}_{offset_s}+ (\text v \text H_\text s+\text y)\text W_\text s+\text x

最终 LUT 存储的就是:

\text {LUT} [\text{bev}_\text{idx},\text v,\text s,\text k] = \text{src}_ \text {idx}

其中 ​k 为局部 Kernel 中的第 ​k 个位置。