3D视觉处理导论

随着AR/VR,自动驾驶的快速发展,由于3D视觉比2D觉有更加丰富的信息,3D视觉变得越来越重要。这里引入两个基本的3D场景分析的深度学习模型,VoxNetPointNet

3D 格式简介

3D图像测量更多的一个维度,深度信息。通常我们使用最多的3D格式是RGB-D点云

RGB-D

通常的像素图片,像素可以通过坐标(x,y)找到,然后得到(R,G,B)的颜色信息。然而对于RGB-D的图像,可以找到(Depth,R,G,B)的四个属性的信息。RGB-D和点云的不同之处在于,点云的坐标(x,y)反应的是真实世界的实际值,而不是简单的整数值。
3D视觉处理导论-LMLPHP

Point Cloud点云

点云可以从RGB-D图像中构建出来。如果你有了RGB-D的图像和相机的内参,那么就可以创建从RGB-D图像中创建点云,通过简单的计算真实世界位置通过相机的内参。这被称之为相机校准。因此,到现在,可以发现,RGB-D图像是网格对齐的图像,而点云则是具有稀疏结构的点
3D视觉处理导论-LMLPHP

3D Vision

正如,2D图像处理问题,我们可以检测,识别所有的物体,在3D图像中。

Voxel Grid

Voxel grid 是最符合直觉的方法,能够将3D模型放入到网格中,让其更像是像素图像,这里称之为Voxel(体素)。那么3D图像就由坐标(x,y,z)描述,像乐高玩具一样。

Voxnet是一个深度学习架构,使用占用网格对3D点云进行分类,在分类问题上取得了非常好的结果。

举例,如果我们将点云放入到32x32x32的体素网格中,我们就可以建立32x32x32的数组,然后将点云在每一个体素中进行计算。

在得到体素后,就可以使用3D卷积网络,高效的在立方体上滑动进行基于体素图像的处理。

我们需要将扫描的图像放入到相同的基中,这样即使我们的图像不同,依然会变成处理同一个问题。然而,对提升采样的操作,则很难确定Voxel的RGB图像。

对于简单的数据集,具有相似的点数,相似的扫描尺度,Voxnet确实是一个很好的,很简单的方法。但是,对于复杂的数据集,却不是一个很好的选择。此外,由于Voxel需要形成3D的数据,使得点云的稀疏结构变得稠密了,对于需要低功耗,实时性高场合,变得不是很有优势。
3D视觉处理导论-LMLPHP

Points

基于Voxel的方法在分类问题上表现出很好的性能,但是同时采样行为也牺牲了很多信息。因此,我们需要对每一个点在网络中进行训练。
3D视觉处理导论-LMLPHP
第一个问题是点的序列问题,我们知道,点云是与点的序列无关的。

主要有三个策略处理序列问题:

    1. 点排序
    1. 以RNN序列输入,采用各种各样的交换增加序列
    1. 使用对称函数聚合每个点的信息。使用对称函数,比如+或者×这类二元函数。

在PointNet文章中,他们认为第一种方法计算强度过大,第二种方法不够鲁棒。因为,使用对称函数max pooling。max pooling是一个主要的操作。

3D视觉处理导论-LMLPHP
总的来说,是convolution,max pooling, dense层的灵活运用。第一次看很难明白,看代码会容易一些。

首先,点云的每一行可以用(x,y,z,r,g,b)表示。三点的例子如下:

-38. 17. 54. 149 148 147
-38. 89. 54. 152 153 152
-79. 99. 32. 151 151 148

PointNet分类

第一个操作是2d convolution, 滤波器大小(1,6)来聚合每一个点的相关信息(x,y,z,r,g,b),输出的维度应该是(n,1,64)

net = tf_util.conv2d(input_image, 64, [1,6], padding='VALID', stride=[1,1], scope='conv1')

注意,此处的padding是选用VALID。

接着,几个1x1的卷积操作检测每个点的小特征。因此,我们的输出尺寸是$ (n, 1, 1024) $.

net = tf_util.conv2d(net, 64, [1,1], padding='VALID', stride=[1,1], scope='conv2')
net = tf_util.conv2d(net, 64, [1,1], padding='VALID', stride=[1,1], scope='conv3')
net = tf_util.conv2d(net, 128, [1,1], padding='VALID', stride=[1,1], scope='conv4')
points_feat1 = tf_util.conv2d(net, 1024, [1,1], padding='VALID', stride=[1,1], scope='conv5')

最重要的步骤是,max pooling选择所有点最突出的特征。这就是为什么点顺序不变性的原因。由于之前的网络层具有1024个滤波器,将得到1024个特征。

pc_feat1 = tf_util.max_pool2d(points_feat1, [n,1], padding='VALID', scope='maxpool1')

接着所有的特征输入到dense层中

pc_feat1 = tf.reshape(pc_feat1, [batch_size, -1])
pc_feat1 = tf_util.fully_connected(pc_feat1, 256, bn=True, scope='fc1')
pc_feat1 = tf_util.fully_connected(pc_feat1, 128, bn=True, scope='fc2')

再添加一层dense层,指定输入的分类数,这就是PointNet对点云的分类方式了。简单总结如下:

    1. 聚合每一个点的信息 - conv2d, kernal size(1,6)
    1. 找到每个点最显著地特征 - max pooling
    1. 全连接分类 - dense

PointNet语义分割

语义分割是分类模型的后续工作。我们依然需要网络能够忽略点排序。所有我们将串联每一个点的特征,每一个点与上下文相关。

pc_feat1_reshape = tf.reshape(pc_feat1, [batch_size, 1, 1, -1])
pc_feat1_expand = tf.tile(pc_feat1_reshape, [1, num_point, 1, 1])
points_feat1_concat = tf.concat(axis=3, values=[points_feat1, pc_feat1_expand])

接着使用1x1的卷积核提取新的逐点特征。

net = tf_util.conv2d(points_feat1_concat, 512, [1,1], padding='VALID', stride=[1,1], scope='conv6')
net = tf_util.conv2d(net, 256, [1,1], padding='VALID', stride=[1,1], scope='conv7')

最后,我们能够做一个逐点预测,比如,每一个点13个分类

net = tf_util.conv2d(net, 13, [1,1], padding='VALID', stride=[1,1], activation_fn=None, scope='conv8')

Reference

  1. An introduction towards 3D Computer Vision

  2. DeepLearning.ai-Summary

  3. Github pointnet

  4. Slides:PointNet: Deep Learning on Point Sets for
    3D Classification and Segmentation

  5. Nvidia Slides:POINT CLOUD DEEP LEARNING

10-03 16:43