论文标题:BEVFormer: Learning Bird’s-Eye-View Representation from Multi-Camera Images via Spatiotemporal Transformers
论文链接:https://arxiv.org/abs/2203.17270
代码链接:https://github.com/fundamentalvision/BEVFormer
前言
目前存在的问题:1)流行的框架是基于深度信息的(LSS
)生成 BEV
特征的,这种范式对深度值或深度分布的准确性很敏感。因检测性能会受到符合误差的影响,BEV
特征不准确会严重影响最终的性能。2)现有的多相机目标检测方法很少利用时间信息,简单堆叠不同时刻的 BEV
特征会带来额外的计算代价和推理信息,效果并不理想。
基于以上两点,本文作者选用了 Transfomer
动态聚合有价值的特征,利用 BEV
特征循环传递过去到现在的时间信息。
网络架构
核心流程:
- 输入
4
个样本,包括1
个当前样本、3
个历史样本,经过backbone
、FPN
生成多尺度特征 F t − 3 F_{t-3} Ft−3 , F t − 2 F_{t-2} Ft−2, F t − 1 F_{t-1} Ft−1, F t F_{t} Ft - 将
F
t
−
3
F_{t-3}
Ft−3 ,
F
t
−
2
F_{t-2}
Ft−2,
F
t
−
1
F_{t-1}
Ft−1 依次输入
encoder
生成BEV
特征 B t − 3 B_{t-3} Bt−3, B t − 2 B_{t-2} Bt−2, B t − 1 B_{t-1} Bt−1 - 将
B
t
−
1
B_{t-1}
Bt−1 与
BEV Queries
输入Temporal Self-Attention
查询并融合时间信息 - 将
F
t
F_t
Ft 与融合时间信息后的
BEV Queries
输入Spatial Cross-Attention
查询并融合空间信息 - 循环 6 次最终输出当前
BEV
特征 B t B_t Bt
多尺度特征
BEVFormer
使用 resnet101
返回 stage1
, stage2
, stage3
阶段的特征,然后传入到 FPN
生成 4
种多尺度特征。
bevformer.py#extract_img_feat()
img = img.reshape(B * N, C, H, W) # torch.Size([6, 3, 928, 1600])
# resnet101返回stage1, stage2, stage3的feature maps
# 特征维度分别为512, 1024, 2048
# torch.Size([6, 512, 116, 200]), torch.Size([6, 1024, 58, 100])
# torch.Size([6, 2048, 29, 50])
img_feats = self.img_backbone(img)
# FPN返回4个feature maps,特征维度256
# torch.Size([6, 256, 116, 200]), torch.Size([6, 256, 58, 100])
# torch.Size([6, 256, 29, 50]), torch.Size([6, 256, 15, 25])
img_feats = self.img_neck(img_feats)
历史 BEV 特征
随机获取前 2
秒内的 3
个样本,经过 backbone
和 FPN
获取多尺度特征
F
t
−
3
F_{t-3}
Ft−3 、
F
t
−
2
F_{t-2}
Ft−2 、
F
t
−
1
F_{t-1}
Ft−1,然后,迭代生成历史 BEV
特征
B
t
−
3
B_{t-3}
Bt−3、
B
t
−
2
B_{t-2}
Bt−2、
B
t
−
1
B_{t-1}
Bt−1 。
生成历史 BEV
特征的逻辑与生成当前 BEV
特征的逻辑一致,区别在于不需要进行反向传播。
transformer.py#obtain_history_bev()
# torch.Size([1, 3, 6, 3, 928, 1600])
bs, len_queue, num_cams, C, H, W = imgs_queue.shape
imgs_queue = imgs_queue.reshape(bs*len_queue, num_cams, C, H, W)
# 获取多尺度特征(4个尺度,长宽分别为(116, 200)、(58, 100)、(29, 50)、(15, 25))
img_feats_list = self.extract_feat(img=imgs_queue, len_queue=len_queue)
# 迭代生成t-3,t-2,t-1时刻BEV特征
prev_bev = None
for i in range(len_queue):
img_metas = [each[i] for each in img_metas_list]
# 根据第二个维度len_queue拆分img_feats_list torch.Size([1, 6, 256, 116, 200])
img_feats = [each_scale[:, i] for each_scale in img_feats_list]
# pts_bbox_head 方法就是根据历史BEV和当前图片生成当前BEV
# 此处循环迭代,以此生成t-3,t-2,t-1时刻BEV特征
prev_bev = self.pts_bbox_head(img_feats, img_metas, prev_bev, only_bev=True)
BEV Queries
以车为中心,将 BEV
平面分成 (H
, W
) 个网格,每个网格对应真实世界中的某块区域。
bevformer_base.py
# 点云范围,102.4, 102.4, 8
point_cloud_range = [-51.2, -51.2, -5.0, 51.2, 51.2, 3.0]
bev_h_ = 200 # BEV平面高
bev_w_ = 200 # BEV平面宽
真实世界的 H
,W
都是102.4, BEV
平面 H
,W
都是200,则每个网格代表的真实世界的大小就是相应位置 0.512mx0.512m 的区域。
Q
∈
R
H
×
W
×
C
Q\in\mathbb{R}^{H\times W\times C}
Q∈RH×W×C 是网格状的可学习参数,每个
q
u
e
r
y
∈
R
1
×
C
query\in\mathbb{R}^{1\times C}
query∈R1×C 负责 BEV
平面中相应的网格单元。
bevformer_head.py
# 表示Q结构时,将H,W拉平处理 torch.Size([(40000, 256)])
bev_queries = nn.Parameter(bev_h * bev_w, embed_dims)
时间自注意力
时间自注意力和空间交叉注意力最终的实现都是基于 deformable attention
。时间自注意力以
B
t
−
1
B_{t-1}
Bt−1 和 BEV Quereis
作为入参,查询并融合时间信息。若
B
t
−
1
B_{t-1}
Bt−1 不存在则退化为自注意力。
以 BEV
平面网格的中心点作为时间自注意力中所需要的 reference points
。
encoder.py#get_reference_points()
ref_y, ref_x = torch.meshgrid(
torch.linspace(0.5, H - 0.5, H, dtype=dtype, device=device),
torch.linspace(0.5, W - 0.5, W, dtype=dtype, device=device)
) # torch.Size([200, 200])
ref_y = ref_y.reshape(-1)[None] / H # torch.Size([1, 40000])
ref_x = ref_x.reshape(-1)[None] / W # torch.Size([1, 40000])
ref_2d = torch.stack((ref_x, ref_y), -1) # torch.Size([1, 40000, 2])
根据 can_bus
里面的信息可以求出车的旋转角度和平移距离,移动
B
t
−
1
B_{t-1}
Bt−1 与
Q
Q
Q 对齐,记对齐后的历史 BEV
特征为
B
t
−
1
′
B_{t-1}^{'}
Bt−1′ 。与 deformable attention
不同,时间自注意力中的每个 query
会与 t
时刻的 BEV queries
和
B
t
−
1
B_{t-1}
Bt−1 交互,交互的结果求均值得到时间信息,再将时间信息融合到 BEV Queries
中。若
B
t
−
1
B_{t-1}
Bt−1 不存在,时间自注意力退化成自注意力。
value = torch.stack([query, query], 1) # 前一时刻BEV特征不存在,退化成自注意力
# value = torch.stack([prev_bev, query], 1) # 前一时刻BEV特征存在,时间自注意力
# 根据query获取 offsets 和 attention weights
query = torch.cat([value[:bs], query], -1) # torch.Size([1, 40000, 512])
sampling_offsets = self.sampling_offsets(query) # torch.Size([1, 40000, 8, 2, 1, 4, 2])
attention_weights = self.attention_weights(query).view(bs, num_query,
self.num_heads, self.num_bev_queue, self.num_levels * self.num_points)
sampling_locations = reference_points + sampling_offsets
output = MultiScaleDeformableAttnFunction.apply( # torch.Size([2, 40000, 256])
value, spatial_shapes, level_start_index, sampling_locations,
attention_weights, self.im2col_step)
# 与prev_bev和与query交互的结果求均值
output = output.mean(-1) # torch.Size([40000, 256, 1])
self.dropout(output) + identity
T S A ( Q p , { Q , B t − 1 ′ } ) = ∑ V ∈ { Q , B t − 1 ′ } D e f o r m A t t n ( Q p , p , V ) TSA(Q_p, \{Q, B_{t-1}^{'}\}) = \sum_{V\in\{Q, B_{t-1}^{'}\}}DeformAttn(Q_p, p, V) TSA(Qp,{Q,Bt−1′})=V∈{Q,Bt−1′}∑DeformAttn(Qp,p,V)
Q
p
Q_p
Qp :BEV平面位置
p
=
(
x
,
y
)
p = (x, y)
p=(x,y) 对应的 query
Q
Q
Q : BEV Queries
B
t
−
1
′
B_{t-1}^{'}
Bt−1′ : 与 BEV Queries
对齐后的前一刻 BEV
特征
空间交叉注意力
空间交叉注意力以融合时间信息的 BEV Queries
和
F
t
F_t
Ft 作为入参,查询并融合空间信息。与时间自注意力从 2D
平面选取 reference points
不同,空间交叉注意力需要从 3D
空间中选取 reference points
。
将 BEV
平面上的每个网格沿着 z
轴往上延伸,然后,在柱状体内采样获取 reference points
。实质上就是在以车为中心的 3D
空间分成一个个小立方体,取立方体的中心作为 reference points
。
encoder.py#get_reference_points()
# 以bevformer_base.py配置为例,H, W, Z 分别 102.4m, 102.4m, 8m
# 归一化处理,用于后续等比例放大
zs = torch.linspace(0.5, Z - 0.5, num_points_in_pillar, dtype=dtype,
device=device).view(-1, 1, 1).expand(num_points_in_pillar, H, W) / Z
xs = torch.linspace(0.5, W - 0.5, W, dtype=dtype,
device=device).view(1, 1, W).expand(num_points_in_pillar, H, W) / W
ys = torch.linspace(0.5, H - 0.5, H, dtype=dtype,
device=device).view(1, H, 1).expand(num_points_in_pillar, H, W) / H
# torch.Size([4, 200, 200, 3]) 4x200x200个立方体,每个立方体用(x,y,z)坐标表示
ref_3d = torch.stack((xs, ys, zs), -1)
reference points
是归一化后的逻辑位置,用它计算出真实世界中的位置,然后通过相机的 projection matrix
将真实世界中的位置点投影到相机图片上,最后计算出归一化的图片坐标。为了减少计算量,使用掩码将投射不到的图片区域掩码。
encoder.py#point_sampling()
# 从img_meta取出每张图片的lidar2img矩阵,即投影矩阵projection matrix
lidar2img = img_meta['lidar2img']
# 参考点中的是逻辑位置,以0.5开始,需要转换成真实世界中的位置, x,y,z 都需要,此处只列举了 x
reference_points[..., 0:1] = reference_points[..., 0:1] *
(pc_range[3] - pc_range[0]) + pc_range[0]
# 计算出图片坐标
reference_points_cam = torch.matmul(lidar2img, reference_points)
eps = 1e-5
bev_mask = (reference_points_cam[..., 2:3] > eps) # torch.Size([4, 1, 6, 40000, 1])
# 投影时得到的3*1维,此时最后一位不一定是1,一定要经过归一化,即除以z后(x, y)才能作为图像上的点
# torch.Size([4, 1, 6, 40000, 2])
reference_points_cam = reference_points_cam[..., 0:2] / torch.maximum(
reference_points_cam[..., 2:3], torch.ones_like(reference_points_cam[..., 2:3]) * eps)
reference_points_cam[..., 0] /= img_metas[0]['img_shape'][0][1] # 归一化,除以W
reference_points_cam[..., 1] /= img_metas[0]['img_shape'][0][0] # 归一化,除以H
# <=0 || >= 1 超出范围,加掩码 torch.Size([4, 1, 6, 40000, 1])
bev_mask = (bev_mask
& (reference_points_cam[..., 1:2] > 0.0) & (reference_points_cam[..., 1:2] < 1.0)
& (reference_points_cam[..., 0:1] < 1.0) & (reference_points_cam[..., 0:1] > 0.0))
根据归一化的图片坐标,可以确定每个 reference points
对应的
F
t
F_t
Ft 中的位置(与相应的 H
, W
相乘)。后续与 Deformable Attenton
类似,在
F
t
F_t
Ft 的相应位置附近采样,输出采样点的加权和(空间信息),在与 BEV Queries
融合。
S
C
A
(
Q
p
,
F
t
)
=
1
∣
V
h
i
t
∣
∑
i
∈
V
h
i
t
∑
j
=
1
N
r
e
f
D
e
f
o
r
m
A
t
t
n
(
Q
p
,
P
(
p
,
i
,
j
)
,
F
t
i
)
SCA(Q_p, F_t) = \frac{1}{|\mathcal{V_{hit}}|}\sum_{i\in \mathcal{V_{hit}}}\sum_{j=1}^{N_{ref}}DeformAttn(Q_p, \mathcal{P(p, i, j)}, F_t^i)
SCA(Qp,Ft)=∣Vhit∣1i∈Vhit∑j=1∑NrefDeformAttn(Qp,P(p,i,j),Fti)
Q
p
Q_p
Qp :BEV
平面位置
p
=
(
x
,
y
)
p = (x, y)
p=(x,y) 对应的 query
F
t
F_t
Ft : 时刻 t
的图片特征
V
h
i
t
\mathcal{V_hit}
Vhit :
p
=
(
x
,
y
)
p = (x, y)
p=(x,y) 沿着 z
轴采样的点投影到的相机图片
N
r
e
f
N_{ref}
Nref : 每个 query
的 reference points
数量
P
(
p
,
i
,
j
)
\mathcal{P(p, i, j)}
P(p,i,j) : 3D
点投影到 2D
点的投影函数
结论
BEVFormer
可以有效的聚合时空信息,生成强大的BEV特征,同时支持 3D
检测和地图语义分割任务。
缺陷:基于相机的方法与基于激光雷达的方法在效果和效率上仍存在很大的差距。从 2D
信息准确推断 3D
位置仍然是基于相机的方法面临的一个长期挑战。
影响:聚合时空信息可以显著提高视觉感知模型的性能。
参考资料
https://arxiv.org/abs/2203.17270
https://github.com/fundamentalvision/BEVFormer
https://zhuanlan.zhihu.com/p/543335939
https://www.bilibili.com/video/BV1PF411c78z
https://www.bilibili.com/video/BV1rK411o7PS