本文所涉及所有资源均在传知代码平台可获取。
概述
方法
SparseBEV是一个基于查询的单阶段检测器,具有L个解码器层。SparseBEV首先使用图像主干和FPN结构逐帧处理输入的多摄像机视频。之后,在BEV空间中初始化一组稀疏支柱查询,并通过自适应自注意力进行聚合。这些查询通过自适应时空采样和自适应混合与图像特征交互,以进行3D目标检测。
查询范式
SparseBEV首先定义了一组可学习查询,其中每个查询都由其平移[x,y,z][x,y,z],维度[w,l,h][w,l,h],旋转θθ和速度[vx,vy][v x,v y]表示。查询被初始化为BEV空间中的支柱,其中z被设置为0,h被设置为~4m。初始速度[vx,vy]=0[v x,v y]=0,其他参数(x,y,w,l,θ)(x,y,w,l,θ)来自于随机高斯分布。遵循Sparse R-CNN,SparseBEV将D维查询特征附加到每个查询框,以对丰富的实例特征进行编码。
尺度自适应自我注意力机制
基于密集BEV的方法通常使用BEV编码器来对多尺度BEV特征进行编码,然而,由于SparseBEV没有明确的构建BEV特征,如何在BEV空间中聚合多尺度特征仍然是一个挑战。
在这项工作中,SparseBEV认为自注意可以发挥BEV编码器的作用,因为查询是在BEV空间中定义的。因此,SparseBEV提出了尺度自适应自注意(SASA),它在查询的指导下学习适当的感受域。
首先,SparseBEV计算了BEV空间中查询中心之间的所有对距离D∈RN×ND∈R N×N(N是查询的数量)
注意力机制不仅考虑查询特征之间的相似性,还考虑它们之间的距离,公式如下
其中Q,K,V∈RN×dQ,K,V∈R N×d是查询本身,d是通道维数,ττ是控制每个查询的感受野的标量。当τ=0τ=0的时候,它退化维具有全局感受野的原始自注意力机制,随着ττ的增长,远程查询的注意力权重变小,接受域变窄。
在实践中,感受野控制器ττ对每个查询都是自适应的,并且对每个头部都是特定的。假设由H个头,SparseBEV使用线性变换从给定的查询q∈Rdq∈R d生成头部特定τ1,τ2,...,τHτ1,τ2,...,τ H.
自适应时空采样
对于每一帧,SparseBEV使用线性层从查询特征自适应地生成一组采样偏移量{(Δxi,Δyi,Δzi)}{(Δx i,Δy i,Δz i)},这些偏移被转换为基于查询支柱的3D采样点。
与BEVFormer的可变形注意力相比,SparseBEV的采样点对查询柱和查询特征都具有自适应性,从而更好地覆盖不同大小的对象。此外,这些点不限于查询,因为SparseBEV不限制采样偏移的范围。
接下来,SparseBEV根据运动扭曲采样点来执行时间对齐。在自动驾驶中,有两种运动,一种是自我运动,一种是对象运动。自车运动描述汽车在环境中行驶时从自身角度的运动,而对象运动是指环境中其他物体在自动驾驶汽车周围移动时的运动。处理对象运动
在自动驾驶中,瞬时速度可以等于短时间窗口内的平均速度,因此,SparseBEV使用查询中的速度矢量,自适应地将采样点扭曲到以前的时间戳。
其中TtT t表示先前t时刻的帧(T0T0表示当前时刻的帧)
处理自车运动
接下来,我们SparseBEV数据集提供的自我姿态对采样点进行扭曲。首先将点变换到全局坐标系,然后变换到帧t的局部坐标系。
其中Et=[R∣t]E t=[R∣t]是帧t时候的自车位置
采样
对于每个时间戳,SparseBEV使用相机内参和外参将扭曲的采样点{(xt,i′,yt,i′,zt,i′)}{(x t,i′,y t,i′,z t,i′)}投影到每个视图上。由于相邻视图之间纯在重叠,投影点可能会碰到一个或多个视图,这些视图被称为v。对于每个命中视图K,SparseBEV有来自图像主干的多尺度特征图Fk,j∣j∈{1,2,...,Nfeat}F k,j∣j∈{1,2,...,N f e a t}。特征首先在图像平面中通过双线性插值B进行采样,然后在比例轴上加权。
其中NfeatN f e a t是多尺度特征图的数量。PkP k是视图k的投影函数,wijw i j是第j个特征图上的第i个点的权重,并且通过线性变换从查询特征中生成。
自适应混合
给定不同时间戳和位置的采样特征,关键是如何在查询的指导下对齐进行自适应解码。受AdaMixer的启发,SparseBEV通过动态卷积和MLP混合器来解码和聚合时空特征。假设总共有T个帧,每个帧有5个采样点,SparseBEV首先将它们进行堆叠生成P=T×SP=T×S个点,因此,采样特征被组织维f∈RP×Cf∈R P×C
通道混合
SparseBEV首先在f上执行通道混合,以增强目标的语义信息,根据特征查询q生成动态权重。
其中WcW c是动态权重,并且在不同的帧和不同采样点之间共享。
点混合
接下来,SparseBEV将特征进行转置,并且将动态权重应用于它的点维:
其中WpW p是动态权重,并且在不同的通道上进行共享。
在通道混合和点混合后,时空特征被线性层展平和聚合,最终的回归和分类预测分别由两个MLPs计算。
演示效果
bbox可视化
采样点可视化
核心逻辑
前向过程
def forward(self, mlvl_feats,img_metas):
# 类似于DAB-DETR,其中查询被显式表示为最终的结果,所以可以直接进行细化处理,采用同样的操作,所以属于静态
query_bbox = self.init_query_bbox.weight.clone() # [Q, 10]
#query_bbox[..., :3] = query_bbox[..., :3].sigmoid()
# query denoising
B = mlvl_feats[0].shape[0]
# BEV中都是在头部就使用了prepare_dn的形式,头部就完成了所有的事情,包括预测的结果,query其中是包含噪声的情况
query_bbox, query_feat, attn_mask, mask_dict = self.prepare_for_dn_input(B, query_bbox, self.label_enc, img_metas)
cls_scores, bbox_preds = self.transformer(
query_bbox,
query_feat,
mlvl_feats,
attn_mask=attn_mask,
img_metas=img_metas,
)
# 将边界框重新转换为现实坐标中
bbox_preds[..., 0] = bbox_preds[..., 0] * (self.pc_range[3] - self.pc_range[0]) + self.pc_range[0]
bbox_preds[..., 1] = bbox_preds[..., 1] * (self.pc_range[4] - self.pc_range[1]) + self.pc_range[1]
bbox_preds[..., 2] = bbox_preds[..., 2] * (self.pc_range[5] - self.pc_range[2]) + self.pc_range[2]
bbox_preds = torch.cat([
bbox_preds[..., 0:2],
bbox_preds[..., 3:5],
bbox_preds[..., 2:3],
bbox_preds[..., 5:10],
], dim=-1) # [cx, cy, w, l, cz, h, sin, cos, vx, vy]
# 如果采用了denoise的形式,这是一步重构策略比较重要,
if mask_dict is not None and mask_dict['pad_size'] > 0: # if using query denoising
# 此时获得的是denoise后的结果
output_known_cls_scores = cls_scores[:, :, :mask_dict['pad_size'], :]
output_known_bbox_preds = bbox_preds[:, :, :mask_dict['pad_size'], :]
# 此时是需要进行match的情况
output_cls_scores = cls_scores[:, :, mask_dict['pad_size']:, :]
output_bbox_preds = bbox_preds[:, :, mask_dict['pad_size']:, :]
# 此时是输出的结果,但输出的结果不一定需要完全正确,真值是没有output的形式
mask_dict['output_known_lbs_bboxes'] = (output_known_cls_scores, output_known_bbox_preds)
# 将其进行修改,其中all_cls_scores只保存match的query的形式
outs = {
'all_cls_scores': output_cls_scores,
'all_bbox_preds': output_bbox_preds,
'enc_cls_scores': None,
'enc_bbox_preds': None,
'dn_mask_dict': mask_dict,
}
else:
outs = {
'all_cls_scores': cls_scores,
'all_bbox_preds': bbox_preds,
'enc_cls_scores': None,
'enc_bbox_preds': None,
}
return outs
部署方式
# Install PyTorch 2.0 + CUDA 11.8
conda create -n sparsebev python=3.8
conda activate sparsebev
conda install pytorch==2.0.0 torchvision==0.15.0 pytorch-cuda=11.8 -c pytorch -c nvidia
# PyTorch 1.10.2 + CUDA 10.2 for older GPUs
conda create -n sparsebev python=3.8
conda activate sparsebev
conda install pytorch==1.10.2 torchvision==0.11.3 cudatoolkit=10.2 -c pytorch
# Install other dependencies:
pip install openmim
mim install mmcv-full==1.6.0
mim install mmdet==2.28.2
mim install mmsegmentation==0.30.0
mim install mmdet3d==1.0.0rc6
pip install setuptools==59.5.0
pip install numpy==1.23.5
# Install turbojpeg and pillow-simd to speed up data loading (optional but important):
sudo apt-get update
sudo apt-get install -y libturbojpeg
pip install pyturbojpeg
pip uninstall pillow
pip install pillow-simd==9.0.0.post1
pip install numpy==1.23.5
pip install fvcore
pip install einops
# Compile CUDA extensions:
cd models/csrc
python setup.py build_ext --inplace
准备数据集
1.从如下网站nuScenes数据集下载数据集,并且把放入 data/nuscenes文件夹下
-
从如下网址下载pkl文件Google Drive并且解压缩它们.
-
形成的文件夹如下图所示
data/nuscenes
├── maps
├── nuscenes_infos_test_sweep.pkl
├── nuscenes_infos_train_sweep.pkl
├── nuscenes_infos_train_mini_sweep.pkl
├── nuscenes_infos_val_sweep.pkl
├── nuscenes_infos_val_mini_sweep.pkl
├── samples
├── sweeps
├── v1.0-test
└── v1.0-trainval
参考文献
感觉不错,点击我,立即使用