0. 简述
本次给大家讲解一篇非常经典的融合工作叫 BEVFusion,我们依旧从算法动机&开创性思路、主体结构、损失函数以及性能对比四个方面展开
BEVFusion 有两篇文章,本次主要讲解的是阿里和北大的:BEVFusion: A Simple and Robust LiDAR-Camera Fusion Framework
另外一篇是 MIT 的工作:BEVFusion: Multi-Task Multi-Sensor Fusion with Unified Bird’s-Eye View Representation
1. 算法动机&开创性思路
我们先看 BEVFusion 这个名字能想到什么呢?很显然,我们将它拆开来看,一个是 BEV,另一个是 Fusion
那 BEV 是什么呢?是一个俯视空间,鸟瞰图,我们称之为上帝视角
那什么叫 Fusion 呢?翻译过来叫融合,那既然是融合是什么和什么的融合呢?通常情况下我们讲的融合是跨模态的融合,比如图像和语音信号的融合,图像和毫米波雷达的融合,图像和激光雷达点云的融合。
BEVFusion 属于哪种如何呢?从下面的框图中我们也能够看出来它属于图像和点云的融合
融合的输入知道了,那是怎么做融合的呢?那也就是说我们怎么把图像和点云融合在一起的呢?
作者认为融合方法可以分为三种,如下图所示:
图2 LiDAR-Camera融合方法对比
第一种叫做 Point-lever Fusion 也就是点级的融合,Point-level 中的 Point 指的是点云中的点,那也就是说 Point-lever 的策略是从点出发,从点云中采样出一些点,然后我们根据相机的内参和外参矩阵将采样的点投影到图像上采样出图像特征,然后再拼接到点云上。我们在 (a) 中能看到橙色 ■ 部分和蓝色 ■ 部分是对应的点特征和图像特征,我们会利用融合后的特征去做 3D 检测,这就是点级的融合。
第二种方式叫做 Feature-level Fusion 也就是特征级的融合,它会将多模态的中间特征通过内外参矩阵拼接投影融合出完整的特征,特征之间传递的是什么呢?是 Query,按照 Query 的方式,比如输入点云通过一个点云网络得到初始的位置,初始位置去图像上采样特征,是一个点云去图像的过程,采样完特征之后我们再拿回到初始的点云空间中拼接到原始的特征上,在 (b) 中蓝色的小方块 ■ 是我们从图像中采样过来的特征,而橙色的小方块 ■ 是我们原始的初始的点云特征,两种类型的特征拼接在一起我们去进行 3D 检测任务。
那看到这里,我们思考下 Point-level 和 Feature-level 的方法存在什么问题呢?
我们从融合的流程上来看两种方案其实都离不开一个映射的过程,也就是说我们利用内参和外参矩阵需要将 3D 的点换算到 2D 空间上。我们讲过内参我们认为相机出厂之后就恒定了,是一个比较稳定的量,而相机外参是衡量相机和激光雷达间相对位置关系的,它可能由于初始的校准或者车辆运行过程中的颠簸抖动造成一定的偏差。外参如果产生偏差,从点云去往图像投影时会很直接的产生一个投影偏差,那这是第一点,即投影过程中可能产生一定的偏差。
那第二点,我们即使很准确的能够看到投影点,但我们发现相机在这个点的成像效果可能并不好,比如我们点云投影到图像上之后,采样处的位置可能没有图像特征,比如像镜头脏引起的遮挡,还有像某些相机损坏可能导致的卡帧等等,我们的点云投影在图像上很难找到一个对应的特征或者说我们找到了不太好的甚至错误的特征,这是第二点,即采样的图像特征的质量并不是特别高
那第三点,从源头上来看,Point-level 和 Feature-level 方法全都是从点云出发的,将点云投影到图像上,假设我们的初始位置就已经丢失了,也就是点云信息没有了,那我们能难找到它在图像上所能对应的投影位置。
所以像 BEVFusion 的作者认为以前的融合方式,无论是 Point-level 还是 Feature-level,它们之间存在一个主次依赖的关系-从点云出发,以点云为基础。那点云如果不准,外参如果不准,后续的检测自然也就不准了
所以 BEVFusion 其实是想能够尽可能的降低这种主次依赖关系,对点云和图像分别去进行处理,然后在 BEV 空间做融合。那所以我们在 © 中可以看到输入是图像和点云,它通过两条并行的网络去做处理,然后通过一个融合模块去做融合,它们之间是没有什么主次依赖关系的,哪个好我就用哪个,那比如在某个点位可能点云信息比较好,那我们就用点云信息,比如另外一个点位可能图像信息比较好,那我们就用图像信息。
另外我们介绍的这篇 BEVFusion 还有一个特点,除了我们一个融合特征的检测头之外,它在每一个模态信息下面都额外接了一个检测头,那像作者的意思是说我们可能融合之后的效果也不是很好,或者说我单一的依靠图像或者单一的依靠点云也不好,但它们是可以融合工作也可以分开独立工作,所以尽可能地避免在一些偏极端情况下产生的一些影响
OK!我们在这里简单总结下,BEVFusion 的动机还是回到了标题,它是一种融合的思路,融合的是什么呢,融合的是点云和图像,怎么做融合呢,在 BEV 空间做的融合,它们的融合有主次依赖关系吗,很弱的一种依赖关系
OK,以上就是 BEVFusion 算法的动机,下面我们重点来看看 BEVFusion 的主体结构,他们是怎么设计这个网络的
2. 主体结构
我们还是老套路看网络先看其输入和输出部分,输入是包含多模态的,一个是多视角的图像输入,还有一个是 Point Clouds 点云输入,输出是 final detection result 即最终的 3D 检测结果。
输入图像通过 Camera Stream 图像流来处理,通过图像编码器 Encoder 可以得到 Multi-view Features 多视角的图像特征,那图像特征怎么转换到 BEV 空间呢?那其实我们在之前也讲过是一个 2D➡3D 的转换器,图像特征会从 2D 映射到 3D,再从 3D 投影到 BEV 空间得到所谓的 Camera BEV Features 即图像特征在 BEV 空间的一种表征。
输入点云通过 LiDAR Stream 点云流来处理,通过 3D Backbone 我们可以得到点云的 BEV 特征,那这里的 3D Backbone 其实可以有很多,包括基于点的方式、基于体素的方式等等,这里并不局限。我们得到点云特征后,将其拍扁到 BEV 上,自然就得到了点云的 BEV 特征。
OK!通过上面图像流和点云流之后,我们有了图像的 BEV 特征,有了点云的 BEV 特征,那接下来我们就需要做融合,所以作者引入了一个额外的 Fusion Module 融合模块,融合模块的输入有两个,一个是图像的 BEV Feature,另一个是纯点云的 BEV Feature,通过这个融合模块我们可以得到点云和图像的混合特征,利用混合特征去做预测。
此外,作者为了实现单一模态也可以做检测的能力,额外加了两个 Detection Head 检测头,图中也列了出来,一个是 Camera Detection Result,利用图像 BEV Feature 可以得到的图像的检测结果,另一个是 LiDAR Detection Result,利用点云 BEV Feature 可以得到的点云的检测结果
那以上就是 BEVFusion 的完整流程了,其实比较简单,两个支路融合然后再检测,那我们接下来具体分开看下每个支路是怎么做的,融合检测又是怎么做的,我们先从图像支路开始
2.1 相机支路
Camera Stream 设计流程如下:
输入:多视角图像
步骤 1:2D Backbone 提取基础图像特征
步骤 2:FPN+ADP,多尺度特征融合
步骤 3:2D➡3D 特征转换模块
步骤 4:3D➡BEV 特征编码模块
输出:Camera BEV Features
图像支路的输入是多视角图像,输出是图像的 BEV 特征,中间会经过很多的模块,包括 Encoder、视角转换等等。我们先来看 Encoder 部分,它包含两个部分,一个是 Backbone 也就是骨干网络,比如想 ResNet 这种,然后通过 FPN(Feature Pyramid Network) 特征金字塔去做一个多尺度的融合,多尺度融合无论是 2D 还是 3D 其实都是一种通用的套路。
图3 FPN+ADP模块
那不同尺度的特征通过 ADP 模块去做融合,为什么需要 ADP 模块呢?那其实主要原因还是我们的多尺度特征尺寸是不一致的,它没有办法通过级联或者相加的操作直接合在一起,所以 ADP 实现的一个重要功能就是通过上采样的操作将不同尺度的特征的尺寸变得一致了,也就是说F2 F3 F4 F5 开始是有大有小,经过 ADP 之后就是一样大了,特征一样大之后我们后续无论是级联还是相加都是可以做的。
FPN+ADP 设计流程如下:
输入:基础图像特征
步骤 1:每层特征使用 ADP 模块
步骤 2:ADP 模块包括上采样、池化、卷积
步骤 3:多层特征融合
输出:多尺度融合特征
所以图像编码部分的工作输入是多视角图像,经过 2D Backbone 得到图像特征,然后通过 FPN 得到多尺度的特征,接着通过 ADP 模块,那 ADP 其中包括一些上采样,平均池化,还有卷积,通过 ADP 模块之后我们做多尺度的特征融合,最终得到一个融合后的图像特征,那这个融合后的图像特征其实是包含了多尺度信息的。
那通过 Encode 得到图像特征后,怎么通过图像特征得到我们想要的 Camera BEV Feature 呢?
2D➡3D 特征转换设计流程如下:
输入:多尺度融合特征
步骤 1:深度分布估计
步骤 2:2D 到 3D 投影计算
输出:3D 伪体素特征
图4 图像2D➡3D特征转换
那转换过程其实是对每一个像素位置去进行一个深度分布的预测,我们会预测一系列离散的深度概率,比如 α0 α1 . . . αD ,那这个概率是作为一个权重乘上像素的图像特征。比如 feature c 是我们原本像素位置的图像特征,乘上对应权重之后从 2D 空间按照深度分布去做的特征转换映射,每一个像素点按射线去进行特征投影,把所有像素点都投影完之后其实就组成了我们所谓的 3D 空间,这个 3D 特征空间不仅有二维的图像特征,还包含有深度信息
最后我们得到了一个叫做 3D 伪体素特征,那为什么叫做伪体素呢?那这个网格并不是按照我们原本输入点云的位置去划分网格的,而是按照深度去划分的网格,它是我们人为定义的一个体素,因此叫做“伪”体素
通过这样的特征转换方式,我们可以把我们得到的 2D 图像特征通过离散深度分布得到 3D 伪体素特征,我们有了 3D 伪体素之后,按照高度维度,我们拍扁也好,或者利用卷积也好,池化也好投影到 BEV 空间我们就可以得到相机支路输出的核心内容了,那也就是我们所谓的 Camera BEV Feature 即相机俯视视角的特征
有了上述分析后,我们再来总结下 Camera Stream 整个流程,如上图所示。输入是 Multi-view Images 多视角图像,通过图像的 Encoder 编码器,图像编码器里面主要是包含两个模块,一个是 2D Backbone 也是就 2D 的主干网络,另一个是 FPN,也就是一个多尺度融合的方式,通过图像编码器后我们可以得到一个 Multi-view Features 多视角的图像特征,我们把图像特征通过我们之前讲的一个 2D➡3D 的转换模块可以映射到 BEV 上,那最终我们就能得到我们需要的图像特征构建出来的 BEV 空间。
2.2 点云支路
OK!我们再来看下 LiDAR Stream 点云支路是怎么做的
其实相比于图像支路而言,点云支路要容易得多,因为点云本身就是 3D 的,我们只要通过我们前面得到的点云特征直接拍扁到 BEV,就可以得到 BEV 空间的点云特征,拍扁这个过程使用卷积、池化等等都可以
那点云特征是怎么提取的呢,前面的 3D Backbone 是什么呢,有基于点的方式的,也有基于体素的方式的,在 BEVFusion 文章中作者本身也提供了很多可选择的方案,包括 PointPillars、CenterPoint、TransFusion
2.3 Fusion
OK,分析完了两个支路,我们再来梳理一下,图像支路得到的是 Camera BEV Features,点云支路得到的是 LiDAR BEV Features,除了两个额外的检测支路之外,下一步我们要做什么呢,那就是融合,也就是 Fusion Module 模块
Fusion Module 模块设计流程如下:
输入:点云和图像 BEV 特征
步骤 1:按通道维度级联点云和图像 BEV 特征,再通过卷积网络提取级联后的特征
步骤 2:通过全局平均池化和卷积预测,实现对级联特征的自适应挑选
输出:融合后的特征
Fusion Module 模块的输入输出是什么呢,我们一直在强调,输入包含两个,一个是图像输入 F_Camera一个是点云输入 F_LiDAR,那怎么融合呢,一个是级联一个是卷积就结束了,然后引入了一个叫 Adaptive Feature Selection,它其实是什么呢,其实就是一个 Attention,翻译过来叫做特征的自适应选择,那什么意思呢,其实就是注意力机制,我们在第二章的基础模块中讲过,所谓的注意力机制包括空间注意力、通道注意力、混合注意力还有 self-attention 等等,BEVFusion 中引入的 Adaptive Feature Selection 属于什么注意力偏向于什么层面的呢,它其实偏向通道层面,去对通道维度进行了加权,考虑的是哪个通道更重要,是点云上的通道呢,还是图像上的通道呢,通过这样一个权重的预测,对通道特征去进行重新的加权,我们从图中可以看到 ⊙ 是一个 Channel-wise 的,是一个通道的相乘,会关注一个重要的通道而忽略不重要的通道
所以在这里融合模块其实可以一定程度上体现 BEVFusion 的作者在 Motivation 中阐述的一个想法,那就是点云和图像是没有主次之分的,可能对于这个场景而言,我们可能认为点云更重要,我们喜欢点云,可能对于下一个场景而言图像更重要,我们喜欢图像,那我们就多关注一点图像,那无论怎么做,它是一种网络自适应的过程,而不是说我们人为定义好了,比如我们就用点云或者就用图像,或者从点云到图像或者从图像到点云,它不是这么做的,它是一种自适应挑选的过程。那融合完成后,我们就可以得到融合特征,自然就可以用来做预测
- 损失函数
OK!我们再来梳理下 BEVFusion
BEVFusion 的输入包含两个方面,一个是多视角的图像输入,还有一个是点云输入,输出对于 3D 检测任务而言是 3D 检测结果。输入图像怎么处理呢,通过 Camera Stream,通过图像编码器 Encoder 我们可以得到图像特征,通过 2D 到 3D 的转换,图像特征可以映射到 BEV 空间,得到所谓的 Camera BEV Features,利用图像特征对 BEV 空间去进行重构。点云怎么处理呢,通过 LiDAR Stream,通过 3D Backbone 网络可以得到点云的 BEV 特征。那现在有了图像的 BEV 特征,有了点云的 BEV 特征,通过融合模块我们得到最终的特征,然后去做检测。
那这里作者引入了两个额外的 Detection Head,像相机模态有一个相机的预测结果,点云模态有一个点云的预测结果,二者合在一起,融合模态有一个融合的预测结果,三个模态其实都有对应的 Loss
另外我们前面提到还有一篇 BEVFusion,是 MIT 的工作,所以我们一般叫 MIT BEVFusion
那这两篇工作属于同时期的工作,我们来看看这两篇文章的思路有什么异同点,那首先输入输出都区别吗,这篇文章的输入同样是多视角图像还有点云数据,那输出有一点点区别,除了 3D 检测任务之外,这篇工作中还引入了分割任务,那任务其实无关紧要,它只是一个额外连接的检测头罢了,任务预测基于的特征是一致的,叫做 Fused BEV Features,融合的 BEV 特征。
那其实这两份工作思路是一致的,它都是通过分开提取特征再融合的方式得到融合后的 BEV 特征,其中 Camera Stream 和 LiDAR Stream 的处理都是一致的,从思路上从框图上讲是完全一样的,有一点点区别的地方在于融合任务,MIT 额外引入了一个分割任务,另外希望大家注意到 BEVFusion-MIT 这篇文章中对单一模态没有特定的检测支路的,那到底需不需要这个额外的模块呢,那这是一个仁者见仁智者见智的事情,大家感兴趣的话也可以在 MIT 的工作基础上添加额外的检测头,看看结果会不会有什么变化
另外想说的是 MIT 的工作其实更偏工程性一点,一些优化的讨论是更丰富的.