03 感知:目标检测 Dense BEV feature
1. LSS 2020(First Depth Distribution)
核心:VoxelPooling (视锥点云特征 -> BEV特征)
将 frastum 投影到ego vehicle 坐标系,形成 geometry,借助此geometry,将feature放到 bev feature
'''
input: geom_feats (B, N, D, H, W, 3)
x (B, N, D, H, W, C)
output: bev features (B, C, X, Y)
'''
# 1. flatten x and geom
B, N, D, H, W, C = x.shape # B: 4 N: 6 D: 41 H: 8 W: 22 C: 64
Nprime = B*N*D*H*W # Nprime: 173184
# flatten x
x = x.reshape(Nprime, C) # 将图像展平,一共有 B*N*D*H*W 个点
# flatten indices
geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long() # 将[-50,50] [-10 10]的范围平移到[0,100] [0,20],计算栅格坐标并取整
geom_feats = geom_feats.view(Nprime, 3) # 将像素映射关系同样展平 geom_feats: B*N*D*H*W x 3 (173184 x 3)
batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,
device=x.device, dtype=torch.long) for ix in range(B)]) # 每个点对应于哪个batch
geom_feats = torch.cat((geom_feats, batch_ix), 1) # geom_feats: B*N*D*H*W x 4(173184 x 4), geom_feats[:,3]表示batch_id
# 2. boundary filter
# 过滤掉在边界线之外的点 x:0~199 y: 0~199 z: 0
kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
& (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
& (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
x = x[kept] # x: 168648 x 64
geom_feats = geom_feats[kept]
# 3. rank: get tensors from the same voxel next to each other
ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
+ geom_feats[:, 1] * (self.nx[2] * B)\
+ geom_feats[:, 2] * B\
+ geom_feats[:, 3] # 给每一个点一个rank值,rank相等的点在同一个batch,并且在在同一个格子里面
sorts = ranks.argsort()
x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts] # 按照rank排序,这样rank相近的点就在一起了
# x: 168648 x 64 geom_feats: 168648 x 4 ranks: 168648
# 4. cumsum trick: sum the features in same bev voxel
x = x.cumsum(0)
kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool)
#TODO kept[1:] = (ranks[1:0] != ranks[0:-1])
kept[:-1] = (ranks[1:] != ranks[:-1])
x, geom_feats = x[kept], geom_feats[kept]
x = torch.cat((x[:1], x[1:] - x[:-1]))
# 5. griddify
final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device) # final: 4 x 64 x 1 x 200 x 200
final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x # 将x按照栅格坐标放到final中
# collapse Z
final = final.squeeze(2)
return final
2. BEVDet 2022(BEV space data augmentation)
LSS segmentation head -> CenterPoint Head
3. BEVDet4D 2022(Spatial alignment; concatenation of multiple feature map) \(\star\)
1. t-1和t帧BEV特征融合为何需要做特征对齐?
- 直接拼接特征送给后面的模块,这时候目标在特征图上的偏移量包含有自车运动的成分,这个偏移量的分布会因为自车的运动而变得复杂。
(如何对齐,参考 BEVDepth temporal fusion)
4. BEVDepth 2022(Depth Correction) BEVDepth: Acquisition of Reliable Depth for Multi-view 3D Object Detection
BEVDepth在保持LSS框架的基础上,重点围绕如何获取更准确的深度估计进行优化,另外还提出了高效计算Voxel Pooling过程的方法,引入了多帧融合,这些工作使BEVDepth取得了不错的提升效果。
-
提高深度预测准确度
- 深度监督:将点云投影到图像上,然后转换成one hot编码,监督depthnet输出。
- 缓解相机内外参不准确的问题:在depthnet中堆叠3个Residual Blocks,降低特征尺寸来增大感受野,然后在它的后面再接上Deformable Conv(DCN)模块。受益于增大感受野,无法对齐的深度真值也能参与准确对齐正确位置上特征的计算。
- 深度预测和相机内参相关,因此将相机内参建模到depthnet中。
-
更高效的voxel pooling
为视锥的每个特征点分配一个CUDA thread,它的作用是把特征值加到相应的BEV网格中。主要是利用了GPU计算的并行化完成稠密特征点的池化过程,这样可以吧BEVDepth提速3倍。 -
temporal fusion (\(t-1, t\))
由于车辆运动,直接融合两帧BEV feture存在非对齐问题,借助前一帧车辆在世界坐标系下坐标和当前帧车辆在世界坐标系下坐标,可以将上一帧视锥点云对齐到当前帧,然后用对齐后的点云进行voxel pooling计算,对齐过程如下:
- 检测头 anchor based(PointPillar) -> center point head
5. BEVFusion MIT(Efficient pv -> bev transformation)
1. Voxel pooling 加速
将视锥点投影到BEV,会存在一个网格存在多个特征点问题,LSS使用求和同一个网格特征表示当前网格特征方法:先对所有特征点按bev网格索引排序;计算前项和(torch.cumsum),然后取出索引值和其后网格索引值不同网格的值;前向减操作,获得每个bev网格特征求和后的特征;将此特征根据bev索引放入BEV特征对应位置。
-
预计算
因为相机内参和外参矩阵固定,可以将图像特征点在BEV网格的索引预先保存(速度提升 2x)。 -
LSS前项和操作需要(tree reduction on the GPU),效率低(产生太多无用的部分和【我们只取索引变化边界前项和】)
每个BEV 网格分配一个GPU线程计算区域和,然后将此结果写回去。- Tree Reduction(树状规约):将数据分成多个块,每个线程处理一小块数据,然后逐层合并结果,最终得到全局前缀和。类似“分而治之”策略。

浙公网安备 33010602011771号