来源:https://markus-enzweiler.de/downloads/publications/ECCV2022-spatial_detr.pdf
代码:GitHub - cgtuebingen/SpatialDETR: Official implementation of SpatialDETR. The paper will be presented at ECCV 2022
这篇文章继承了DETR3D的思路,通过多视角的图像实现3D目标的检测。
1. 动机&创新点
1.1 动机
- 【为什么使用纯视觉?】 尽管雷达刻画3D物体比较精确,但camera的方法具有高帧率以及价格低廉
- 【DETR3D存在啥问题?】DETR3D 仅利用了3D目标中心点在多视角图像中像素点的特征,忽略了大目标在image patch上的信息
1.2 创新点
- geometric positional embedding. 显式利用空间几何结构信息。
- cross-sensor global attention.能够利用query刻画不同view中image patch构成的key之间的相关性。
2. 方法
2.1 框架
整个框架如下图所示,不同的视角图像经过CNN的backbone拿到对应的feature map, 这些feature map与相机的内外参结合经过 几何位置编码(Geometric Positional Encoding)生成用于cross-attention的特征,而目标检测过程则遵循detr的框架,输入是N个可学习的queries,经过多层decoder layer,最终预测3D目标。每一层decoder layer,包含 self-attention,cross-attention,和FFN模块,每个模块都跟着LN层。3D框表示时包含(x,y,z, w, h, l, ).回归则是在sigmoid空间内进行,目的是为了数值稳定。此处见标准的DeformableDETR的结构。
2.2 Geometric Positional Encoding
传统的直接使用sin-cos编码的方式,或者可学习变量的位置编码方式,首先没有显式的刻画相机的外参,其次,因为3D-2D的射影变换使得grid结构不符合图像中像素的真实关系,因此将不同view的图像相对于同一个参考平面进行位置编码能够保证对不同外参的相机车辆拖影的鲁棒性。
本文方法的具体做法是,给定的相机C,必然已经知道其内参,那么直接计算该相机成像平面上每个像素的单位方向向量, 这样本质上是不考虑成像目标的depth信息,而只关心射线方向的相似度。然后将单位向量经过FC层 dir2latent进行编码加到featuremap上。即, k 对应每个camera image经过backbone生成的feature map,用于cross attention时作为keys。
2.3 Spatially-Aware Attention
为了计算query和key之间的相似度,query和key应该在相同的空间内,而key是在2D 图像空间内, query在3D空间内,因此需要将query投影到不同的camera上进行相似度计算。
第一个式子是先由query转成预测的3D box,然后拿到3D box的中心点,第二个式子是通过外参矩阵,由该中心点映射到对应camera上,第三个式子相当于对对应的2D点进行geometric position编码。这里把q和k理解维context embedding更好理解,映射到相同空间上进行几何位置编码,然后计算相似度。
关于query的更新,作者认为每个view的value包含的对应相机的外参信息,多个view的value其实对应3D空间的相同目标,因此有必要对value进行外参的解耦,于是
,
第一个式子是为了从2d表观上拿到深度信息,第二个式子是拿到对应目标在该相机下的坐标,第三个式子是转到世界坐标系下,第四个式子是由2d 特征加上世界坐标系的编码获得目标在3d空间的特征,用于更新query。
因为这里有用到深度信息,所以本质上是可以引入depth监督。
# xs and ys do include "invalid" values at idxs that correspond to padded pixels
xs, ys = torch.meshgrid(
torch.arange(
0, feats_shape[1], device=mask.device, requires_grad=False),
torch.arange(
0, feats_shape[0], device=mask.device, requires_grad=False),
)
xs = xs.float()
ys = ys.float()
# 3 x width x height
cam_embeddings = torch.cat(
[
xs.unsqueeze(dim=0),
ys.unsqueeze(dim=0),
torch.zeros((1, xs.shape[0], xs.shape[1]),
device=xs.device, requires_grad=False),
],
axis=0,
)
for cam_idx in range(len(img_shape)):
for s_id in range(BS):
# TODO refactor as initial check / caching
# allow for different scales via scale_mat @ K
full_img_shape = img_shape[cam_idx]
feature_scale_x = W / full_img_shape[1]
feature_scale_y = H / full_img_shape[0]
if not np.allclose(feature_scale_x, feature_scale_y):
# the feature scale was not the same (due to uneven division)
# padding fixes this -> one side is too long -> too high scale
# to fix we use the smaller feature scale
warnings.warn(
"x/y feature scale diff, double check padding...")
feature_scale = min(feature_scale_x, feature_scale_y)
else:
feature_scale = feature_scale_x
K = img_metas[s_id]["cam_intrinsic"][cam_idx]
# TODO refactor: assumes fx == fy
# scaling can be accounted for by simply scaling focal length and principal point
scale_factor = img_metas[s_id]["scale_factor"]
scale_factor = scale_factor * feature_scale
cx = K[0][2]
cy = K[1][2]
focal_length = K[0][0]
# cx
cam_embeddings[cam_idx, s_id, :, :, 0] -= cx * scale_factor
# cy
cam_embeddings[cam_idx, s_id, :, :, 1] -= cy * scale_factor
# focal length
cam_embeddings[cam_idx, s_id, :, :,
2] = focal_length * scale_factor
3. Experimets
3.1 消融实验
- Table 4. 从这个表中可以看出 将query映射到sensor-relative空间作用最明显, 而将value映射到global空间作用并不明显,而C(Q)是想拿计算量换指标,也没啥效果。这里P(V)没啥效果,个人认为是可以理解的,正如我们前面介绍,k和q都可以当作是context embedding,而key进行了几何位置编码,因此query的编码方式必须要映射到相同空间,显然有效。而从image feature map上取的value,本质上可以认为是和key一样的context embedding,和query本身就是在相同空间内, 是在对query更新时额外因为了3d 空间的位置编码某种程度是不合理的
-
Table 5. 首先相同的decoder结构,900个query数量可能已经饱和,再增加指标不会改善多少,其次相同的query数, layer数少或多都不好,6个比较适合。最后,decoder layer的最后几层共享参数,类似RNN那种指标有进一步改善。
3.2 和已有实验的对比
具体内容见原论文,相对于DETR3d,BEVDET这些方法,即使不使用multi-scale test的策略,其检测指标mAP也是有优势的。但该方法经过多次投影和反投影,感觉时间开销较大。
4. 总结
- 本文提出的几何位置编码,对于非ipm-BEV方式的transformer 方法感觉是合理且有效的;大目标能涨点
- cross-attention内对query更新时value 叠加上对应3d点的位置编码感觉解释的不是很合理,且作用不大;
- 本文方法结构还是比较复杂的,投影和反投影的过程耗时较大。
- decoder的后几层layer采用RNN方式共享参数可以提点,这点可以验证下使用。