PointNet代码详解

2023-05-16

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 image
        Return:
            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]大小的transform
    with 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 BxNx1xK
        Return:
            Transformation matrix of size KxK """
    batch_size = inputs.get_shape()[0].value
    num_point = inputs.get_shape()[1].value

    net = 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_tranform
    point_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].value
    num_point = point_cloud.get_shape()[1].value
    end_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'] = transform
    net_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_size
    concat_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]) # BxNxC

    return net, end_points

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PointNet代码详解 的相关文章

  • 使用Ajax以及CSS+DIV高仿谷歌搜索(附源码下载)

    在使用 Google 搜索或者是 Baidu 搜索的时候 xff0c 在输入搜索关键字的同时 xff0c 会自动弹出匹配的其他关键字的提示 xff0c 全心全意为人民服务的精神在这里崭露无遗 这种利用 Ajax 技术实现输入提示和自动完成的
  • 导致索引失效的可能情况

    如下是可能导致索引失效的情况 xff1a 1 xff0e 隐式转换导致索引失效 这一点应当引起重视 也是开发中经常会犯的错误 由于表的字段tu mdn定义为varchar2 20 但在查询时把该字段作为number类型以where条件传给O
  • 二叉搜索树的增删查

    今天把搜索二叉树的思路又理了一遍 xff0c 把代码又从头到尾敲了一遍 xff0c 各位看客就不要在意代码粗糙和内存溢出了 xff0c 主要把插入和删除的过程理了一遍 xff0c 其中比较复杂的地方就是搜索二叉树的删除 xff0c 涉及了很
  • 中缀表达式转前缀和后缀表达式

    之前笔试中国电信IT研发中心的时候 xff0c 遇到了几个前 中 后缀表达式的相互转换 xff0c 当时忘得差不多了 xff0c 今天好好把该方面的知识好好复习 xff0c 并把相关代码和思路自己缕了一遍 xff1a 将中缀表达式转换为前缀
  • java prometheus 自定义exporter开发,以及实现多个接口返回metrics

    普罗 自定义exporter开发 exporter的作用是采集需要监控的数据 xff0c 并将采集到的数据转换成prometheus所需要的数据格式 xff0c 将这些转换后的数据返回 xff0c 供给prometheus 使用 java

随机推荐

  • 双系统重装Ubuntu经验分享

    真的很喜欢ubuntu 但又没有恒心把它学通透 xff0c 毕竟不是相关专业 第一次重装是因为没多少经验 xff0c 安装qqforlinux的时候多了两个东西 xff0c 还自己生成了快捷方式 xff0c 就想点开看看是啥 xff0c 结
  • 还在迷茫不知Dashboard是什么?答案在文中揭晓

    Dashboard的中文翻译是 仪表盘 xff0c 与汽车的仪表盘相同 一种反映车辆各系统工作状况的装置 xff0c 有车速里程表 转速表 燃油表等 司机可以很方便地从汽车仪表盘中获得汽车整体状况 而Dashboard沿袭了汽车仪表盘理念
  • 问题:UPDATE 失败,因为下列 SET 选项的设置不正确: 'ARITHABORT'。

    解决方案 1 你可以在TSQL前Set ARITHABORT ON 代码如下 Set ARITHABORT ON GO INSERT INTO ta 2 在ADO NET中 你可以这样来写 C 代码 MyConnection Execute
  • 智能制造:三体智能革命

    赵敏 宁振波 郭朝晖是走向智能研究院资深专家 xff0c 三体智能革命 编委会中三位重要作者 他们从去年5月起多次参加了中国工程院主持的 中国智能制造发展战略研究报告 的研讨 评审与修订工作 xff0c 对该报告的形成过程 研究主旨和详细内
  • 小觅相机SDK samples安装Link error: cannot find -lvtkproj4

    Link error cannot find lvtkproj4 error ld returned 1 exit status 找不到相关动态库文件 设置软链接 xff1a ln s usr lib x86 64 linux gnu li
  • 年度回忆录(2011.12----2012.09)

    前几天刚刚参加了提高班十期的开学典礼 xff0c 最近师院的新生也陆 陆续 续的开始报道了 每年到这个时候都会感慨 年年岁岁花相似 xff0c 年年岁岁人不同 啊 对于提高班来说每年都有新的血液注入进来 xff0c 提高班的队伍在不断的扩大
  • python函数(变量,参数)

    函数的变量 1 xff0c 全局变量 定义在最外层的变量 xff0c 对于所有的内函数都能调用 2 xff0c 局部变量 定义在函数内的变量叫做局部变量 xff0c 在函数外是不能访问局部变量 注 xff1a 全局变量不能直接在函数内部进行
  • 程序员读书和练习的方法(个人观点)

    lt 传送门 gt 针对本文的交流探讨 gt 总宗旨 xff1a 打好计算机通用理论基础 通用实战能力 xff0c 便于需要时对各领域的无障碍深钻 时间宝贵 xff0c 不要为了学习而学习 计算机通用理论基础 xff1a 计算机各领域理论基
  • 从零开始的Ubuntu 16.04下PX4编译环境的搭建

    近来入手了一块pixhawk xff0c 想进行一些基于已有代码的二次加工 xff0c 于是到官网https dev px4 io 上看教程 官网上的教程是分为中文 英文以及韩文的版本 很多人肯定第一反应就是看中文的版本 但是这样做弊端真的
  • 驱动程序开发:SPI设备驱动

    目录 Linux下SPI驱动简介SPI架构概述SPI适配器 xff08 控制器 xff09 SPI设备驱动spi driver注册示例SPI 设备和驱动匹配过程编写imc20608六轴传感器SPI驱动设备树编写操作具体的imc20608驱动
  • 操作系统知识点(二)

    文章目录 内存管理程序执行过程内存保护 连续分配非连续分配基本分页存储管理方式基本分段存储管理方式段页式存储管理方式 虚拟内存局部性原理请求分页存储管理 内存管理 内存管理 Memory Management 是操作系统设计中最重要和最复杂
  • VR行业发展的前景和现状?

    标题 VR行业发展的前景和现状 xff1f 1 一个新事物的产生 xff0c 总是伴随着看好和唱衰两种声音 这两种态度自然有其可以理解的地方 xff0c 因为摆在我们面前的是未知 xff0c 而坐在餐桌前的两拨人 xff0c 站在不同的角度
  • 头文件与库的区别

    昨天突然问了一下什么是头文件 xff0c 我一听就傻了 xff0c 虽然上课的时候老师在讲编译的四个过程的时候说了一下 xff0c 但是还是不太理解 xff0c 我们知道编译过程中的预处理阶段会进行头文件展开 xff0c 宏替换以及条件编译
  • 进程、线程

    线程 xff08 thread xff09 线程其实是操作系统能够进行运算调度的最小单位 它是被包含在进程之中的 xff0c 是进程中的实际运作单位 一条线程指的是进程中一个单一顺序的控制流 xff0c 一个进程中可以并发多个线程 xff0
  • 基于Zynq7020双千兆以太网的数字信号处理板设计

    一 背景 背景 Xilinx公司在2010年发布了可扩展的处理器平台Zynq7000系列 xff0c 它采用了28nm工艺 xff0c 将FPGA与ARM cortex A9集成在一颗芯片上 xff0c 实现了高性能 高集成度 低功耗 Zy
  • 深入理解JS中的变量作用域

    在 JS 当中一个变量的作用域 xff08 scope xff09 是程序中定义这个变量的区域 变量分为两类 xff1a 全局 xff08 global xff09 的和局部的 其中全局变量的作用域是全局性的 xff0c 即在 JavaSc
  • 硬件工程师,从零开始无人机开发。

    毕业已经五年了 xff0c 一直在杭州某大厂 xff0c 做无人机硬件开发 无人机这块 xff0c 我进去的时候大厂刚开始 做 xff0c 有幸参与到整个无人机的硬件开发 我这个刚毕业的技术小白 xff0c 在这五年间成长了很多 无奈 今年
  • 个人总结:板球控制系统之串级PID整定方法,速度环与位置环,40S任务10S完成

    其实单环我们先出了所有题目 xff0c 但是效果显然没有串级PID的效果好 xff0c 有人需要的话可以把程序包发出来 xff0c 板球运行视屏也有 另外 xff1a 天下舵机参差不齐 xff08 哪怕型号相同 xff09 xff0c 想要
  • 树莓派3B+踩坑记录:一、安装Ubuntu Mate

    树莓派3B 43 踩坑记录 xff1a 一 安装Ubuntu Mate 树莓派 xff0c Ubuntu xff0c ROS硬件准备软件准备系统烧录安装Ubuntu Mate更换国内源网络配置开启ssh远程其他彩虹屏解决方案XShell和X
  • PointNet代码详解

    PointNet代码详解 最近在做点云深度学习的机器人抓取 xff0c 这篇博客主要是把近期学习PointNet的一些总结的知识点汇总一下 PointNet概述详见以下网址和博客 xff0c 这里也就不再赘述了 三维深度学习之pointne