BEV_Occ LSS推理加速与部署
BEV方案中非常关键的问题是将图像成像平面里的信息转换到BEV空间,这部分内容也成了BEV方案区别于其它3D方案的核心。模型的输入是图像,这是3D物体信息通过针孔原理在平面上的投影,所以叫2D图像空间;图像信息经过特征提取后,需要再将信息恢复到原来的空间,这个过程就是图像特征到3D特征的视角转换。实际上不管是传统方案还是BEV方案这都必须要做的事情,区别在于前者更倾向后处理上做,而后者直接在模型里做。
介绍BEV的博客文章中,会重点介绍2D to 3D的视角转换(有兴趣的可以去学习,包括一些综述),它们通常会将视角转换分为两大类——LSS方式和transformer方式。基于LSS的方式通过额外的深度估计/假设,利用深度信息将图像特征映射回3D空间;而基于transformer的方式通常是反向操作,先在3D空间定义特征表现形式(query),通过映射关系再去图像空间提取特征。这样划分只是习惯。
事实上这种视角转换方式在Occ上是相同的,也可以划分为LSS方式和transformer方式,唯一区别是在Occ里要转换为体素特征,而BEV空间可以理解为柱状特征(高度维不体现)。
本文主要介绍LSS方式下,视角转换的推理加速,以及部署问题。
附赠自动驾驶最全的学习资料和量产经验:链接
1 LSS特征视角转换原理
图1 LSS特征视角转换原理
-
首先图像经过特征提取网络获得图像平面特征,同时通过深度网络估计每个像素的深度信息(这里的像素是下采样了,一个像素格子对应原始一片像素);这里深度是预定义的D通道,比如60通道可以代表预定义深度1m-60m每隔1m采样一个点,每个通道的数字代表该点的概率;
-
然后,针对每个像素,将该像素的特征分布到60个采样点上(深度概率作为权重);
-
之后,所有采样点经过内参和外参转换,映射到3D空间的体素格子里,每个体素如果有多个采样点,对应特征则做sumpooling或者maxpooling,如果是BEV空间,则需要拍平高度维(sumpooing或者其它降维方法)。
2 LSS原版推理方式
下面按照函数讲解lss原版推理流程
2.1 生成图像空间视锥
每个像素沿深度方向均匀采样D个点,生成维度DxHxWx3视锥点,D为深度维,H和W为特征图长宽,最后一维3代表坐标(u,v,d)。该步在初始化阶段完成。
2.2 映射到自车坐标系
-
将一个相机的视锥扩展为BxNxDxHxWx3,新增的B为batch维,N为相机个数。
-
将视锥点通过内参矩阵K从图像空间转换为相机空间
-
再通过外参旋转矩阵R和平移t,从相机空间再转到自车空间,输出维度不变,依旧为BxNxDxHxWx3
如果涉及图像增加,则需要额外的旋转和平移转换
2.3 提取图像特征和深度
图像BxNx3xHxW张量,经过特征提取网络得到BxNxCxHxW特征张量,通过深度网络得到BxNxDxHxW深度张量,将特征张量和深度张量做叉乘,得到BxNxDxHxWxC张量。
2.4 voxel_pooling
-
将图像特征张量BxNxDxHxWxC平铺成B*N*D*H*WxC的二维张量x;
-
将视锥点根据点坐标转换到预定义的BEV空间格子,同样平铺成二维张量B*N*D*H*Wx3的二维张量;另外新增一个batch_idx,变为B*N*D*H*Wx4的张量geom;
-
根据预定义的BEV区域,过滤区域外的采样点geom,特征x相应位置做同样操作;
-
通过geom生成长度相同的rank向量,数值代表该采样点落在BEV第几个格子,数值相同表示落在相同格子;
-
对rank进行重新排序,使得落在同一个格子的采样点相邻 ;排列变化同样操作在geom和x张量上。
图2 voxel pooling
2.5 cumsum trick
由于rank经过排序,如果一个数值连续出现,则代表的是相同BEV格子的采样点;所以理论上只要对数值相同的区域的特征进行求和,即可得到该BEV格子的特征。
实际做了些trick:1)对重新排列的特征x进行累积求和x_sum;2)找到rank中数值变化的位置;3)x_sum数值变化的位置减去上个位置即得到该段特征和。
图3 cumsum trick
3 BEV pooling
3.1 BEV pooling
来源:BEVFusion
如上所述,LSS方式产生的视锥点维度为BxNxDxHxWx3,带入具体参数可能会得到百万级的伪点云数量,比lidar点云数量都大两个数量级,所以是非常耗算力和内存的任务。
BEV pooling在LSS基础上,通过两个优化点加速视角转换的推理。
第一个是预计算。一般情况下,相机在标定之后,内参和外参都是大致不变的(不考虑相机在线标定对外参细微的调整),所以相机工作时图像视锥点到BEV空间的映射关系也是固定的。这样生成自车坐标系的伪点云、投影BEV的关系rank,以及重新排序这些都是可以预先计算好,不在推理中实时计算。
第二个是减少计算间隔。如下图所示,LSS现有的实现首先计算所有点的前缀和,然后减去索引变化的边界处的值。然而,前缀和操作需要在GPU上进行树简化,并产生许多未使用的部分和(因为我们只需要边界上的这些值),这都会导致低效。为了加速特征聚合,BEV pooling实现了一个专门的GPU内核,它直接在BEV网格上并行化:为每个网格分配一个GPU线程,计算其间隔和并将结果写回来。该内核消除了输出之间的依赖关系(因此不需要多级树缩减),并避免将部分和写入DRAM。
图4 BEV pooling 的优化
3.2 BEV pooling V2
来源:BEVPoolv2
论文链接:https://arxiv.org/pdf/2211.17111v1
如前面所述,图像经过特征提取网络得到BxNxCxHxW特征张量,通过深度网络得到BxNxDxHxW深度张量,将特征张量和深度张量做叉乘得到BxNxDxHxWxC视锥体特征。由于这个视锥体特征的尺寸太大,其计算和存储都是非常耗资源的。BEV pooling v2优化点便是解决这个问题,对比如下图所示。
图5 BEV pooling v2
BEV pooling v2不再直接将特征张量和深度张量进行叉乘,而是在原来的视锥体素索引rank的基础上,新增特征张量的索引rank_feat和深度张量的索引rank_depth。通过对rank预处理操作,将落在同一个BEV网格的点相邻排列,同时作用到rank_feat和rank_depth上。最后在计算每个BEV网格特征时,只需要通过rank_feat索引从特征张量中拿对应的特征信息,通过rank_depth索引从深度张量中拿深度信息,然后融合。从而避免直接计算和存储BxNxDxHxWxC的视锥体特征。
另外与BEV_Pooling一样的,BEV pooling v2也是预计算视锥点和映射关系,以及对rank、rank_feat和rank_depth的预处理部分。
4 深度均匀分布
参考:FastBEV
论文地址:https://arxiv.org/pdf/2301.12511
基于深度的特征视角转换研究中,有一些研究不对像素进行深度估计,而是假设每个像素在各种深度下概率是相等的,即深度均匀分布。这样做的明显好处,一个是省了深度估计,二是不用再计算每个像素的特征在深度上的分配。而FastBEV在深度均匀分布的假设下,将推理加速做了更好的优化。
借鉴BEV Pooling的思路,在相机标定后,图像特征到BEV空间体素的映射是固定的,所以FastBEV也采用预计算的方式,并且构建出了一个LUT查询表。其构建LUT查询表的算法如下:
图6 LUT查询表构建
LUT查询表中,每个有效的BEV体素都能对应一个像素。在使用时,得到像素特征后,每个BEV体素的特征直接通过查表去拿对应的像素特征,所以速度是极快的。如下图所示。
图7 通过查询表将像素特征转换为体素特征。
然后通过S2C算子将体素特征XxYxZxC转换为无高度的BEV特征XxYx(ZC)。
当然,由于没有做深度估计,粗糙的深度表示会对精度有影响。但是通过上述方法极大地提高了转换效率,从而更强的特征提取方式(比如多尺度方法)可以应用。综合下来,FastBEV证明深度均匀分布导致精度损失很小。