三維點雲網絡PointNet——模型及代碼分析


PointNet架構

PointNet主要架構如下圖所示:
pointNet
主要包含了點雲對齊/轉換、mpl學習、最大池化得到全局特征三個主要的部分。

-T-Net用於將不同旋轉平移的原始點雲和點雲特征進行規范化;

  • mpl是多層感知機,n個共享的mpl用於處理n個點/特征
  • max pooling 用於融合多個特征並得到全局的1024維的特征;
  • 最后根據任務的不同,利用一個MPL實現分類;結合局部信息利用多個mpl實現分割
    haha|left
1.T-Net

首先我們來看T-Net模型的代碼,它的主要作用是學習出變化矩陣來對輸入的點雲或特征進行規范化處理。其中包含兩個函數,分別是
學習點雲變換矩陣的:input_transform_net(point_cloud, is_training, bn_decay=None, K=3)
學習特征變換矩陣的:feature_transform_net(inputs, is_training, bn_decay=None, K=64)

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 = point_cloud.get_shape()[0].value num_point = point_cloud.get_shape()[1].value input_image = tf.expand_dims(point_cloud, -1) #轉為4D張量 #構建T-Net模型,64--128--1024 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) 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') #利用1024維特征生成256維度的特征 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) 

mpl網絡的定義如上,其輸入為點雲數據,每一個點雲作為一個batch。

  • 首先將三通道的點雲拓展為4-D的張量,tf.expend_dims(),ref,將得到batchn3*1的數據作為網絡的輸入;
  • 隨后構建網絡,利用1*1的卷積來實現全連接。每一層單元依次為64-128-1024-512-256的網絡結構;
    在這里插入圖片描述
    接下來需要將mpl得到的256維度特征進行處理,以輸出3*3的旋轉矩陣:
 #生成點雲旋轉矩陣 T=3*3 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) transform = tf.reshape(transform, [batch_size, 3, K]) return transform 

通過定義權重[W(256,3*K), bais(3*K)],將上面的256維特征轉變為3*3旋轉矩陣輸出。
haha|left
同樣對於特征層的規范化處理,其輸入為n*64的特征輸出為64*64的旋轉矩陣,網絡結構與上面完全相同,只是在輸入輸出的維度需要變化:

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 #構建T-Net模型,64--128--1024 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) #生成特征旋轉矩陣 T=64*64 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 

在這里插入圖片描述
mpl網絡定義每一層的神經元數量為64--128--512--256。同樣在得到256維的特征后利用weight(256*K*K), bais(K*K)來計算出K*K特征旋轉矩陣,其中K為64,為默認輸出特征數量。
代碼鏈接

2.MPL處理點雲

在得到點雲的規范化選擇矩陣后,將原始輸入進行處理。旋轉后的點雲point_cloud_transformed作為MPL的輸入抽取特征。
在這里插入圖片描述
此時輸入是旋轉后的點雲,並通過一個兩層的mpl(64--64)得到了64維的特征;

 transform = input_transform_net(point_cloud, is_training, bn_decay, K=3) # 預測出旋轉矩陣T point_cloud_transformed = tf.matmul(point_cloud, transform) #原始點雲乘以旋轉矩陣,得到矯正后點雲 input_image = tf.expand_dims(point_cloud_transformed, -1) #構建感知機 兩層64--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) 

隨后利用特征旋轉矩陣transform對特征進行規范化處理,得到校正后的特征。此時的新特征net_transformed將輸入到下一個MPL中進行處理

 with tf.variable_scope('transform_net2') as sc: transform = feature_transform_net(net, is_training, bn_decay, K=64) #預測出旋轉矩陣T end_points['transform'] = transform net_transformed = tf.matmul(tf.squeeze(net, axis=[2]), transform) #特征乘以旋轉矩陣,得到矯正后特征 net_transformed = tf.expand_dims(net_transformed, [2]) 
3.MPL處理特征

對於處理點雲后的特征需要利用另一個多層感知機進行處理,將表示每個點的輸入64維的向量表示為1024維輸出:
在這里插入圖片描述

 #構建mpl 64-128-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) 

這部分包含三層mpl(64--128--1024),最終輸出n*1024維度的特征矩陣.
haha|left

4.Max Pooling(對稱函數)得到全局特征

此時每個輸入點從三維變成了1024維的表示,此時需要對n個點所描述的點雲進行融合處理以得到全局特征,源碼中使用了最大池化層來實現這一功能:
在這里插入圖片描述

 # Symmetric function: max pooling # 最大池化,二維的池化函數對點雲中點的數目這個維度進行池化,n-->1 net = tf_util.max_pool2d(net, [num_point,1], padding='VALID', scope='maxpool') 

輸出為全局特征[num_point,1]表示將每一個點雲的n個點最大池化為1個特征,這個特征的長度為1024。此時通過了兩次mpl的處理將一個點雲的特征逐點進行描述,並合並到了1024維的全局特征上來。
haha|left

5.分類

利用上面的1024維特征,就可以基於這一特征對點雲的特性進行學習實現分類任務,PointNet利用了一個三層感知機MPL(512--256--40)來對特征進行學習,最終實現了對於40類的分類.
在這里插入圖片描述

 net = tf.reshape(net, [batch_size, -1]) # 定義分類的mpl512-256-k, k為分類類別數目 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') 

這一感知機由全連接層組成,其中包含了兩個dropout=0.7防止過擬合。最終就可以根據輸出K個分類值分數的大小來確定輸入點雲的分類了。
源碼鏈接
haha|left

6.分割

對於分割任務,需要加入局域信息來進行學習。所以分類任務的輸入包括了1024維的全局信息還包括了n*64的從點雲直接學習出的局部信息。PointNet的做法是將全局信息附在每一個局部點描述的后面,形成了1024+64=1088維的向量,而后通過兩個感知機來進行分割:
在這里插入圖片描述

 global_feat_expand = tf.tile(global_feat, [1, num_point, 1, 1]) concat_feat = tf.concat(3, [point_feat, global_feat_expand]) 

新得到的特征concat_feat輸入兩個連續的感知機mpl(512,256),mpl(128,m)都通過1*1卷積實現:

 # 定義分割的mpl512-256-128 128-m, m為點所屬的類別數目 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) net = tf_util.conv2d(net, 50, [1,1], padding='VALID', stride=[1,1], activation_fn=None, scope='conv10') net = tf.squeeze(net, [2]) # BxNxC 

由於點雲的分割問題可以看做是對於每一個點的分類問題,需要對每一個點的分類進行預測。在通過對全局+局部特征學習后,最后將每一個點分類到50類中,並輸出n*50的輸出。
代碼鏈接
haha|left

論文中最后的效果如下:
在這里插入圖片描述

Full code from: https://github.com/charlesq34/pointnet
插圖來自pics.sc.chinaz.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM