0%

3D Vision: 3D CNN, PointNet and PointNet++

3D Vision 数据表征方法

Voxel

Voxel (译为体素),是一种结构化的数据表征方法,就是把空间划为网格,每个网格有占据/空置两种状态,如果是占据状态,则可以用 RGB 三通道来表征它的颜色。当然,一个点除了 RGB 之外,还有法向量等特征。用公式写出:

\[
x_i = (x, y, z, o, r, g, b, …)
\]

其中,$o$ 可取 0 或 1,表示是否占据。一些情况下,也可以取 0 至 1 中的某些数,表示占据的概率。一个例子如下图:

alt text

Voxel 的好处是结构化,形状规则,坏处是内存开销较大,分辨率恒定(在一些精细结构上,恒定的分辨率就显得不够用了)。当然,为了解决分辨率的问题,还有一种 Octree (八叉树结构), 采用树结构进行存储。一个空间被分为 8 份(8 个立方体),如果这一份里有信息,就继续细分,如果这一份里没有信息,就不再分。这样有助于节约存储空间,实现动态的分辨率。如下图:
alt text

Mesh

Mesh 也是一种结构化存储三维数据的方法。它由顶点、边和面构成。它首先存储一个顶点列表:$x_i = (x, y, z)$,再存储一个面索引列表:$f_s = (x_i, x_j, x_k,…)$. Mesh 可以按照面的形状分类,有三角形 (Triangle Mesh)、四边形 (Quad Mesh)、不定形 (Polygon Mesh) 等等。

alt text

3D CNN

3D CNN 就是把二维的卷积神经网络拓展到三维,最早的 3D CNN 被用于处理时空序列信号(如视频,见:Tran, Du, et al. “Learning spatiotemporal features with 3d convolutional networks.” Proceedings of the IEEE international conference on computer vision. 2015.),当然,3D CNN 也可用于处理 3D Vision.

下面介绍这篇文章中 3D CNN 的结构:与二维卷积类似,每一个卷积层仍有一个卷积核,在空间上尺寸为 3*3*3,通道数为 3 ,每次移动的 stride 为 1(在三个维度上的移动 stride 一致),卷积核的个数在图中列出。

每一个卷积层的后面都紧跟着一个 max pooling(PointNet也有),就是在一个 pooling kernel 的范围内,选择最大的数值以代表整个 pooling kernel. Pooling 是 CNN 的常见操作,有助于降低计算复杂度、降噪、提取到更高级/抽象的特征。当然,适不适合 pooling 要根据具体问题选择,比如 AlphaGo 在处理围棋的棋局信息时,显然就不适合用 pooling. Max pooling 可以参考下图。
alt text

最后先要把三维特征图展平为一维,然后经过两个 FC 网络,目的是把图像的特征映射为分数向量,实现分类。整个流程图如下:

alt text

注:

  1. 自己学习 CNN 的时候一开始错误地理解了卷积核的个数和通道数的关系,总结一下:上一层的卷积核的个数作为这一层的输入通道数,这一层的每一个卷积核对所有的输入通道求和,这一层的输出通道数等于这一层的卷积核的个数。
  2. 3D CNN 由于其处理的特性,它只能接受结构化的数据(如 Voxel 这样),对于非结构化的数据(如点云,它需要先把点云转为 Voxel.

PointNet

PointNet 直接处理点云数据,点云数据的特点:

  • 无序性:每个点之间独立,N 个点输入是无序的,即模型比如对 N! 种不同排列的输入给出同样的输出。
  • 几何变换不变性:对点云在空间上做旋转变换(相当于传感器的位置变化),模型在做分类、分割等任务时应给出同样的结果。

PointNet 的思路:点云数据的无序性导致我们应该采用一种对称函数,同时这样的函数要有足够强的拟合能力。于是就有了 PointNet,其整体思路是:对于不同的点先采用同样的多层感知机将其映射到高维空间,然后对其作 max pooling (这显然是一个对称函数), 得到一个向量,可以作为整个点云的语义特征。下面根据不同的任务分别讨论:

  • 分类任务:直接把这个整体的语义特征过一个 MLP 就得到打分,进行分类即可(输出是概率向量)。公式化如下,式中 $\gamma$, $h$ 均为 MLP:

    \[
    f(x_1, x_2,…, x_n) = \gamma(\max_{i = 1, 2,…, n}{h(x_i)})
    \]

  • 分割任务:分割任务要求对每一个点都要进行分类,所以我们要基于整体语义理解每一个点的信息,因此做一个 concat,即把单点向量与整体语义向量直接拼接后经过 MLP 得到单点特征,再过一个 MLP 得到每一个点的打分(输出是矩阵)。

至于空间变换不变性,PointNet 采取了对输入点云预处理的方法以将输入对齐,具体来说:先用一个 T-Net (整个 PointNet 的缩小版,仍含有 MLP, max pooling 等基本结构) 预测一个 3*3 矩阵,再直接将这个矩阵乘以输入点云的位置维度,相当于把输入点云做了一次旋转变换。在点云经过 MLP 提取特征之后,对特征进行同样的操作(这里的矩阵就是特征的维度*特征的维度,这会带来参数量较大,容易过拟合,不易学习的问题,因此作者在这里又针对这个矩阵设置了正则项)。

alt text

注:映射到高维空间可以增加复杂度增强模型的表达能力,并且原始论文论证了当维度足够高时,PointNet 可以拟合任意的连续集合函数。

PointNet++

PointNet 处理的是 3D 视觉的问题,却抛开了视觉最常用的 CNN 结构,很有创新,但是如果能借鉴一些 CNN 的思想,或许模型的效果还会提高,这就有了 PointNet++。

宏观上看,PointNet++ 是在点云局部递归地调用 PointNet, 使其能发现局部特征并且得到不同层级的特征信息(神似 CNN)。

Sampling

在每一次调用 PointNet 时,首先对输入的点云进行采样和分组。采样一般采用最远点采样策略(Farthest Point Sampling, FPS),描述如下:

  1. 随机选取一个点作为中心点
  2. 计算每个点到中心点集中所有点的最近距离
  3. 选出最近距离最远的那个点作为下一个中心点,重复 2, 直至选出 N 个中心点。

相比于随机采样,或 KNN 采样,这样计算量小且中心点分布更均匀,覆盖性好。

Grouping

至于分组方法,有两种办法:球查询和 KNN 分组。

  • 球查询(Query Ball):设定一个欧氏空间的距离 r, 对于所有与中心点距离小于 r 的点,都归入这个中心点的组,这样适合点云密度均匀的情景,如果密度不均,可能出现一些组点数很少,一些组点数太多。

  • KNN 分组:与中心点最近的 k 个点归入这个中心点的组,这样保证每个组的半径一样。

在 PointNet++ 论文中,每一组的点云数量为 K,是一个固定值,这是先经过原始分组之后,若小于 K, 则利用 padding 方法补充,若大于 K, 则直接截断。

采样分组之后就对每一个组运用 PointNet, 得到每个组的一个特征,再把这些特征视作点云(当然输入的维数增加了很多),重复采样-分组- PointNet 这个循环即可实现多层特征的提取。(论文中称之为 Sampling Layer - Grouping Layer - PointNet Layer)

从形状上理解:输入为 $N*(d + C)$, d 为 3 , C 为原始特征(如法向量、RGB等),经过 Sampling Layer, 得到 $N’*(d+C)$, 经过 Grouping Layer, 得到 $N’*K*(d+C)$, 再经过 PointNet Layer, 得到 $N’*(d+C’)$ ($C’$ 为抽取的高维特征)。

MSG/MRG

在 Grouping Layer 中还有一个遗留问题,即对于密度非均匀的点云,Query Ball/KNN 都不能很好的处理,在稠密点云上抽取特征的方法可能不能很好地泛化到稀疏点云上。而实际上 LiDAR 传回的点云数据往往是非均匀的,距离近的位置点云稠密,距离远的位置点云稀疏。

对此,作者提出了 Multi-scale grouping (MSG) 和 Multi-resolution grouping (MRG) 两种办法。MSG 就是在每一层都作不同大小的半径进行分组,然后经过 PointNet Layer 抽取的特征作 concat, 结果为总特征。

然而 MSG 相当于每一个循环里的 Grouping 和 PointNet 都要重复很多次,且包含了在较低层级上对较大的半径作 PointNet, 这样的计算开销很大,因此,作者又提出了 MRG. 融合了不同层级的特征.

MRG 思路如下:每一层的特征依然是两个特征的 concat, 第一个特征是这一层的子特征作 PointNet(left), 第二个特征是这一层的前一层的点直接作 PointNet(right). 在稀疏点云中,右侧的特征更可信,稠密点云中,左侧的特征更可信。把这两种不同的特征融合起来,可以使模型更适应不同密度的点云。

下图中 (a) 是 MSG,(b) 是 MRG.

alt text

PointNet++ 的整体流程图如下:

alt text

论文中提到,这种分组方法相比于 CNN 的卷积核滑动的办法,能减少 overlap, 保证每个分组之间的独立性,效果更好。

Classification & Segmentation

对于视觉的分类/分割任务,PointNet++ 给出了两种不同的办法:

  • 分类:持续抽取特征,直至抽取为一个向量,这就是全局的特征。再把全局特征经过 FC, 就可以得到不同类别的打分向量。
  • 分割:要对每一个点给出一个打分向量进行分类,这就要用到逆距离加权插值的办法。当 PointNet++ 抽取到高级特征之后,设高级特征的点为 $P_s$, 需要将这些特征点回传到原始点 $P_0$. 插值的公式如下:
    alt text
    其中归一化权重如下:
    alt text
    论文中提到,一般 $k$ 取 3, $\alpha$ 取 2. 最后将插值的结果经过 MLP 层得到打分矩阵,就可以将每个点进行分类。