PointNet & PointNet++ 论文解析+算法理解+复现建议(3D点云 分类+分割)

论文介绍地址(Paper,Code,Presentation video and Slides)

PointNet:https://web.stanford.edu/~rqi/pointnet/
PointNet++:https://web.stanford.edu/~rqi/pointnet2/

一、PointNet & PointNet++:问题陈述(Problem Statement)

  1. 由于点云的无序性,直接操作点云比较困难 - PointNet解决(使用具有置换不变性的对称函数)

在这里插入图片描述

图1. 点云的无序性(图片来源:风中摇曳的小萝卜@Bilibili)

视频链接:[5分钟点云学习] #02 PointNet 开山之作

  1. 点云密度不均匀问题 - PointNet++解决
    在这里插入图片描述
图2. ScanNet的虚拟扫描,模拟点云密度不均匀(图片来源:PointNet++)

二、方法(Method)

PointNet:通用连续集函数逼近器(Universal Continuous Set Function Approximator)

在这里插入图片描述

图3. 不同缓解点云分布不均匀的方法(图片来源:PointNet++)

方案1: 将输入排序为规范顺序
要求该图在维度减小时保持空间邻近性,一般难以实现,且效果不好

方案2: 将输入视为序列来训练 RNN,但通过各种排列来扩充训练数据
将元素数量扩展到数千个输入元素(常规点云数量)难以确保鲁棒性

方案3: 使用对称函数
对于在 x i ∈ R d x_i\in\mathbb{R}^d xiRd的无序点云数据 { x 1 , x 2 , . . . , x n } \{x_1,x_2,...,x_n\} {x1,x2,...,xn},可以定义一组函数 f : X → R f:\mathcal{X}\to\mathbb{R} f:XR,将点云映射到向量上:
f ( x 1 , x 2 , … , x n ) = γ ( MAX ⁡ i = 1 , … , n { h ( x i ) } ) , f\left(x_1, x_2, \ldots, x_n\right)=\gamma\left(\operatorname{MAX}_{i=1, \ldots, n}\left\{h\left(x_i\right)\right\}\right), f(x1,x2,,xn)=γ(MAXi=1,,n{h(xi)}),其中 γ \gamma γ h h h通常是MLP网络。

这些函数称为对称函数,除了可以取最大值,取最小值、平均值的操作也可以看作对称函数。

在这里插入图片描述

图4. 对称函数图解(图片来源:风中摇曳的小萝卜@Bilibili)

视频链接: [5分钟点云学习] #03 PointNet++ 竟然是图结构?!

分层点集特征学习(Hierarchical Point Set Feature Learning) - PointNet++

构建点的分层分组,并沿着层次结构逐步抽象出越来越大的局部区域网络结构。

在这里插入图片描述

图5. 分层点集特征学习(图片来源:PointNet++)

集合抽象层(set abstraction layer)

  1. 采样层(sampling):从输入点中选择一组局部区域质心

  2. 分组层(grouping):通过查找质心周围的“相邻”点来构造 局部区域集(质心+“相邻”点)

  3. PointNet层:使用mini-PointNet将局部区域模式编码为特征向量

数据维数的变化(不考虑BatchSize):

N × (d + C) -> 集合抽象层 -> N’ × (d +C’)

  • N:采样点的个数, 4096
  • d:坐标信息,xyz(xy去中心化,绕z轴随机旋转),故为 3
  • C:特征数量(xyz(xy去中心化,绕z轴随机旋转) + RGB(=/255, 0-1) + xyz(=/max, 0-1), 3+3+3 = 91
  • N’:子采样点的个数,由sample_and_group函数参数决定,1024
  • C’:总结局部上下文的新特征数,由MLP的输出通道数决定,64
采样层(sampling layer)

给定N = 4096个点 { x 1 , x 2 , . . . , x n } \{x_{1},x_{2},...,x_{n}\} {x1,x2,...,xn},使用迭代最远点采样(farthest point sampling, FPS)选出N’ = 1024个采样点(质心) { x i 1 , x i 2 , . . . , x i m } \{x_{i_1},x_{i_2},...,x_{i_m}\} {xi1,xi2,...,xim},使得 x i j x_{i_j} xij是和其他点 { x i 1 , x i 2 , . . . , x i j − 1 } \{x_{i_1},x_{i_2},...,x_{i_{j-1}}\} {xi1,xi2,...,xij1}距离最远的点。

在这里插入图片描述

图6. FPS图解(图片来源:风中摇曳的小萝卜@Bilibili)

视频链接:[5分钟点云学习] #01 最远点采样 FPS

分组层(grouping layer)

输入:

  • 点集:N × (d +C) 质心集坐标:N’ × d

输出:

  • 点集组:N’ × K × (d + C) ,其中“K”为质心点邻域中的点数

有两种分组策略:

  1. kNN:取每个质心点最近的K个点
  2. 球查询(ball query):在给定半径范围内取K个点(按序号从小到大排前K个),其局部邻域保证了固定的区域尺度,从而使局部区域特征在空间上更具泛化性

在这里插入图片描述

图7. kNN与球查询的对比(图片来源:PointNet++)

论文中主要使用了基于半径的球查询。在这里,作者还实验了基于kNN的邻域搜索,并使用不同的搜索半径和k。在这个实验中,所有的训练和测试都是在具有均匀采样密度的ModelNet40分类任务数据集上进行的。使用1024个点。

如表5所示,基于半径的球查询略好于基于kNN的方法。然而,我们推测,在非常不均匀的点集中,基于kNN的查询将导致较差的泛化能力。我们还观察到,稍大的半径对性能有帮助,可能是因为它捕捉到了更丰富的局部模式

PointNet层(PointNet layer)

输入:

  • 采样分组后的点集组:N’ × K × (d + C)

输出:

  • 经过PointNet层提取特征后的点集组:N’ × (d + C’)
  1. 先将局部区域中的点的坐标转换为相对于质心点的局部坐标系(即采样分组后的点集组减去质心坐标
grouped_xyz_norm = grouped_xyz - new_xyz.view(B, S, 1, C)
  1. 经过多层MLP
for i, conv in enumerate(self.mlp_convs):
    bn = self.mlp_bns[i]
    new_points = F.relu(bn(conv(new_points)))
  1. 对特征进行最大池化
new_points = torch.max(new_points, 2)[0]

用于集合分割的点特征传播(Point Feature Propagation for Set Segmentation)

在这里插入图片描述

图8. PointNet++网络结构(图片来源:PointNet++)

采样出的点小于原始点的数量,如何恢复所有点的特征?

一种简单的方法是采样所有点,但会造成很高的计算成本。

文中提出使用点特征传播(Point Feature Propagation)的方式,基于距离的插值跨级跳跃链接的分层传播策略,如要将点特征从 N l × ( d + C ) N_{l}\times(d+C) Nl×(d+C)点传播到 N l − 1 N_{l-1} Nl1 点,其中 N l − 1 N_{l-1} Nl1 N l N_l Nl(其中 N l ≤ N l − 1 , N_{l}\leq N_{l-1}, NlNl1,)通过在 N l − 1 N_{l-1} Nl1点的坐标处插值 N l N_l Nl 点的特征值 f f f 来实现特征传播:

在这里插入图片描述

其中, f f f 为特征值, p = 2 p = 2 p=2 k = 3 k = 3 k=3

如此基于距离的倒数对特征进行加权拟合,恢复出未被采样到点的特征。层层恢复回去,最终得到每个点的特征以及语义信息。

连接的特征通过单位点网(unit pointnet),类似于 CNN 中的1x1卷积。应用一些共享的全连接层和 ReLU 层来更新每个点的特征向量。重复该过程,直到将特征传播到原始点集。

多尺度分组和多分辨率分组(MSG & MRG)

点集在不同区域的密度不均匀是很常见的。这种不均匀性给点集特征学习带来了重大挑战。在密集数据中学习的特征可能无法推广到稀疏采样区域。因此,针对稀疏点云训练的模型可能无法识别细粒度的局部结构。
为解决这一问题,文章提出了密度自适应 PointNet 层。

在这里插入图片描述

图9. 多尺度分组和多分辨率分组(图片来源:PointNet++)
  • 多尺度分组(Multi-scale grouping, MSG)
    • 在每个质心点的大规模邻域中运行本地 PointNet,时间成本高
  • 多分辨率分组(Multi-resolution grouping, MRG)
    • 在计算上更加高效,因为避免了在最低级别的大规模邻域中进行特征提取

网络结构(Architecture)

  1. PointNet

在这里插入图片描述

图10. PointNet网络结构(图片来源:PointNet)
  1. PointNet++

在这里插入图片描述

图11. PointNet++网络结构(图片来源:PointNet++)

三、实验(Experiment)

性能比较 - PointNet

在这里插入图片描述

图12. 不同缓解点云分布不均匀的方法(图片来源:PointNet++))
  • 未排序的MLP(n×3 arrays)
  • 排序的MLP(n×3 arrays)
  • 将输入点视为序列的RNN模型(LSTM)
  • 基于注意力的加权和,其中从每个点特征预测标量分数,然后通过计算 softmax 跨点对分数进行归一化。然后根据归一化分数和点特征计算加权和。(O. Vinyals, S. Bengio, and M. Kudlur. Order matters: Sequence to sequence for sets. arXiv preprint arXiv:1511.06391, 2015.
  • 平均值池化
  • 最大值池化

性能比较 - PointNet++

在这里插入图片描述

图13. PointNet++性能(图片来源:PointNet++)

在这里插入图片描述

图14. 从ScanNet生成的虚拟扫描(图片来源:PointNet++))
  • ScanNet:原始数据集
  • ScanNet non-uniform:作者合成的类似于激光雷达直接获取的扫描场景的虚拟扫描,测试模型在具有非均匀采样密度的扫描上执行情况

由于采样密度从均匀点云转移到虚拟扫描场景,SSG性能大大下降。但是,MRG网络对采样密度偏移更具鲁棒性,因为当采样稀疏时,它能够自动切换到描绘更粗粒度的特征。MSG网络仅受到轻微影响,并且在比较中实现了最佳精度。这些证明了的密度自适应层设计的有效性。

鲁棒性测试 - PointNet

在这里插入图片描述

图15. PointNet鲁棒性测试(图片来源:PointNet)
  • 删除点(最远点/随机点)
  • 插异常值
  • 添加高斯噪声

鲁棒性测试 - PointNet++

在这里插入图片描述

图16. PointNet++鲁棒性测试(图片来源:PointNet++)

在测试期间随机丢弃点(见图左4),以验证PointNer++对非均匀和稀疏数据的鲁棒性。

  • 在图4中,右侧,我们看到MSG+DP(训练期间具有随机输入丢失的多尺度分组)和MRG+DP。MSG+DP性能从1024个测试点下降到256个测试点,降幅不到1%。此外,与替代方案相比,它在几乎所有采样密度上都实现了最佳性能。
  • 原始PointNet 在密度变化下相当稳健,因为它关注全局概况而不是精细细节。然而,与PointNet++相比,细节的丢失也使其功能减弱。
  • SSG(每级单尺度分组的消融PointNet++)不能推广到稀疏采样密度,而SSG+DP通过在训练时间内随机丢点来弥补这一问题。

可视化 - PointNet

在这里插入图片描述

图17. PointNet可视化(图片来源:PointNet)

三种点集形状将给出相同的全局形状特征 f ( S ) f(S) f(S)

  • 原始形状(Original Shape)
  • 关键点集(Critical Point Sets):对最大池化特征有贡献的关键点
  • 上采样形状(Upper-bound Shapes):通过网络 forwarding 边长为 2 的立方体中的所有点,并选择其点函数值 ( h 1 ( p ) , h 2 ( p ) , ⋯   , h K ( p ) ) (h_1(p),h_2(p),\cdots,h_K(p)) (h1(p),h2(p),,hK(p))不大于全局形状描述符的点 p p p

可视化 - PointNet++

在这里插入图片描述

图18. PointNet++可视化(图片来源:PointNet++)

作者可视化了分层网络的第一级内核所学到的东西:在空间中创建了一个体素网格,并聚合局部点集,这些点集在网格单元中激活某些神经元最多。在可视化中可以看到平面、双平面、直线、角等结构。

在这里插入图片描述

图19. PointNet++可视化 - 分类结果(图片来源:PointNet++)

PointNet正确地捕捉了房间的整体布局,但未能发现家具。相比之下,PointNet++在分割房间布局之外的对象方面要好得多。因为PointNet++进行分层特征学习,并捕获了不同尺度的几何特征,这对于理解多个级别的场景和标记各种大小的对象非常重要。

ScanNet实验细节 - PointNet++

为了从ScanNet场景(ScanNet为体素数据集)中生成训练数据,作者从初始场景中采样1.5米乘1.5米乘3米的立方体,然后保留其中≥2%的体素被占用,且≥70%的表面体素具有有效注释的立方体。

作者在飞行中对这样的训练立方体进行采样,并沿右上轴随机旋转它,将扩充的点添加到点集以形成固定的基数(在本文的情况下为8192)。

在测试期间,作者类似地将测试场景拆分为更小的立方体,并首先获得立方体中每个点的标签预测,然后合并来自同一场景的所有立方体中的标签预测。如果一个点从不同的立方体中得到不同的标签,则进行多数投票,得到最终的点标签预测。

法向量预测 - PointNet

在这里插入图片描述

图20. PointNet法向量预测(图片来源:PointNet)

更改分割PointNet的最后一层,预测每个点的法向量。我们使用余弦距离的绝对值作为Loss。

将PointNet法线预测结果(左列)与从网格计算的ground-truth法线(右列)进行了比较。可以观察到一个合理的正常重建。PointNet的预测比ground-truth更平滑、更连续,ground-truth包括某些区域的翻转法线方向。

代码参考

  1. PyTorch版本(非官方-S3DIS):Pointnet_Pointnet2_pytorch
    https://github.com/yanx27/Pointnet_Pointnet2_pytorch
  2. PyTorch版本(非官方-ScanNet):Pointnet2.ScanNet
    https://github.com/daveredrum/Pointnet2.ScanNet
  3. Tensorflow版本(官方-ScanNet):pointnet2
    https://github.com/charlesq34/pointnet2

复现建议:

使用第一个GitHub项目(PyTorch版本+S3DIS)的代码为基础,环境配置较为容易,只需要安装PyTorch和tqdm即可。如果需要使用ScanNet数据集进行测试,可以参考第二个第三个GitHub项目的数据导入部分进行更改。

四、算法不足(prospect)

注意,本节非PointNet、PointNet++论文中内容

  1. PointNet对大场景的局部结构捕捉能力有限。
  2. PointNet++遇到更大场景时,需要增加采样点数,导致占用内存较大,限制了其在更大规模点云上的表现。
  3. 采样慢,FPS需要200多秒才能对100万个点中的10%进行采样。(Hu, Qingyong et al. “RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds.” 2020 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR) (2019): 11105-11114.

  1. PointNet++原论文使用仅XYZ的ScanNet,特征数量为3,上述9维的特征数量为PointNet使用S3DIS数据集所用的配置 ↩︎

  • 33
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PointNet++是一种用于点云识别的神经网络算法,它包含基于点云数据的多个层次,可以适应不同大小和分辨率的点云。以下是PointNet++的一个简单实现示例。 ``` import tensorflow as tf from tensorflow.keras import layers def pointnet2_model(num_classes, input_shape): inputs = layers.Input(shape=input_shape) # Pointnet++ MSG x = layers.Conv1D(64, 1, activation='relu')(inputs) x = layers.BatchNormalization()(x) x = layers.Conv1D(64, 1, activation='relu')(x) x = layers.BatchNormalization()(x) # Set Abstraction Block 1 x0 = x x = layers.Conv1D(64, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x = layers.Conv1D(64, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x1 = layers.Conv1D(64, 1)(x) x = layers.Conv1D(128, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x = layers.Conv1D(128, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x2 = layers.Conv1D(128, 1)(x) x = layers.Conv1D(256, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x = layers.Conv1D(256, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x3 = layers.Conv1D(256, 1)(x) # Set Abstraction Block 2 x = layers.Concatenate()([x1, x2, x3]) x = layers.Conv1D(256, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x = layers.Conv1D(512, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x = layers.Conv1D(1024, 1, activation='relu')(x) x = layers.BatchNormalization()(x) x4 = layers.Conv1D(1024, 1)(x) # Global Feature Extraction x = layers.MaxPooling1D(pool_size=input_shape[0])(x4) x = layers.Dense(512, activation='relu')(x) x = layers.BatchNormalization()(x) x = layers.Dense(256, activation='relu')(x) x = layers.BatchNormalization()(x) # Classification x = layers.Flatten()(x) outputs = layers.Dense(num_classes, activation='softmax')(x) # Model Definition model = tf.keras.Model(inputs=inputs, outputs=outputs, name='pointnet2') return model ``` 在这个示例中,我们定义了一个名为`pointnet2_model`的函数,该函数接受两个参数:`num_classes`是输出类别的数量,`input_shape`是输入点云数据的形状。函数返回一个Keras模型对象。 该模型包含两个Set Abstraction Block和一个Global Feature Extraction层,以及一个分类器层。每个Set Abstraction Block包含一个MSG层和三个PointNet++层,用于对点云数据进行多个分辨率的特征提取。Global Feature Extraction层将所有点的特征向量池化为一个全局特征向量,用于分类器。 在训练过程中,我们可以使用类似于其他神经网络模型的训练方法,如随机梯度下降(SGD)或自适应矩估计(Adam)。 ``` model = pointnet2_model(num_classes=10, input_shape=(1024, 3)) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, batch_size=32, epochs=100, validation_data=(x_val, y_val)) ``` 在这个示例中,我们使用了Adam优化器和交叉熵损失函数进行训练。我们还将训练数据分为批次,以便更有效地训练模型,并记录模型的准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值