点云深度学习系列1——PointCNN代码阅读心得

先是一点题外话,作者的Github维护的很好,经常更新,而且对读者的答疑也很及时,大赞。下面进入正题。

看过代码的同学都知道,作者的核心思想X变换在代码pointcnn.py中,里面主要包含两部分,特征提取和X矩阵训练。下面分开来说。

用于提取邻域特征的只有两个dense层(也叫fc层/MLP),很简单地将尺度为(P,K,C)的邻域结构升维到了(P,K,C’)。

    # Prepare features to be transformed
    nn_fts_from_pts_0 = pf.dense(nn_pts_local_bn, C_pts_fts, tag + 'nn_fts_from_pts_0', is_training)#fc1, (N, P, K, C_pts_fts)
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts, tag + 'nn_fts_from_pt', is_training)#fc2, features, f_delta
    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts, indices, name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev], axis=-1, name=tag + 'nn_fts_input')

接下来作者没有使用pointnet中的maxpooling,这一点论文中也提到了,因为作者认为训练出一个X变换来能达到更好的效果。

而关于X变换,居然有三层,第一层是卷积层,让人很吃惊,卷积核是1*k的,也就是在邻域维度上,直接把k个邻域点汇聚到一个点上,且用了K×K个卷积层,把特征维度升高到k*k,维度从(P,K,C)变成了(P,1,K×K);

        ######################## X-transformation #########################
        X_0 = pf.conv2d(nn_pts_local_bn, K * K, tag + 'X_0', is_training, (1, K), with_bn=False)
        #kernal size(1, K, 3), kernal num=K*K, so the output size is (N, P, 1, K*K). so this operator is in the neighbor point dimentional.

紧接着,作者用了两个dense层,保持了这个结构

        X_1 = pf.dense(X_0, K * K, tag + 'X_1', is_training, with_bn=False)#in the center point dimensional ,P decrease to 1.
        X_2 = pf.dense(X_1, K * K, tag + 'X_2', is_training, with_bn=False, activation=None)#(N, P, 1, K*K)

然后reshape成(P,K,K),这就得到了X-transporm矩阵。

        X = tf.reshape(X_2, (N, P, K, K), name=tag + 'X')

与上文得到的特征图进行卷积相乘。

        fts_X = tf.matmul(X, nn_fts_input, name=tag + 'fts_X')

最后是卷积操作,输出通道数为C,(1, K)尺度的卷积核用来把K邻域融合,此处有别于Pointnet++中的池化操作。

 
 
    fts = pf.separable_conv2d(fts_X, C, tag + 'fts', is_training, (1, K), depth_multiplier=depth_multiplier)#输出(N, P, 1, C)
    return tf.squeeze(fts, axis=2, name=tag + 'fts_3d')#输出(N, P, C)

关于可分卷积separable_conv2d的介绍可以参考我的另一篇博客:tf.layers.separable_conv2d实现深度可分卷积

坦白说,看完以后挺吃惊的。因为看上去训练X变换矩阵的复杂性比特征提取部分还要大。(事实上PointCNN中的变换矩阵已经比PointNet中的T-net代码量小多了)

总的来说,作者的出发点是解决点云排序问题,从代码来看,更确切地说,是K邻域的排序问题。提取特征的时候,小心翼翼,邻域维度上不敢轻举妄动,因为诸如卷积操作等都会因排序的变换而使结果发生变化,因此在这个维度上,pointnet用了maxpooling,本文作者没有进行任何操作,就静静地让邻域的点,各自升高了维度,然后听侯差遣。但是在训练X变换矩阵的时候,就放开手脚、大开大合了,为了得到K*K的矩阵,不管物理意义,进行各种维度变化,可以说无所顾忌了。

又想到另一个问题,因为这里解决的是邻域点的排序问题,那你从一堆点云里选择中心点的时候,中心点的排序就不要紧了吗?或者中心点的选择如果每次都不一样的话,即使能够解决邻域的排序问题,但是这些邻域压根可能就互不相同。因此,今年新出来的SONet解决了这个问题。将点云在空间的分布进行了唯一的提取。这个思路还是挺棒的,实验效果也超越了PointNet++和PointCNN。但是运行代码需要配置的东西太复杂,我一直都没搞好,这也算一个小小的缺陷吧。

代码GitHub地址:https://github.com/yangyanli/PointCNN

论文arXiv地址:https://arxiv.org/abs/1801.07791

也欢迎关注公众号——3D点云深度学习。


猜你喜欢

转载自blog.csdn.net/qq_33278989/article/details/80047252