# PointNet

## &#x20;原文链接

论文原文链接：<https://arxiv.org/abs/1612.00593>

{% file src="<https://3522718595-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FD1M64ktNsEA8xmIL6eKj%2Fuploads%2FZVTPCXo7oNZbtttZN31V%2FPointNet_Deep%20Learning%20on%20Point%20Sets%20for%203D%20Classification%20and%20Segmentation.pdf?alt=media&token=242f1aaa-b62c-42ae-b6c8-999b3341d184>" %}
点击下载论文原文
{% endfile %}

Github 基于Pytorch实现链接：<https://github.com/yanx27/Pointnet_Pointnet2_pytorch>

参考视频：<https://www.bilibili.com/video/BV12e4y147xP>

## 简介

点云是一种很重要的几何数据结构。因为它的格式很不规则，所以很多研究者会把这种数据转成规则的3D方格或者一堆图片集合。但这样做有时候会导致数据变得不必要的庞大，而且还会有些问题。这篇论文提出了一种新型的神经网络，能够直接使用点云数据，并且可以很好地处理输入中点的位置变化。这个网络叫PointNet，可以用于对象分类、部分分割、场景语义分析等多个领域。尽管它很简单，但非常高效而且有效。实际测试中表现很强，能够达到甚至超过当前技术水平。从理论上讲，我们也分析了这个网络学到了什么以及为什么它对输入扰动和破坏很鲁棒。

## 源码解析

### STN3d、STNkd

Spatial Transformer Network (STN) 模型中的 3D 版本，用于学习输入点云的旋转、缩放和平移变换，使输入点云更加规范化和可比较。

<figure><img src="https://3522718595-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FD1M64ktNsEA8xmIL6eKj%2Fuploads%2FUFECGV5O6WI7W2mQEuzA%2Fimage.png?alt=media&#x26;token=21e97d12-da2f-43c9-89b1-83bf53083d67" alt=""><figcaption><p>原文中的图，左边是学习3x3矩阵(STN3d)，右边是学习kxk的矩阵</p></figcaption></figure>

STNkd也是类似的结构，只是输出的矩阵变成了k \* k

**前向传播结构**

<table><thead><tr><th width="60" data-type="number">N</th><th width="223.5">步骤</th><th width="400">源码</th><th>尺寸</th></tr></thead><tbody><tr><td>1</td><td>输入，点云数组，点数为n，每个点三个维度</td><td></td><td>(n, 3)</td></tr><tr><td>2</td><td>进行一维卷积，3升维到64，进行归一化，然后进入relu</td><td><code>x = F.relu(self.bn1(self.conv1(x)))</code></td><td>(n, 64)</td></tr><tr><td>3</td><td>进行一维卷积，64升维到128，进行归一化，然后进入relu</td><td><code>x = F.relu(self.bn2(self.conv2(x)))</code></td><td>(n, 128)</td></tr><tr><td>4</td><td>进行一维卷积，128升维到1024，进行归一化，然后进入relu</td><td><code>x = F.relu(self.bn3(self.conv3(x)))</code></td><td>(n, 1024)</td></tr><tr><td>5</td><td>在第2个维度上取最大值 （纵轴，1个batch中每一个向量的同一个下标）</td><td><code>x = torch.max(x, 2, keepdim=True)[0] x = x.view(-1, 1024)</code></td><td>(1, 1024)</td></tr><tr><td>6</td><td>全连接 1024 到 512 后 归一化 relu</td><td><code>x = F.relu(self.bn4(self.fc1(x)))</code></td><td>(1, 512)</td></tr><tr><td>7</td><td>全连接 512 到 256 后 归一化 relu</td><td><code>x = F.relu(self.bn5(self.fc2(x)))</code></td><td>(1, 256)</td></tr><tr><td>8</td><td>全连接 256 到 9</td><td><code>x = self.fc3(x)</code></td><td>(1, 9)</td></tr><tr><td>9</td><td>保证结果是正交矩阵</td><td><code>iden = [1,0,0,0,1,0,0,0,1]</code><br><code>x = x + iden</code></td><td>(1, 9)</td></tr><tr><td>10</td><td>得到旋转矩阵</td><td><code>x = x.view(-1, 3, 3)</code></td><td>(1, 3, 3)</td></tr></tbody></table>

### PointNetfeat

PointNet的特征提取模块,得到的结果能用于提取点云的全局(进行点云分类)或局部特征(进行点云分割)，以便进行分类或分割任务。

<figure><img src="https://3522718595-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FD1M64ktNsEA8xmIL6eKj%2Fuploads%2Fh8DJbZOYjEohAmZksDVd%2Fimage.png?alt=media&#x26;token=a5b0d892-1bd5-49b8-9c5b-93461b5f3689" alt=""><figcaption><p>源码此类实现了图中的位置的网络</p></figcaption></figure>

**前向传播结构**

<table><thead><tr><th width="62" data-type="number"></th><th>步骤</th><th>源码</th><th width="119">尺寸</th></tr></thead><tbody><tr><td>1</td><td>输入一组点云</td><td><code>n_pts = x.size()[2]</code></td><td>(batch_size, 3, n) <br>以下忽略batchsize</td></tr><tr><td>2</td><td>用STN3d，学习旋转矩阵 trans</td><td><code>trans = self.stn(x)</code></td><td></td></tr><tr><td>3</td><td>将x进行1与2维度转换<br>将x进行矩阵的旋转<br>再将维度换回来</td><td><code>x = x.transpose(2, 1)</code><br><code>x = torch.bmm(x, trans)</code><br><code>x = x.transpose(2, 1)</code></td><td>(3, n)<br>(n, 3)<br>(3, n)</td></tr><tr><td>4</td><td>1D卷积层 3->64 、归一化、relu</td><td><p><code>x = F.relu(s</code></p><p><code>elf.bn1(self.conv1(x)))</code></p></td><td>(64, n)</td></tr><tr><td>5</td><td><p>判断是否进行STN64</p><p>若是 学习矩阵、转维度、应用旋转、再转回来</p></td><td><code>trans_feat = self.fstn(x)</code><br><code>x = x.transpose(2,1)</code><br><code>x = torch.bmm(x, trans_feat)</code><br><code>x = x.transpose(2,1)</code></td><td><p>(64, n)</p><p>(n, 64)</p><p>(64, n)</p></td></tr><tr><td>6</td><td>至此，得到了点云里每个点的特征 升到了64维，记录为pointfeat</td><td><code>pointfeat = x</code></td><td>(64, n)</td></tr><tr><td>7</td><td>将x进行连续升维 64->128->1024</td><td><code>x = F.relu(self.bn2(self.conv2(x)))</code><br><code>x = self.bn3(self.conv3(x))</code></td><td>(128, n)<br>(1024, n)</td></tr><tr><td>8</td><td>最大池化,将全局特征变为1024</td><td><code>x = torch.max(x, 2, keepdim=True)[0]</code></td><td>(1024, 1)</td></tr><tr><td>9</td><td>如果输出全局特征，则直接返回x</td><td><code>if self.global_feat: # 是否输出旋转矩阵特征 return x, trans, trans_feat</code></td><td></td></tr><tr><td>10</td><td>否则进行每个点与全局特征的拼接 ，将全局特征数据复制npt次拼上每个点</td><td><code>x = x.view(-1, 1024, 1).repeat(1, 1, n_pts)</code><br><code>return torch.cat([x, pointfeat], 1), trans, trans_feat</code></td><td>[64+1024=1088，num_points]</td></tr></tbody></table>

### PointNetDenseCls

用于对输入点云进行密集分类任务，即对每个点都预测其所属的类别。

<figure><img src="https://3522718595-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FD1M64ktNsEA8xmIL6eKj%2Fuploads%2FGqGoR9DyTnhTErT0nAJF%2Fimage.png?alt=media&#x26;token=50d76f56-da37-4b88-b56f-54e39b4d00cc" alt=""><figcaption><p>源码此类实现了图中的位置的网络</p></figcaption></figure>

### PointNetCls

PointNet 的分类模块，用于对输入点云（整个模型）进行分类任务。

<figure><img src="https://3522718595-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FD1M64ktNsEA8xmIL6eKj%2Fuploads%2F62vFtxiFlKA0w2WAfBFj%2Fimage.png?alt=media&#x26;token=38bc983c-6208-4995-87bf-c84a78faded7" alt=""><figcaption><p>源码此类实现了图中的位置的网络</p></figcaption></figure>
