PointNet代码详解

article/2025/9/14 3:29:25

PointNet代码详解

最近在做点云深度学习的机器人抓取,这篇博客主要是把近期学习PointNet的一些总结的知识点汇总一下。

PointNet概述详见以下网址和博客,这里也就不再赘述了。
三维深度学习之pointnet系列详解
PointNet网络结构详细解析
PointNet论文理解和代码分析
PointNet论文复现及代码详解

这里着重来探讨一下内部的代码(pointnet-master\models路径下的)。
PointNet原文及Github代码下载

详细的网络结构图如下
在这里插入图片描述
主要讲一下应该注意的地方:
(1)网络结构内部主要分为分类和分割两部分,从 global feature 开始区分分类和分割,关于点云的分类和分割,详见点云分类与分割的区别联系。
(2)我们这边主要定义数据维度的表示为 (B, H, W, C) ,也就是Batch, Height, Width, Channel。 开始输入时是一个3D的张量 (B, n, 3),其中B即为训练的批量, n 为点云个数,3则代表了点云的(x,y,z)的3个位置,因此为了后续的卷积操作,会将其增加维度到4D张量(B, n, 3, 1),方便后面卷积核提取产生特征通道数C,(B, n, 3, C)。
(3)第一层的卷积核大小为(1, 3),因为每个点的维度都是(x, y, z),后续的所有卷积核大小均为(1, 1),因为经过第一次卷积之后数据就变为了(B, n, 1, C)。
(4)整个网络框架内部使用了两个分支网络Transform(T-Net)。T-Net对原样本进行一定的卷积和全连接等操作后得到变换矩阵并与原样本相乘完成点云内部结构的调整,但并不改变原样本数据的格式。第一次T-Net输出一个33的矩阵,第二次T-Net输出一个6464的矩阵。

阅读后续代码前请仔细看完这篇博客,务必理解里面内容
PointNet网络结构详细解析

T-Net

对应文件为“pointnet-master\models\transform_nets.py”
根据网络结构图可知输入量时B×n×3,对于input_transform来说,主要经历了以下处理过程:
卷积:64–128–1024
全连接:1024–512–256–3*K(代码中给出K=3)
最后reshape得到变换矩阵

def input_transform_net(point_cloud, is_training, bn_decay=None, K=3):""" Input (XYZ) Transform Net, input is BxNx3 gray imageReturn:Transformation matrix of size 3xK """#建议阅读时忽略batch_size的维度,将张量视作一个3维矩阵[n, W, C]#其中n为点云数,也就是Height;W为矩阵宽Width;C为特征通道数batch_size = point_cloud.get_shape()[0].value	#得到训练批量num_point = point_cloud.get_shape()[1].value	#得到点云个数input_image = tf.expand_dims(point_cloud, -1)	#扩展维度为四维张量,-1表示最后一个维度,第四个维度来表示特征通道数#input_image  [batch_size, num_point, 3, 1]#第一次卷积,采用64个大小为[1,3]的卷积核#完成之后  net表示为[batch_size, num_point, 1, 64]net = tf_util.conv2d(input_image, 64, [1,3],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='tconv1', bn_decay=bn_decay)#第二次卷积,采用128个大小为[1,1]的卷积核#完成之后  net表示为[batch_size, num_point, 1, 128]net = tf_util.conv2d(net, 128, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='tconv2', bn_decay=bn_decay)#第三次卷积,采用1024个大小为[1,1]的卷积核#完成之后  net表示为[batch_size, num_point, 1, 1024]net = tf_util.conv2d(net, 1024, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='tconv3', bn_decay=bn_decay)#max pooling,扫描的模板大小为[num_point, 1],也就是每一个特征通道仅保留一个feature#完成该池化操作之后的net表示为[batch_size, 1, 1, 1024]net = tf_util.max_pool2d(net, [num_point,1],padding='VALID', scope='tmaxpool')#reshape张量,即将其平面化为[batch_size, 1024]net = tf.reshape(net, [batch_size, -1])net = tf_util.fully_connected(net, 512, bn=True, is_training=is_training,scope='tfc1', bn_decay=bn_decay)net = tf_util.fully_connected(net, 256, bn=True, is_training=is_training,scope='tfc2', bn_decay=bn_decay)#最后得到的net表示为[batch_size, 256],即每组只保留256个特征#再经过下述的全连接操作之后得到[batch_size, 3*K]大小的transformwith tf.variable_scope('transform_XYZ') as sc:assert(K==3)weights = tf.get_variable('weights', [256, 3*K],initializer=tf.constant_initializer(0.0),dtype=tf.float32)biases = tf.get_variable('biases', [3*K],initializer=tf.constant_initializer(0.0),dtype=tf.float32)biases += tf.constant([1,0,0,0,1,0,0,0,1], dtype=tf.float32)transform = tf.matmul(net, weights)transform = tf.nn.bias_add(transform, biases)#重新塑造变换矩阵[batch_size, 3*K]为[batch_size, 3, K]transform = tf.reshape(transform, [batch_size, 3, K])return transform

feature_transform同input_transform类似,经历处理过程表示为:
卷积:64–128–1024
全连接:1024–512–256–64*K(代码中给出K=64)
最后reshape得到变换矩阵
这里就不再注释,大家也可以通过下面未注释的代码测试一下上一个transform有没有看懂了。

def feature_transform_net(inputs, is_training, bn_decay=None, K=64):""" Feature Transform Net, input is BxNx1xKReturn:Transformation matrix of size KxK """batch_size = inputs.get_shape()[0].valuenum_point = inputs.get_shape()[1].valuenet = tf_util.conv2d(inputs, 64, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='tconv1', bn_decay=bn_decay)net = tf_util.conv2d(net, 128, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='tconv2', bn_decay=bn_decay)net = tf_util.conv2d(net, 1024, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='tconv3', bn_decay=bn_decay)net = tf_util.max_pool2d(net, [num_point,1],padding='VALID', scope='tmaxpool')net = tf.reshape(net, [batch_size, -1])net = tf_util.fully_connected(net, 512, bn=True, is_training=is_training,scope='tfc1', bn_decay=bn_decay)net = tf_util.fully_connected(net, 256, bn=True, is_training=is_training,scope='tfc2', bn_decay=bn_decay)with tf.variable_scope('transform_feat') as sc:weights = tf.get_variable('weights', [256, K*K],initializer=tf.constant_initializer(0.0),dtype=tf.float32)biases = tf.get_variable('biases', [K*K],initializer=tf.constant_initializer(0.0),dtype=tf.float32)biases += tf.constant(np.eye(K).flatten(), dtype=tf.float32)transform = tf.matmul(net, weights)transform = tf.nn.bias_add(transform, biases)transform = tf.reshape(transform, [batch_size, K, K])return transform

分类网络结构

分类网络内容即PointNet网络结构图中最上面的那个主要框图,即Classification Network。对应文件为“pointnet-master\models\pointnet_cls.py”

def get_model(point_cloud, is_training, bn_decay=None):""" Classification PointNet, input is BxNx3, output Bx40 """batch_size = point_cloud.get_shape()[0].value	#得到训练批量num_point = point_cloud.get_shape()[1].value	#得到点云个数end_points = {}									#生成字典with tf.variable_scope('transform_net1') as sc:transform = input_transform_net(point_cloud, is_training, bn_decay, K=3)	#通过第一个T-Net得到input_tranformpoint_cloud_transformed = tf.matmul(point_cloud, transform)	#将原矩阵和input_transform相乘完成转换input_image = tf.expand_dims(point_cloud_transformed, -1)	#扩展为4维张量[batch_size, num_point, 3, 1]#使用64个大小为[1,3]的卷积核得到64个特征通道net = tf_util.conv2d(input_image, 64, [1,3],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv1', bn_decay=bn_decay)#使用64个大小为[1,1]的卷积核得到64个特征通道net = tf_util.conv2d(net, 64, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv2', bn_decay=bn_decay)#最后得到net为[batch_size, point_num, 1, 64]#第二次使用T-Net进行转换with tf.variable_scope('transform_net2') as sc:transform = feature_transform_net(net, is_training, bn_decay, K=64)end_points['transform'] = transform		#在字典中用transform保存feature transform,记录原始特征#squeeze操作删除大小为1的维度#注意到之前操作之后得到的net为[batch_size, num_point, 1, 64]#从维度0开始,这里指定为第二个维度,squeeze之后为[batch_size, num_point, 64]#然后相乘得到变换后的矩阵net_transformed = tf.matmul(tf.squeeze(net, axis=[2]), transform)#指定第二个维度并膨胀为4维张量[batch_size, num_point, 1, 64]net_transformed = tf.expand_dims(net_transformed, [2])#进行3次卷积操作,最后得到[batch_size, num_point, 1, 1024]net = tf_util.conv2d(net_transformed, 64, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv3', bn_decay=bn_decay)net = tf_util.conv2d(net, 128, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv4', bn_decay=bn_decay)net = tf_util.conv2d(net, 1024, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv5', bn_decay=bn_decay)# Symmetric function: max pooling#max pooling,扫描的模板大小为[num_point, 1],也就是每一个特征通道仅保留一个feature#完成该池化操作之后的net表示为[batch_size, 1, 1, 1024]net = tf_util.max_pool2d(net, [num_point,1],padding='VALID', scope='maxpool')#重塑张量得到[batch_size, 1024]net = tf.reshape(net, [batch_size, -1])#3次全连接并且进行2次dropout#最后得到40种分类结果net = tf_util.fully_connected(net, 512, bn=True, is_training=is_training,scope='fc1', bn_decay=bn_decay)net = tf_util.dropout(net, keep_prob=0.7, is_training=is_training,scope='dp1')net = tf_util.fully_connected(net, 256, bn=True, is_training=is_training,scope='fc2', bn_decay=bn_decay)net = tf_util.dropout(net, keep_prob=0.7, is_training=is_training,scope='dp2')net = tf_util.fully_connected(net, 40, activation_fn=None, scope='fc3')#return返回分类结果以及n*64的原始特征return net, end_points

分割网络结构

在PointNet原网络结构图中从global_feature中向下的分支,即Segmentation Network。对应文件为“pointnet-master\models\pointnet_seg.py”。

由于结构与分类网络类似,这里仅简单注释一下。

def get_model(point_cloud, is_training, bn_decay=None):""" Classification PointNet, input is BxNx3, output BxNx50 """batch_size = point_cloud.get_shape()[0].valuenum_point = point_cloud.get_shape()[1].valueend_points = {}#第一次T-Net转换,转换完成之后膨胀为四维张量with tf.variable_scope('transform_net1') as sc:transform = input_transform_net(point_cloud, is_training, bn_decay, K=3)point_cloud_transformed = tf.matmul(point_cloud, transform)input_image = tf.expand_dims(point_cloud_transformed, -1)#两次卷积,完成后得到[batch_size, num_point, 1, 64]net = tf_util.conv2d(input_image, 64, [1,3],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv1', bn_decay=bn_decay)net = tf_util.conv2d(net, 64, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv2', bn_decay=bn_decay)#第二次T-Net转换,转换过程中注意维度的变换操作with tf.variable_scope('transform_net2') as sc:transform = feature_transform_net(net, is_training, bn_decay, K=64)end_points['transform'] = transformnet_transformed = tf.matmul(tf.squeeze(net, axis=[2]), transform)point_feat = tf.expand_dims(net_transformed, [2])print(point_feat)#3次卷积操作和1次最大池化操作#最后得到[batch_size, 1, 1, 1024]net = tf_util.conv2d(point_feat, 64, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv3', bn_decay=bn_decay)net = tf_util.conv2d(net, 128, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv4', bn_decay=bn_decay)net = tf_util.conv2d(net, 1024, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv5', bn_decay=bn_decay)global_feat = tf_util.max_pool2d(net, [num_point,1],padding='VALID', scope='maxpool')print(global_feat)#未进行性tile之前为[batch_size, 1, 1, 1024]#tile()函数用来对张量进行扩展,对维度1复制拓展至num_point倍#tile()操作之后为[batch_size, num_point, 1, 1024]global_feat_expand = tf.tile(global_feat, [1, num_point, 1, 1])#tf.concat()将两个矩阵进行拼接,拼接得到网络结构中的n×1088的矩阵,n即num_point,此处忽略batch_sizeconcat_feat = tf.concat(3, [point_feat, global_feat_expand])print(concat_feat)#5次卷积操作得到128个特征通道输出 [batch_size, num_point, 1, 128]net = tf_util.conv2d(concat_feat, 512, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv6', bn_decay=bn_decay)net = tf_util.conv2d(net, 256, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv7', bn_decay=bn_decay)net = tf_util.conv2d(net, 128, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv8', bn_decay=bn_decay)net = tf_util.conv2d(net, 128, [1,1],padding='VALID', stride=[1,1],bn=True, is_training=is_training,scope='conv9', bn_decay=bn_decay)#max_pooling操作得到50个特征输出#[batch_size, num_point, 1, 50]net = tf_util.conv2d(net, 50, [1,1],padding='VALID', stride=[1,1], activation_fn=None,scope='conv10')#squeeze()删除大小为1的第二个维度net = tf.squeeze(net, [2]) # BxNxCreturn net, end_points

以上便是此次全部代码解释内容,有错误请及时留言告知


http://chatgpt.dhexx.cn/article/xnmxav5r.shtml

相关文章

PointNet++训练自己的数据集(附源码)

本文针对PointNet强大的三维点云分类功能,详细讲解怎么训练自己的数据集,在此之前,需要确保已经能够跑通源码的训练和测试,如果没有,请参考PointNet的源码运行。 数据放置 1.1. 在mytensor_shape_names.txt中配置自己的…

PointNet网络详解+分类解读

PointNet设想的由来 说到如何设计PointNet网络的,那我们首先就要从输入数据的特性说起。点云数据是一种不规则的数据,在空间上和数量上可以任意分布,由于其特性而不能直接适用于传统CNN。以往的研究者想出了很多种处理点云方式: …

PointNet论文及代码详细解析

基本都是搬运的知乎刘昕宸大佬的文章,自己也是想记录学习一下 原文链接: https://zhuanlan.zhihu.com/p/264627148 这篇论文的题目是:PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation 首先回答三个问题作为引…

3D点云(3D point cloud)及PointNet、PointNet++

文章目录 一、什么是3D点云二、基于3D点云的一些任务三、如何提取3D点云数据的特征:PointNet(1)在PointNet之前也有工作在做点云上的深度学习(2)PointNet(1)置换不变性(Permutation …

PointNet++:Deep Hierarchical Feature Learning on Point Sets in a Metric Space

在上一篇文章中,提及了3D点云分类与分割的开山鼻祖——PointNet:https://blog.csdn.net/Alkaid2000/article/details/127253473,但是这篇PointNet是存在有很多不足之处的,在文章的末尾也提及了,它没有能力捕获局部结构…

Pointnet系列(二)Pointnet++

简介 作者在先前的研究中提出了Pointnet,此论文是Pointnet的改进版Pointnet。提出改进的理由是因为Pointnet无法很好地捕捉由度量空间引起的局部结构问题,由此限制了网络对精细场景的识别以及对复杂场景的泛化能力。 Pointnet的基本思想是对输入点云中…

PointNet++详解与代码

在之前的一篇文章《PointNet:3D点集分类与分割深度学习模型》中分析了PointNet网络是如何进行3D点云数据分类与分割的。但是PointNet存在的一个缺点是无法获得局部特征,这使得它很难对复杂场景进行分析。在PointNet中,作者通过两个主要的方法…

【3D计算机视觉】从PointNet到PointNet++理论及pytorch代码

从PointNet到PointNet理论及代码详解 1. 点云是什么1.1 三维数据的表现形式1.2 为什么使用点云1.3 点云上以往的相关工作 2. PointNet2.1 基于点云的置换不变性2.1.1 由对称函数到PointNet(vanilla)2.1.2 理论证明 2.2 基于点云的旋转不变性2.3 网络总体结构2.4 实验结果和网络…

Pointnet网络结构与代码解读

前言 Pointnet开创性地将深度学习直接用于三维点云任务。由于点云数据的无序性,无法直接对原始点云使用卷积等操作。Pointnet提出对称函数来解决点的无序性问题,设计了能够进行分类和分割任务的网络结构,本文结合源码与个人的理解对于T-net网…

PointNet++

[NIPS 2017]PointNet: Deep Hierarchical Feature Learning onPoint Sets in a Metric Space 语雀(原文内容多一点,CSDN导不进来) [论文地址][项目页面][GitHub] 在之前的文章中分析了PointNet网络是如何进行3D点云数据分类与分割的。但是P…

一文搞懂PointNet全家桶——强势的点云处理神经网络

作者:黎国溥,3D视觉开发者社区签约作者,CSDN博客专家,华为云-云享专家 首发:公众号【3D视觉开发者社区】 前言 PointNet是由斯坦福大学的Charles R. Qi等人在《PointNet:Deep Learning on Point Sets for 3D Classifi…

PointNet理解(PointNet实现第4步)

PointNet第4步——PointNet理解 前面,我们讲到了点云的挑战,针对点云的挑战,PointNet论文提出了下面的解决方案。 下面用到的PPT来源于PointNet作者本人,不得不说大佬还是大佬,介绍也十分清晰,下面附上祁芮…

Pointnet/Pointnet++学习

一、点云的应用 二、点云的表述 三、Pointnet 四、Pointnet Pointnet概述 虽然这篇文章叫PointNet,但和PointNet相比还是有很大的改进。文章非常核心的一点就是提出了多层次特征提取结构。具体来说就是先在输…

PointNet介绍

论文:PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation 代码:https://github.com/charlesq34/pointnet 0 引言 PointNet是处理点云数据的深度学习模型,其地位堪比2D图像处理中的CNN网络, 后续的诸…

三维深度学习之pointnet系列详解(一)

目前二维深度学习取得了很大的进步并且应用范围越来越广,随着三维设备的发展,三维深度学习得到了很大的关注。 最近接触了三维深度学习方面的研究,从pointnet入手,对此有了一点点了解希望记录下来并分享,若有误希望指…

综述|PointNet、PointNet++、 F-PointNet基于深度学习的3D点云分类和分割

点击下方卡片,关注计算机视觉工坊公众号 干货第一时间送达 作者:黎国溥,3D视觉开发者社区签约作者,CSDN博客专家,华为云-云享专家。 编辑:3D视觉开发者社区 前言 PointNet是由斯坦福大学的Charles R. …

PointNet++分类与分割详解

前言 PointNet是一个用于对不规则形状的点云数据进行分类和分割任务的深度神经网络。相对于传统的基于网格的3D数据表示方法,点云数据更易于获取和处理。PointNet的另一个优势是它引入了多尺度层次结构,可以处理更为复杂的点云数据。相比于第一版的Point…

【点云分类和分割】简述PointNet和PointNet++的理解

Hello大家好,最近阅读了PointNet和PointNet两篇论文,本人觉得这是点云方向入手的比较简单的入门论文,下面阐述一下自己对这两篇论文的理解 一、首先点云是非常重要的三维数据结构,但是其有着非常特殊的性质,不规则性和…

PointNet解读

PointNet解决的问题: 如上图所示: 1.点云图像的分类(整片点云是什么物体) 2.点云图像的部件分割(整片点云所代表的物体能拆分的结构) 3.点云图像的语义分割(将三维点云环境中不同的物体用不同…

基于深度学习方法的点云算法3——PointNet++(点云分类分割)

基于深度学习方法的点云算法3——PointNet(点云分类分割) 请点点赞,会持续更新!!! 基于深度学习方法的点云算法1——PointNetLK(点云配准) 基于深度学习方法的点云算法2——PointNet…