当前位置:   article > 正文

pointNet训练预测自己的数据集Charles版本(一)_pointnet 训练自己的数据集

pointnet 训练自己的数据集

这里跑下作者在github提供的pointNet源码,也会训练和预测下自己的数据集。实际动手看下效果。

论文链接:https://openaccess.thecvf.com/content_cvpr_2017/papers/Qi_PointNet_Deep_Learning_CVPR_2017_paper.pdfhttps://openaccess.thecvf.com/content_cvpr_2017/papers/Qi_PointNet_Deep_Learning_CVPR_2017_paper.pdfgithub源码:

https://github.com/charlesq34/pointnethttps://github.com/charlesq34/pointnet

这边思路还是一样的,什么东西在被自己所用之前,先理解下原作者的代码,多debug下,然后才能更好地吸收。所以此篇先介绍如何跑通源码(分类,语义分割,部件分割都会跑一遍)。

一. 点云分类和预测

1.训练分类网络

所用的环境还是python下的一个虚拟环境,和之前博客中pointNet(Keras实现)那部分实验环境保持一致。工程下载完毕后,用pycharm community版打开, 如下是根目录下的train.py文件,做点云分类训练用。

作者说了,当时是在 Tensorlfow1.x版本下实现的

当前博主用的虚拟环境下的tensorflow版本是2.4.0, 所以要对源代码进行一些修改,才能跑起来,可以参考博主之前的博客进行修改。

tensorflow1.x代码转换到tensorflow2.x_竹叶青lvye的博客-CSDN博客

重点说几点:

1). 将import tensorflow as tf语句修改为如下:

import tensorflow.compat.v1 as tf
tf.compat.v1.disable_eager_execution()

2). 若碰到如下报错

AttributeError: 'int' object has no attribute 'value'

那解决办法是将value属性去掉即可,这里也贴上自己工程的链接。

链接: https://pan.baidu.com/s/1JvFgbW9aAI0akwvZ6w4JlQ 提取码: 4cvb
 

3).initializer =tf.contrib.layers.xavier_initializer()的报错问题,可参考如下论坛回复

python - change tf.contrib.layers.xavier_initializer() to 2.0.0 - Stack Overflow

训练之前要先下载modelnet40_ply_hdf5_2048数据集,github上有该数据集的下载地址,可手动下载。文末有该数据集的介绍,下载完毕后,博主放在如下工程目录下:

 完毕后执行train.py,程序就能进入训练阶段

  1. import tensorflow.compat.v1 as tf
  2. tf.compat.v1.disable_eager_execution()
  3. import argparse
  4. import math
  5. import h5py
  6. import numpy as np
  7. import socket
  8. import importlib
  9. import os
  10. import sys
  11. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  12. sys.path.append(BASE_DIR)
  13. sys.path.append(os.path.join(BASE_DIR, 'models'))
  14. sys.path.append(os.path.join(BASE_DIR, 'utils'))
  15. import provider
  16. import tf_util
  17. parser = argparse.ArgumentParser()
  18. parser.add_argument('--gpu', type=int, default=0, help='GPU to use [default: GPU 0]')
  19. parser.add_argument('--model', default='pointnet_cls', help='Model name: pointnet_cls or pointnet_cls_basic [default: pointnet_cls]')
  20. parser.add_argument('--log_dir', default='log', help='Log dir [default: log]')
  21. parser.add_argument('--num_point', type=int, default=1024, help='Point Number [256/512/1024/2048] [default: 1024]')
  22. parser.add_argument('--max_epoch', type=int, default=250, help='Epoch to run [default: 250]')
  23. parser.add_argument('--batch_size', type=int, default=32, help='Batch Size during training [default: 32]')
  24. parser.add_argument('--learning_rate', type=float, default=0.001, help='Initial learning rate [default: 0.001]')
  25. parser.add_argument('--momentum', type=float, default=0.9, help='Initial learning rate [default: 0.9]')
  26. parser.add_argument('--optimizer', default='adam', help='adam or momentum [default: adam]')
  27. parser.add_argument('--decay_step', type=int, default=200000, help='Decay step for lr decay [default: 200000]')
  28. parser.add_argument('--decay_rate', type=float, default=0.7, help='Decay rate for lr decay [default: 0.8]')
  29. FLAGS = parser.parse_args()
  30. BATCH_SIZE = FLAGS.batch_size
  31. NUM_POINT = FLAGS.num_point
  32. MAX_EPOCH = FLAGS.max_epoch
  33. BASE_LEARNING_RATE = FLAGS.learning_rate
  34. GPU_INDEX = FLAGS.gpu
  35. MOMENTUM = FLAGS.momentum
  36. OPTIMIZER = FLAGS.optimizer
  37. DECAY_STEP = FLAGS.decay_step
  38. DECAY_RATE = FLAGS.decay_rate
  39. MODEL = importlib.import_module(FLAGS.model) # import network module
  40. MODEL_FILE = os.path.join(BASE_DIR, 'models', FLAGS.model+'.py')
  41. LOG_DIR = FLAGS.log_dir
  42. if not os.path.exists(LOG_DIR): os.mkdir(LOG_DIR)
  43. os.system('cp %s %s' % (MODEL_FILE, LOG_DIR)) # bkp of model def
  44. os.system('cp train.py %s' % (LOG_DIR)) # bkp of train procedure
  45. LOG_FOUT = open(os.path.join(LOG_DIR, 'log_train.txt'), 'w')
  46. LOG_FOUT.write(str(FLAGS)+'\n')
  47. MAX_NUM_POINT = 2048
  48. NUM_CLASSES = 40
  49. BN_INIT_DECAY = 0.5
  50. BN_DECAY_DECAY_RATE = 0.5
  51. BN_DECAY_DECAY_STEP = float(DECAY_STEP)
  52. BN_DECAY_CLIP = 0.99
  53. HOSTNAME = socket.gethostname()
  54. # ModelNet40 official train/test split
  55. TRAIN_FILES = provider.getDataFiles( \
  56. os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/train_files.txt'))
  57. TEST_FILES = provider.getDataFiles(\
  58. os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/test_files.txt'))
  59. def log_string(out_str):
  60. LOG_FOUT.write(out_str+'\n')
  61. LOG_FOUT.flush()
  62. print(out_str)
  63. def get_learning_rate(batch):
  64. learning_rate = tf.train.exponential_decay(
  65. BASE_LEARNING_RATE, # Base learning rate.
  66. batch * BATCH_SIZE, # Current index into the dataset.
  67. DECAY_STEP, # Decay step.
  68. DECAY_RATE, # Decay rate.
  69. staircase=True)
  70. learning_rate = tf.maximum(learning_rate, 0.00001) # CLIP THE LEARNING RATE!
  71. return learning_rate
  72. def get_bn_decay(batch):
  73. bn_momentum = tf.train.exponential_decay(
  74. BN_INIT_DECAY,
  75. batch*BATCH_SIZE,
  76. BN_DECAY_DECAY_STEP,
  77. BN_DECAY_DECAY_RATE,
  78. staircase=True)
  79. bn_decay = tf.minimum(BN_DECAY_CLIP, 1 - bn_momentum)
  80. return bn_decay
  81. def train():
  82. with tf.Graph().as_default():
  83. with tf.device('/gpu:'+str(GPU_INDEX)):
  84. pointclouds_pl, labels_pl = MODEL.placeholder_inputs(BATCH_SIZE, NUM_POINT)
  85. is_training_pl = tf.placeholder(tf.bool, shape=())
  86. print(is_training_pl)
  87. # Note the global_step=batch parameter to minimize.
  88. # That tells the optimizer to helpfully increment the 'batch' parameter for you every time it trains.
  89. batch = tf.Variable(0)
  90. bn_decay = get_bn_decay(batch)
  91. tf.summary.scalar('bn_decay', bn_decay)
  92. # Get model and loss
  93. pred, end_points = MODEL.get_model(pointclouds_pl, is_training_pl, bn_decay=bn_decay)
  94. loss = MODEL.get_loss(pred, labels_pl, end_points)
  95. tf.summary.scalar('loss', loss)
  96. correct = tf.equal(tf.argmax(pred, 1), tf.to_int64(labels_pl))
  97. accuracy = tf.reduce_sum(tf.cast(correct, tf.float32)) / float(BATCH_SIZE)
  98. tf.summary.scalar('accuracy', accuracy)
  99. # Get training operator
  100. learning_rate = get_learning_rate(batch)
  101. tf.summary.scalar('learning_rate', learning_rate)
  102. if OPTIMIZER == 'momentum':
  103. optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=MOMENTUM)
  104. elif OPTIMIZER == 'adam':
  105. optimizer = tf.train.AdamOptimizer(learning_rate)
  106. train_op = optimizer.minimize(loss, global_step=batch)
  107. # Add ops to save and restore all the variables.
  108. saver = tf.train.Saver()
  109. # Create a session
  110. config = tf.ConfigProto()
  111. config.gpu_options.allow_growth = True
  112. config.allow_soft_placement = True
  113. config.log_device_placement = False
  114. sess = tf.Session(config=config)
  115. # Add summary writers
  116. #merged = tf.merge_all_summaries()
  117. merged = tf.summary.merge_all()
  118. train_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'train'),
  119. sess.graph)
  120. test_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'test'))
  121. # Init variables
  122. init = tf.global_variables_initializer()
  123. # To fix the bug introduced in TF 0.12.1 as in
  124. # http://stackoverflow.com/questions/41543774/invalidargumenterror-for-tensor-bool-tensorflow-0-12-1
  125. #sess.run(init)
  126. sess.run(init, {is_training_pl: True})
  127. ops = {'pointclouds_pl': pointclouds_pl,
  128. 'labels_pl': labels_pl,
  129. 'is_training_pl': is_training_pl,
  130. 'pred': pred,
  131. 'loss': loss,
  132. 'train_op': train_op,
  133. 'merged': merged,
  134. 'step': batch}
  135. for epoch in range(MAX_EPOCH):
  136. log_string('**** EPOCH %03d ****' % (epoch))
  137. sys.stdout.flush()
  138. train_one_epoch(sess, ops, train_writer)
  139. eval_one_epoch(sess, ops, test_writer)
  140. # Save the variables to disk.
  141. if epoch % 10 == 0:
  142. save_path = saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"))
  143. log_string("Model saved in file: %s" % save_path)
  144. def train_one_epoch(sess, ops, train_writer):
  145. """ ops: dict mapping from string to tf ops """
  146. is_training = True
  147. # Shuffle train files
  148. train_file_idxs = np.arange(0, len(TRAIN_FILES))
  149. np.random.shuffle(train_file_idxs)
  150. for fn in range(len(TRAIN_FILES)):
  151. log_string('----' + str(fn) + '-----')
  152. current_data, current_label = provider.loadDataFile(TRAIN_FILES[train_file_idxs[fn]])
  153. current_data = current_data[:,0:NUM_POINT,:]
  154. current_data, current_label, _ = provider.shuffle_data(current_data, np.squeeze(current_label))
  155. current_label = np.squeeze(current_label)
  156. file_size = current_data.shape[0]
  157. num_batches = file_size // BATCH_SIZE
  158. total_correct = 0
  159. total_seen = 0
  160. loss_sum = 0
  161. for batch_idx in range(num_batches):
  162. start_idx = batch_idx * BATCH_SIZE
  163. end_idx = (batch_idx+1) * BATCH_SIZE
  164. # Augment batched point clouds by rotation and jittering
  165. rotated_data = provider.rotate_point_cloud(current_data[start_idx:end_idx, :, :])
  166. jittered_data = provider.jitter_point_cloud(rotated_data)
  167. feed_dict = {ops['pointclouds_pl']: jittered_data,
  168. ops['labels_pl']: current_label[start_idx:end_idx],
  169. ops['is_training_pl']: is_training,}
  170. summary, step, _, loss_val, pred_val = sess.run([ops['merged'], ops['step'],
  171. ops['train_op'], ops['loss'], ops['pred']], feed_dict=feed_dict)
  172. train_writer.add_summary(summary, step)
  173. pred_val = np.argmax(pred_val, 1)
  174. correct = np.sum(pred_val == current_label[start_idx:end_idx])
  175. total_correct += correct
  176. total_seen += BATCH_SIZE
  177. loss_sum += loss_val
  178. log_string('mean loss: %f' % (loss_sum / float(num_batches)))
  179. log_string('accuracy: %f' % (total_correct / float(total_seen)))
  180. def eval_one_epoch(sess, ops, test_writer):
  181. """ ops: dict mapping from string to tf ops """
  182. is_training = False
  183. total_correct = 0
  184. total_seen = 0
  185. loss_sum = 0
  186. total_seen_class = [0 for _ in range(NUM_CLASSES)]
  187. total_correct_class = [0 for _ in range(NUM_CLASSES)]
  188. for fn in range(len(TEST_FILES)):
  189. log_string('----' + str(fn) + '-----')
  190. current_data, current_label = provider.loadDataFile(TEST_FILES[fn])
  191. current_data = current_data[:,0:NUM_POINT,:]
  192. current_label = np.squeeze(current_label)
  193. file_size = current_data.shape[0]
  194. num_batches = file_size // BATCH_SIZE
  195. for batch_idx in range(num_batches):
  196. start_idx = batch_idx * BATCH_SIZE
  197. end_idx = (batch_idx+1) * BATCH_SIZE
  198. feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
  199. ops['labels_pl']: current_label[start_idx:end_idx],
  200. ops['is_training_pl']: is_training}
  201. summary, step, loss_val, pred_val = sess.run([ops['merged'], ops['step'],
  202. ops['loss'], ops['pred']], feed_dict=feed_dict)
  203. pred_val = np.argmax(pred_val, 1)
  204. correct = np.sum(pred_val == current_label[start_idx:end_idx])
  205. total_correct += correct
  206. total_seen += BATCH_SIZE
  207. loss_sum += (loss_val*BATCH_SIZE)
  208. for i in range(start_idx, end_idx):
  209. l = current_label[i]
  210. total_seen_class[l] += 1
  211. total_correct_class[l] += (pred_val[i-start_idx] == l)
  212. log_string('eval mean loss: %f' % (loss_sum / float(total_seen)))
  213. log_string('eval accuracy: %f'% (total_correct / float(total_seen)))
  214. log_string('eval avg class acc: %f' % (np.mean(np.array(total_correct_class)/np.array(total_seen_class,dtype=np.float))))
  215. if __name__ == "__main__":
  216. train()
  217. LOG_FOUT.close()

这里有个小插曲,即博主在cmd终端train时候,训练的很快,在pycharm中训练的很慢,发现是因为pycharm环境变量中并没有修正最新的cuda的路径,而导致跑在gpu上异常,模型训练实际是跑在cpu上的,所以这里需要结合自己的路径重新配置下。

 是否跑在gpu上可用如下语句来验证

  1. import tensorflow as tf
  2. print(tf.test.is_gpu_available())

时间有限,博主这边只迭代训练了80次。如下语句可以通过tensorboard去查看训练的日志

tensorboard --logdir=/home/sxhlvye/Trial/pointnet-master/log/train --host=127.0.0.1

 然后可以通过网页来查看详情

 Tensorboard使用详情可参考如下博客

Keras相关知识点整理(tensorflow2.4)_with writer.as_default()_竹叶青lvye的博客-CSDN博客

可看到每份点云数据个数和对应每份点云数据的类别,这里共有2048份点云数据,所以label的维度是2048*1。

可看到modelnet40_ply_hdf5中的点云数据是3维的,只含有x,y,z信息

 博主这边改写了下,这样可以在训练之前先加载下预训练的模型,在此基础上做增强学习,代码如下:

  1. import tensorflow.compat.v1 as tf
  2. tf.compat.v1.disable_eager_execution()
  3. import argparse
  4. import math
  5. import h5py
  6. import numpy as np
  7. import socket
  8. import importlib
  9. import os
  10. import sys
  11. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  12. sys.path.append(BASE_DIR)
  13. sys.path.append(os.path.join(BASE_DIR, 'models'))
  14. sys.path.append(os.path.join(BASE_DIR, 'utils'))
  15. import provider
  16. import tf_util
  17. parser = argparse.ArgumentParser()
  18. parser.add_argument('--gpu', type=int, default=0, help='GPU to use [default: GPU 0]')
  19. parser.add_argument('--model', default='pointnet_cls', help='Model name: pointnet_cls or pointnet_cls_basic [default: pointnet_cls]')
  20. parser.add_argument('--log_dir', default='log', help='Log dir [default: log]')
  21. parser.add_argument('--num_point', type=int, default=1024, help='Point Number [256/512/1024/2048] [default: 1024]')
  22. parser.add_argument('--max_epoch', type=int, default=250, help='Epoch to run [default: 250]')
  23. parser.add_argument('--batch_size', type=int, default=32, help='Batch Size during training [default: 32]')
  24. parser.add_argument('--learning_rate', type=float, default=0.001, help='Initial learning rate [default: 0.001]')
  25. parser.add_argument('--momentum', type=float, default=0.9, help='Initial learning rate [default: 0.9]')
  26. parser.add_argument('--optimizer', default='adam', help='adam or momentum [default: adam]')
  27. parser.add_argument('--decay_step', type=int, default=200000, help='Decay step for lr decay [default: 200000]')
  28. parser.add_argument('--decay_rate', type=float, default=0.7, help='Decay rate for lr decay [default: 0.8]')
  29. parser.add_argument('--model_path', default='log/model.ckpt', help='model checkpoint file path [default: log/model.ckpt]')
  30. FLAGS = parser.parse_args()
  31. BATCH_SIZE = FLAGS.batch_size
  32. NUM_POINT = FLAGS.num_point
  33. MAX_EPOCH = FLAGS.max_epoch
  34. BASE_LEARNING_RATE = FLAGS.learning_rate
  35. GPU_INDEX = FLAGS.gpu
  36. MOMENTUM = FLAGS.momentum
  37. OPTIMIZER = FLAGS.optimizer
  38. DECAY_STEP = FLAGS.decay_step
  39. DECAY_RATE = FLAGS.decay_rate
  40. MODEL = importlib.import_module(FLAGS.model) # import network module
  41. MODEL_FILE = os.path.join(BASE_DIR, 'models', FLAGS.model+'.py')
  42. LOG_DIR = FLAGS.log_dir
  43. if not os.path.exists(LOG_DIR): os.mkdir(LOG_DIR)
  44. os.system('cp %s %s' % (MODEL_FILE, LOG_DIR)) # bkp of model def
  45. os.system('cp train.py %s' % (LOG_DIR)) # bkp of train procedure
  46. LOG_FOUT = open(os.path.join(LOG_DIR, 'log_train.txt'), 'w')
  47. LOG_FOUT.write(str(FLAGS)+'\n')
  48. MAX_NUM_POINT = 2048
  49. NUM_CLASSES = 40
  50. BN_INIT_DECAY = 0.5
  51. BN_DECAY_DECAY_RATE = 0.5
  52. BN_DECAY_DECAY_STEP = float(DECAY_STEP)
  53. BN_DECAY_CLIP = 0.99
  54. HOSTNAME = socket.gethostname()
  55. # ModelNet40 official train/test split
  56. TRAIN_FILES = provider.getDataFiles( \
  57. os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/train_files.txt'))
  58. TEST_FILES = provider.getDataFiles(\
  59. os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/test_files.txt'))
  60. def log_string(out_str):
  61. LOG_FOUT.write(out_str+'\n')
  62. LOG_FOUT.flush()
  63. print(out_str)
  64. def get_learning_rate(batch):
  65. learning_rate = tf.train.exponential_decay(
  66. BASE_LEARNING_RATE, # Base learning rate.
  67. batch * BATCH_SIZE, # Current index into the dataset.
  68. DECAY_STEP, # Decay step.
  69. DECAY_RATE, # Decay rate.
  70. staircase=True)
  71. learning_rate = tf.maximum(learning_rate, 0.00001) # CLIP THE LEARNING RATE!
  72. return learning_rate
  73. def get_bn_decay(batch):
  74. bn_momentum = tf.train.exponential_decay(
  75. BN_INIT_DECAY,
  76. batch*BATCH_SIZE,
  77. BN_DECAY_DECAY_STEP,
  78. BN_DECAY_DECAY_RATE,
  79. staircase=True)
  80. bn_decay = tf.minimum(BN_DECAY_CLIP, 1 - bn_momentum)
  81. return bn_decay
  82. def train():
  83. with tf.Graph().as_default():
  84. with tf.device('/gpu:'+str(GPU_INDEX)):
  85. pointclouds_pl, labels_pl = MODEL.placeholder_inputs(BATCH_SIZE, NUM_POINT)
  86. is_training_pl = tf.placeholder(tf.bool, shape=())
  87. print(is_training_pl)
  88. # Note the global_step=batch parameter to minimize.
  89. # That tells the optimizer to helpfully increment the 'batch' parameter for you every time it trains.
  90. batch = tf.Variable(0)
  91. bn_decay = get_bn_decay(batch)
  92. tf.summary.scalar('bn_decay', bn_decay)
  93. # Get model and loss
  94. pred, end_points = MODEL.get_model(pointclouds_pl, is_training_pl, bn_decay=bn_decay)
  95. loss = MODEL.get_loss(pred, labels_pl, end_points)
  96. tf.summary.scalar('loss', loss)
  97. correct = tf.equal(tf.argmax(pred, 1), tf.to_int64(labels_pl))
  98. accuracy = tf.reduce_sum(tf.cast(correct, tf.float32)) / float(BATCH_SIZE)
  99. tf.summary.scalar('accuracy', accuracy)
  100. # Get training operator
  101. learning_rate = get_learning_rate(batch)
  102. tf.summary.scalar('learning_rate', learning_rate)
  103. if OPTIMIZER == 'momentum':
  104. optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=MOMENTUM)
  105. elif OPTIMIZER == 'adam':
  106. optimizer = tf.train.AdamOptimizer(learning_rate)
  107. train_op = optimizer.minimize(loss, global_step=batch)
  108. # Add ops to save and restore all the variables.
  109. saver = tf.train.Saver()
  110. # Create a session
  111. config = tf.ConfigProto()
  112. config.gpu_options.allow_growth = True
  113. config.allow_soft_placement = True
  114. config.log_device_placement = True
  115. sess = tf.Session(config=config)
  116. # Add summary writers
  117. #merged = tf.merge_all_summaries()
  118. merged = tf.summary.merge_all()
  119. train_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'train'),
  120. sess.graph)
  121. test_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'test'))
  122. # Init variables
  123. init = tf.global_variables_initializer()
  124. # To fix the bug introduced in TF 0.12.1 as in
  125. # http://stackoverflow.com/questions/41543774/invalidargumenterror-for-tensor-bool-tensorflow-0-12-1
  126. #sess.run(init)
  127. sess.run(init, {is_training_pl: True})
  128. saver.restore(sess, FLAGS.model_path)
  129. log_string("Model restored.")
  130. config.log_device_placement = False
  131. ops = {'pointclouds_pl': pointclouds_pl,
  132. 'labels_pl': labels_pl,
  133. 'is_training_pl': is_training_pl,
  134. 'pred': pred,
  135. 'loss': loss,
  136. 'train_op': train_op,
  137. 'merged': merged,
  138. 'step': batch}
  139. for epoch in range(MAX_EPOCH):
  140. log_string('**** EPOCH %03d ****' % (epoch))
  141. sys.stdout.flush()
  142. train_one_epoch(sess, ops, train_writer)
  143. eval_one_epoch(sess, ops, test_writer)
  144. # Save the variables to disk.
  145. if epoch % 10 == 0:
  146. save_path = saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"))
  147. log_string("Model saved in file: %s" % save_path)
  148. def train_one_epoch(sess, ops, train_writer):
  149. """ ops: dict mapping from string to tf ops """
  150. is_training = True
  151. # Shuffle train files
  152. train_file_idxs = np.arange(0, len(TRAIN_FILES))
  153. np.random.shuffle(train_file_idxs)
  154. for fn in range(len(TRAIN_FILES)):
  155. log_string('----' + str(fn) + '-----')
  156. current_data, current_label = provider.loadDataFile(TRAIN_FILES[train_file_idxs[fn]])
  157. current_data = current_data[:,0:NUM_POINT,:]
  158. current_data, current_label, _ = provider.shuffle_data(current_data, np.squeeze(current_label))
  159. current_label = np.squeeze(current_label)
  160. file_size = current_data.shape[0]
  161. num_batches = file_size // BATCH_SIZE
  162. total_correct = 0
  163. total_seen = 0
  164. loss_sum = 0
  165. for batch_idx in range(num_batches):
  166. start_idx = batch_idx * BATCH_SIZE
  167. end_idx = (batch_idx+1) * BATCH_SIZE
  168. # Augment batched point clouds by rotation and jittering
  169. rotated_data = provider.rotate_point_cloud(current_data[start_idx:end_idx, :, :])
  170. jittered_data = provider.jitter_point_cloud(rotated_data)
  171. feed_dict = {ops['pointclouds_pl']: jittered_data,
  172. ops['labels_pl']: current_label[start_idx:end_idx],
  173. ops['is_training_pl']: is_training,}
  174. summary, step, _, loss_val, pred_val = sess.run([ops['merged'], ops['step'],
  175. ops['train_op'], ops['loss'], ops['pred']], feed_dict=feed_dict)
  176. train_writer.add_summary(summary, step)
  177. pred_val = np.argmax(pred_val, 1)
  178. correct = np.sum(pred_val == current_label[start_idx:end_idx])
  179. total_correct += correct
  180. total_seen += BATCH_SIZE
  181. loss_sum += loss_val
  182. log_string('total_correct: %d' % total_correct)
  183. log_string('total_seen: %d' % total_seen)
  184. log_string('file size: %d' % file_size)
  185. log_string('mean loss: %f' % (loss_sum / float(num_batches)))
  186. log_string('accuracy: %f' % (total_correct / float(total_seen)))
  187. def eval_one_epoch(sess, ops, test_writer):
  188. """ ops: dict mapping from string to tf ops """
  189. is_training = False
  190. total_correct = 0
  191. total_seen = 0
  192. loss_sum = 0
  193. total_seen_class = [0 for _ in range(NUM_CLASSES)]
  194. total_correct_class = [0 for _ in range(NUM_CLASSES)]
  195. for fn in range(len(TEST_FILES)):
  196. log_string('----' + str(fn) + '-----')
  197. current_data, current_label = provider.loadDataFile(TEST_FILES[fn])
  198. current_data = current_data[:,0:NUM_POINT,:]
  199. current_label = np.squeeze(current_label)
  200. file_size = current_data.shape[0]
  201. num_batches = file_size // BATCH_SIZE
  202. for batch_idx in range(num_batches):
  203. start_idx = batch_idx * BATCH_SIZE
  204. end_idx = (batch_idx+1) * BATCH_SIZE
  205. feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
  206. ops['labels_pl']: current_label[start_idx:end_idx],
  207. ops['is_training_pl']: is_training}
  208. summary, step, loss_val, pred_val = sess.run([ops['merged'], ops['step'],
  209. ops['loss'], ops['pred']], feed_dict=feed_dict)
  210. pred_val = np.argmax(pred_val, 1)
  211. correct = np.sum(pred_val == current_label[start_idx:end_idx])
  212. total_correct += correct
  213. total_seen += BATCH_SIZE
  214. loss_sum += (loss_val*BATCH_SIZE)
  215. for i in range(start_idx, end_idx):
  216. l = current_label[i]
  217. total_seen_class[l] += 1
  218. total_correct_class[l] += (pred_val[i-start_idx] == l)
  219. log_string('eval mean loss: %f' % (loss_sum / float(total_seen)))
  220. log_string('eval accuracy: %f'% (total_correct / float(total_seen)))
  221. log_string('eval avg class acc: %f' % (np.mean(np.array(total_correct_class)/np.array(total_seen_class,dtype=np.float))))
  222. if __name__ == "__main__":
  223. train()
  224. LOG_FOUT.close()

 可看到,训练第一步的第一次批次精度就能到达0.93多了。

 博主有逐步debug过,对其里面的机制还是了解的,感兴趣的童鞋可以自行去分析下,值得去做这样的事情。

2.分类网络预测

博主简化了下evaluate.py文件中的代码(之前的代码里是批量对测试图片进行预测,并统计分析结果),只预测一张图片,并显示预测的图片及结果,代码如下:

  1. import tensorflow.compat.v1 as tf
  2. tf.compat.v1.disable_eager_execution()
  3. import numpy as np
  4. import argparse
  5. import socket
  6. import importlib
  7. import time
  8. import os
  9. import scipy.misc
  10. import sys
  11. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  12. sys.path.append(BASE_DIR)
  13. sys.path.append(os.path.join(BASE_DIR, 'models'))
  14. sys.path.append(os.path.join(BASE_DIR, 'utils'))
  15. import provider
  16. import pc_util
  17. from matplotlib import pyplot as plt
  18. from mpl_toolkits.mplot3d import Axes3D
  19. parser = argparse.ArgumentParser()
  20. parser.add_argument('--gpu', type=int, default=0, help='GPU to use [default: GPU 0]')
  21. parser.add_argument('--model', default='pointnet_cls', help='Model name: pointnet_cls or pointnet_cls_basic [default: pointnet_cls]')
  22. parser.add_argument('--batch_size', type=int, default=1, help='Batch Size during training [default: 1]')
  23. parser.add_argument('--num_point', type=int, default=1024, help='Point Number [256/512/1024/2048] [default: 1024]')
  24. parser.add_argument('--model_path', default='log/model.ckpt', help='model checkpoint file path [default: log/model.ckpt]')
  25. parser.add_argument('--dump_dir', default='dump', help='dump folder path [dump]')
  26. parser.add_argument('--visu', action='store_true', help='Whether to dump image for error case [default: False]')
  27. FLAGS = parser.parse_args()
  28. BATCH_SIZE = FLAGS.batch_size
  29. NUM_POINT = FLAGS.num_point
  30. MODEL_PATH = FLAGS.model_path
  31. GPU_INDEX = FLAGS.gpu
  32. MODEL = importlib.import_module(FLAGS.model) # import network module
  33. DUMP_DIR = FLAGS.dump_dir
  34. if not os.path.exists(DUMP_DIR): os.mkdir(DUMP_DIR)
  35. LOG_FOUT = open(os.path.join(DUMP_DIR, 'log_evaluate.txt'), 'w')
  36. LOG_FOUT.write(str(FLAGS)+'\n')
  37. NUM_CLASSES = 40
  38. SHAPE_NAMES = [line.rstrip() for line in \
  39. open(os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/shape_names.txt'))]
  40. HOSTNAME = socket.gethostname()
  41. # ModelNet40 official train/test split
  42. TRAIN_FILES = provider.getDataFiles( \
  43. os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/train_files.txt'))
  44. TEST_FILES = provider.getDataFiles(\
  45. os.path.join(BASE_DIR, 'data/modelnet40_ply_hdf5_2048/test_files.txt'))
  46. def log_string(out_str):
  47. LOG_FOUT.write(out_str+'\n')
  48. LOG_FOUT.flush()
  49. print(out_str)
  50. def evaluate(num_votes):
  51. is_training = False
  52. with tf.device('/gpu:'+str(GPU_INDEX)):
  53. pointclouds_pl, labels_pl = MODEL.placeholder_inputs(BATCH_SIZE, NUM_POINT)
  54. is_training_pl = tf.placeholder(tf.bool, shape=())
  55. # simple model
  56. pred, end_points = MODEL.get_model(pointclouds_pl, is_training_pl)
  57. loss = MODEL.get_loss(pred, labels_pl, end_points)
  58. # Add ops to save and restore all the variables.
  59. saver = tf.train.Saver()
  60. # Create a session
  61. config = tf.ConfigProto()
  62. config.gpu_options.allow_growth = True
  63. config.allow_soft_placement = True
  64. config.log_device_placement = True
  65. sess = tf.Session(config=config)
  66. # Restore variables from disk.
  67. saver.restore(sess, MODEL_PATH)
  68. log_string("Model restored.")
  69. ops = {'pointclouds_pl': pointclouds_pl,
  70. 'labels_pl': labels_pl,
  71. 'is_training_pl': is_training_pl,
  72. 'pred': pred,
  73. 'loss': loss}
  74. eval_one_epoch(sess, ops, num_votes)
  75. def eval_one_epoch(sess, ops, num_votes=1, topk=1):
  76. error_cnt = 0
  77. is_training = False
  78. total_correct = 0
  79. total_seen = 0
  80. loss_sum = 0
  81. total_seen_class = [0 for _ in range(NUM_CLASSES)]
  82. total_correct_class = [0 for _ in range(NUM_CLASSES)]
  83. fout = open(os.path.join(DUMP_DIR, 'pred_label.txt'), 'w')
  84. current_data, current_label = provider.loadDataFile(TEST_FILES[0])
  85. current_label = np.squeeze(current_label)
  86. # predict a pointcloud
  87. object_index = 1111
  88. current_data = current_data[object_index:object_index+1, 0:NUM_POINT, :]
  89. feed_dict = {ops['pointclouds_pl']: current_data,
  90. ops['labels_pl']: current_label[object_index:object_index+1],
  91. ops['is_training_pl']: is_training}
  92. loss_val, pred_val = sess.run([ops['loss'], ops['pred']], feed_dict=feed_dict)
  93. pred_val = np.argmax(pred_val, 1)
  94. fig = plt.figure(figsize=(15, 10))
  95. ax = fig.add_subplot(1, 1, 1, projection="3d")
  96. ax.scatter(current_data[:,:,0], current_data[:, :, 1], current_data[:, :, 2])
  97. ax.set_title("label: {:}, pred: {:}".format(SHAPE_NAMES[current_label[object_index]], SHAPE_NAMES[pred_val[0]]))
  98. ax.set_axis_off()
  99. plt.show()
  100. if __name__=='__main__':
  101. with tf.Graph().as_default():
  102. evaluate(num_votes=1)
  103. LOG_FOUT.close()

 运行结果如下:

 若object_index设直888,则预测结果如下:

可看到这两份点云都分类预测正确。

附:也可以用Open3D来显示点云,Open3D的使用可参考博主之前的博客:

pycharm配置PyQt5、Open3D、Python-pcl_pyqt5 open3d_竹叶青lvye的博客-CSDN博客

这里博主使用的是open3d_python-0.3.0.0-py2.py3-none-any.whl安装包,python环境是3.6,如下代码来可视化上面ply_data_test0.h5中的数据

  1. import os
  2. import sys
  3. import numpy as np
  4. import h5py
  5. import open3d as o3d
  6. def load_h5(h5_filename):
  7. f = h5py.File(h5_filename)
  8. data = f['data'][:]
  9. label = f['label'][:]
  10. return (data, label)
  11. if __name__ == '__main__':
  12. current_data, current_label = load_h5("ply_data_test0.h5")
  13. pcd = o3d.PointCloud()
  14. object_index = 1111
  15. pcd.points = o3d.Vector3dVector(current_data[object_index:object_index+1,:,:].reshape(-1,3))
  16. o3d.draw_geometries([pcd])

 工程目录结构

运行结果如下:

 如下代码是带指定颜色的(对各点)来显示点云

  1. import os
  2. import sys
  3. import numpy as np
  4. import h5py
  5. import open3d as o3d
  6. def load_h5(h5_filename):
  7. f = h5py.File(h5_filename)
  8. data = f['data'][:]
  9. label = f['label'][:]
  10. return (data, label)
  11. if __name__ == '__main__':
  12. current_data, current_label = load_h5("ply_data_test0.h5")
  13. pcd = o3d.PointCloud()
  14. object_index = 1111
  15. pcd.points = o3d.Vector3dVector(current_data[object_index:object_index+1,:,:].reshape(-1,3))
  16. colors = []
  17. for i in range(2048):
  18. colors.append([0,0,255])
  19. pcd.colors = o3d.Vector3dVector(colors)
  20. o3d.draw_geometries([pcd])

二. 点云分割和预测

1.训练语义分割网络

首先下载训练集,路径可以从sem_seg目录下的download_data.sh中找到,也可以手动到如下网页上下载

https://shapenet.cs.stanford.edu/media/indoor3d_sem_seg_hdf5_data.zip

下载完毕后博主放在工程的如下目录下

 博主修改了sem_seg目录下的train.py(因为显存原因,博主这边对每份点云随机抽取了1024个点云来训练网络),代码如下:

  1. import tensorflow.compat.v1 as tf
  2. tf.compat.v1.disable_eager_execution()
  3. import argparse
  4. import math
  5. import h5py
  6. import numpy as np
  7. import socket
  8. import os
  9. import sys
  10. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  11. ROOT_DIR = os.path.dirname(BASE_DIR)
  12. sys.path.append(BASE_DIR)
  13. sys.path.append(ROOT_DIR)
  14. sys.path.append(os.path.join(ROOT_DIR, 'utils'))
  15. import provider
  16. import tf_util
  17. from model import *
  18. parser = argparse.ArgumentParser()
  19. parser.add_argument('--gpu', type=int, default=0, help='GPU to use [default: GPU 0]')
  20. parser.add_argument('--log_dir', default='log', help='Log dir [default: log]')
  21. parser.add_argument('--num_point', type=int, default=1024, help='Point number [default: 4096]')
  22. parser.add_argument('--max_epoch', type=int, default=50, help='Epoch to run [default: 50]')
  23. parser.add_argument('--batch_size', type=int, default=2, help='Batch Size during training [default: 24]')
  24. parser.add_argument('--learning_rate', type=float, default=0.001, help='Initial learning rate [default: 0.001]')
  25. parser.add_argument('--momentum', type=float, default=0.9, help='Initial learning rate [default: 0.9]')
  26. parser.add_argument('--optimizer', default='adam', help='adam or momentum [default: adam]')
  27. parser.add_argument('--decay_step', type=int, default=300000, help='Decay step for lr decay [default: 300000]')
  28. parser.add_argument('--decay_rate', type=float, default=0.5, help='Decay rate for lr decay [default: 0.5]')
  29. parser.add_argument('--test_area', type=int, default=6, help='Which area to use for test, option: 1-6 [default: 6]')
  30. FLAGS = parser.parse_args()
  31. BATCH_SIZE = FLAGS.batch_size
  32. NUM_POINT = FLAGS.num_point
  33. MAX_EPOCH = FLAGS.max_epoch
  34. NUM_POINT = FLAGS.num_point
  35. BASE_LEARNING_RATE = FLAGS.learning_rate
  36. GPU_INDEX = FLAGS.gpu
  37. MOMENTUM = FLAGS.momentum
  38. OPTIMIZER = FLAGS.optimizer
  39. DECAY_STEP = FLAGS.decay_step
  40. DECAY_RATE = FLAGS.decay_rate
  41. LOG_DIR = FLAGS.log_dir
  42. if not os.path.exists(LOG_DIR): os.mkdir(LOG_DIR)
  43. os.system('cp model.py %s' % (LOG_DIR)) # bkp of model def
  44. os.system('cp train.py %s' % (LOG_DIR)) # bkp of train procedure
  45. LOG_FOUT = open(os.path.join(LOG_DIR, 'log_train.txt'), 'w')
  46. LOG_FOUT.write(str(FLAGS)+'\n')
  47. MAX_NUM_POINT = 4096
  48. NUM_CLASSES = 13
  49. BN_INIT_DECAY = 0.5
  50. BN_DECAY_DECAY_RATE = 0.5
  51. #BN_DECAY_DECAY_STEP = float(DECAY_STEP * 2)
  52. BN_DECAY_DECAY_STEP = float(DECAY_STEP)
  53. BN_DECAY_CLIP = 0.99
  54. HOSTNAME = socket.gethostname()
  55. ALL_FILES = provider.getDataFiles('indoor3d_sem_seg_hdf5_data/all_files.txt')
  56. room_filelist = [line.rstrip() for line in open('indoor3d_sem_seg_hdf5_data/room_filelist.txt')]
  57. # Load ALL data
  58. data_batch_list = []
  59. label_batch_list = []
  60. for h5_filename in ALL_FILES:
  61. data_batch, label_batch = provider.loadDataFile(h5_filename)
  62. data_batch_list.append(data_batch)
  63. label_batch_list.append(label_batch)
  64. data_batches = np.concatenate(data_batch_list, 0)
  65. label_batches = np.concatenate(label_batch_list, 0)
  66. print(data_batches.shape)
  67. print(label_batches.shape)
  68. test_area = 'Area_'+str(FLAGS.test_area)
  69. train_idxs = []
  70. test_idxs = []
  71. for i,room_name in enumerate(room_filelist):
  72. if test_area in room_name:
  73. test_idxs.append(i)
  74. else:
  75. train_idxs.append(i)
  76. train_data = data_batches[train_idxs,...]
  77. train_label = label_batches[train_idxs]
  78. test_data = data_batches[test_idxs,...]
  79. test_label = label_batches[test_idxs]
  80. print(train_data.shape, train_label.shape)
  81. print(test_data.shape, test_label.shape)
  82. def log_string(out_str):
  83. LOG_FOUT.write(out_str+'\n')
  84. LOG_FOUT.flush()
  85. print(out_str)
  86. def get_learning_rate(batch):
  87. learning_rate = tf.train.exponential_decay(
  88. BASE_LEARNING_RATE, # Base learning rate.
  89. batch * BATCH_SIZE, # Current index into the dataset.
  90. DECAY_STEP, # Decay step.
  91. DECAY_RATE, # Decay rate.
  92. staircase=True)
  93. learning_rate = tf.maximum(learning_rate, 0.00001) # CLIP THE LEARNING RATE!!
  94. return learning_rate
  95. def get_bn_decay(batch):
  96. bn_momentum = tf.train.exponential_decay(
  97. BN_INIT_DECAY,
  98. batch*BATCH_SIZE,
  99. BN_DECAY_DECAY_STEP,
  100. BN_DECAY_DECAY_RATE,
  101. staircase=True)
  102. bn_decay = tf.minimum(BN_DECAY_CLIP, 1 - bn_momentum)
  103. return bn_decay
  104. def train():
  105. with tf.Graph().as_default():
  106. with tf.device('/gpu:'+str(GPU_INDEX)):
  107. pointclouds_pl, labels_pl = placeholder_inputs(BATCH_SIZE, NUM_POINT)
  108. is_training_pl = tf.placeholder(tf.bool, shape=())
  109. # Note the global_step=batch parameter to minimize.
  110. # That tells the optimizer to helpfully increment the 'batch' parameter for you every time it trains.
  111. batch = tf.Variable(0)
  112. bn_decay = get_bn_decay(batch)
  113. tf.summary.scalar('bn_decay', bn_decay)
  114. # Get model and loss
  115. pred = get_model(pointclouds_pl, is_training_pl, bn_decay=bn_decay)
  116. loss = get_loss(pred, labels_pl)
  117. tf.summary.scalar('loss', loss)
  118. correct = tf.equal(tf.argmax(pred, 2), tf.to_int64(labels_pl))
  119. accuracy = tf.reduce_sum(tf.cast(correct, tf.float32)) / float(BATCH_SIZE*NUM_POINT)
  120. tf.summary.scalar('accuracy', accuracy)
  121. # Get training operator
  122. learning_rate = get_learning_rate(batch)
  123. tf.summary.scalar('learning_rate', learning_rate)
  124. if OPTIMIZER == 'momentum':
  125. optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=MOMENTUM)
  126. elif OPTIMIZER == 'adam':
  127. optimizer = tf.train.AdamOptimizer(learning_rate)
  128. train_op = optimizer.minimize(loss, global_step=batch)
  129. # Add ops to save and restore all the variables.
  130. saver = tf.train.Saver()
  131. # Create a session
  132. config = tf.ConfigProto()
  133. config.gpu_options.allow_growth = True
  134. config.allow_soft_placement = True
  135. config.log_device_placement = True
  136. sess = tf.Session(config=config)
  137. # Add summary writers
  138. merged = tf.summary.merge_all()
  139. train_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'train'),
  140. sess.graph)
  141. test_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'test'))
  142. # Init variables
  143. init = tf.global_variables_initializer()
  144. sess.run(init, {is_training_pl:True})
  145. ops = {'pointclouds_pl': pointclouds_pl,
  146. 'labels_pl': labels_pl,
  147. 'is_training_pl': is_training_pl,
  148. 'pred': pred,
  149. 'loss': loss,
  150. 'train_op': train_op,
  151. 'merged': merged,
  152. 'step': batch}
  153. for epoch in range(MAX_EPOCH):
  154. log_string('**** EPOCH %03d ****' % (epoch))
  155. sys.stdout.flush()
  156. train_one_epoch(sess, ops, train_writer)
  157. eval_one_epoch(sess, ops, test_writer)
  158. # Save the variables to disk.
  159. if epoch % 10 == 0:
  160. save_path = saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"))
  161. log_string("Model saved in file: %s" % save_path)
  162. def train_one_epoch(sess, ops, train_writer):
  163. """ ops: dict mapping from string to tf ops """
  164. is_training = True
  165. log_string('----')
  166. current_data, current_label, _ = provider.shuffle_data(train_data, train_label)
  167. current_data = current_data[:,0:NUM_POINT,:]
  168. current_label = current_label[:,0:NUM_POINT]
  169. file_size = current_data.shape[0]
  170. num_batches = file_size // BATCH_SIZE
  171. total_correct = 0
  172. total_seen = 0
  173. loss_sum = 0
  174. for batch_idx in range(num_batches):
  175. if batch_idx % 100 == 0:
  176. print('Current batch/total batch num: %d/%d'%(batch_idx,num_batches))
  177. start_idx = batch_idx * BATCH_SIZE
  178. end_idx = (batch_idx+1) * BATCH_SIZE
  179. feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
  180. ops['labels_pl']: current_label[start_idx:end_idx],
  181. ops['is_training_pl']: is_training,}
  182. summary, step, _, loss_val, pred_val = sess.run([ops['merged'], ops['step'], ops['train_op'], ops['loss'], ops['pred']],
  183. feed_dict=feed_dict)
  184. train_writer.add_summary(summary, step)
  185. pred_val = np.argmax(pred_val, 2)
  186. correct = np.sum(pred_val == current_label[start_idx:end_idx])
  187. total_correct += correct
  188. total_seen += (BATCH_SIZE*NUM_POINT)
  189. loss_sum += loss_val
  190. log_string('mean loss: %f' % (loss_sum / float(num_batches)))
  191. log_string('accuracy: %f' % (total_correct / float(total_seen)))
  192. def eval_one_epoch(sess, ops, test_writer):
  193. """ ops: dict mapping from string to tf ops """
  194. is_training = False
  195. total_correct = 0
  196. total_seen = 0
  197. loss_sum = 0
  198. total_seen_class = [0 for _ in range(NUM_CLASSES)]
  199. total_correct_class = [0 for _ in range(NUM_CLASSES)]
  200. log_string('----')
  201. current_data, current_label, _ = provider.shuffle_data(test_data, test_label)
  202. current_data = current_data[:, 0:NUM_POINT, :]
  203. current_label = current_label[:, 0:NUM_POINT]
  204. current_label = np.squeeze(current_label)
  205. file_size = current_data.shape[0]
  206. num_batches = file_size // BATCH_SIZE
  207. for batch_idx in range(num_batches):
  208. start_idx = batch_idx * BATCH_SIZE
  209. end_idx = (batch_idx+1) * BATCH_SIZE
  210. feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
  211. ops['labels_pl']: current_label[start_idx:end_idx],
  212. ops['is_training_pl']: is_training}
  213. summary, step, loss_val, pred_val = sess.run([ops['merged'], ops['step'], ops['loss'], ops['pred']],
  214. feed_dict=feed_dict)
  215. test_writer.add_summary(summary, step)
  216. pred_val = np.argmax(pred_val, 2)
  217. correct = np.sum(pred_val == current_label[start_idx:end_idx])
  218. total_correct += correct
  219. total_seen += (BATCH_SIZE*NUM_POINT)
  220. loss_sum += (loss_val*BATCH_SIZE)
  221. for i in range(start_idx, end_idx):
  222. for j in range(NUM_POINT):
  223. l = current_label[i, j]
  224. total_seen_class[l] += 1
  225. total_correct_class[l] += (pred_val[i-start_idx, j] == l)
  226. log_string('eval mean loss: %f' % (loss_sum / float(total_seen/NUM_POINT)))
  227. log_string('eval accuracy: %f'% (total_correct / float(total_seen)))
  228. log_string('eval avg class acc: %f' % (np.mean(np.array(total_correct_class)/np.array(total_seen_class,dtype=np.float))))
  229. if __name__ == "__main__":
  230. train()
  231. LOG_FOUT.close()

 运行train.py文件便进入训练阶段(博主训练了12小时左右)

 前面点云分类,对应每份点云数据是一个类别信息。参考图像语义分割(像素级分割),这里的点云中的每个点都应该有个类别标签,我们debug看一下。可看到对应于data_batches的label_batches,其维度是23585*4096,因为有23585份点云数据,4096就对应一份点云中每个点的标签信息。同时也看到一份点云中每个点的label值是不一样的,所以训练的时候是拿的各场景下的实际点云数据来训练的(将房间的点云数据划分为1 m × 1 m 的块,从各块中随机抽取4096个点)

2. 点云分割网络预测

(1)npy数据准备

源代码并不是用的前面indoor3d_sem_seg_hdf5_data中的h5数据来做预测(虽然h5文件中包含了点云和label标签信息,以能去判断预测结果和ground true是不是一致),拿的是点云的npy格式数据来做预测的。那么我们就要生成一份npy数据出来。博主这里只对Stanford3dDataset_v1.2_Aligned_Version数据集(底部附注中有对该数据集做介绍)中的Area_6生成一份npy。可运行collect_indoor3d_data.py文件开完成。博主的数据集放置在工程中的如下位置

 debug可看到,此份点云数据points有7090个点,每个点有6维来表示。labels则有7090维,对应每一个点的标签。合并后的points_list中的元素维度则变为了7090*7,相较于之前的原始数据,这里多了一个标签信息。

 同时为了节省时间(这里的初衷只是为了跑通一下源码),所以只对Area_6中的四份Annotions中的数据做转换。所以all_data_label.txt中的文本修改为如下:

  1. Area_6_conferenceRoom_1.npy
  2. Area_6_office_10.npy
  3. Area_6_openspace_1.npy
  4. Area_6_pantry_1.npy

anno_paths.txt中的文本修改为如下:

  1. Area_6/conferenceRoom_1/Annotations
  2. Area_6/office_10/Annotations
  3. Area_6/openspace_1/Annotations
  4. Area_6/pantry_1/Annotations

area6_data_label.txt中的文本修改为如下:

  1. data/stanford_indoor3d/Area_6_conferenceRoom_1.npy
  2. data/stanford_indoor3d/Area_6_office_10.npy
  3. data/stanford_indoor3d/Area_6_openspace_1.npy
  4. data/stanford_indoor3d/Area_6_pantry_1.npy

运行collect_indoor3d_data.py文件,结果如下:

 可看到生成的四份npy文件,每一份npy文件都是对应场景下Annotations中所有文件中点云拼接的数据(同时包含了label信息)。这么费劲做,而不是直接读那个和Annotations文件夹并列的完整点云数据(是原始数据,不含有标签信息),就是为了获得每个点的标签信息。

(2)预测

博主这边简单修改了下batch_inference.py文件中的代码,如下:

  1. import tensorflow.compat.v1 as tf
  2. tf.compat.v1.disable_eager_execution()
  3. import argparse
  4. import os
  5. import sys
  6. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  7. ROOT_DIR = os.path.dirname(BASE_DIR)
  8. sys.path.append(BASE_DIR)
  9. from model import *
  10. import indoor3d_util
  11. parser = argparse.ArgumentParser()
  12. parser.add_argument('--gpu', type=int, default=0, help='GPU to use [default: GPU 0]')
  13. parser.add_argument('--batch_size', type=int, default=1, help='Batch Size during training [default: 1]')
  14. parser.add_argument('--num_point', type=int, default=4096, help='Point number [default: 4096]')
  15. parser.add_argument('--model_path', default='log/model.ckpt', help='model checkpoint file path')
  16. parser.add_argument('--dump_dir', default='dump', help='dump folder path')
  17. parser.add_argument('--output_filelist', default='output.txt', help='TXT filename, filelist, each line is an output for a room')
  18. parser.add_argument('--room_data_filelist', default='meta/area6_data_label.txt', help='TXT filename, filelist, each line is a test room data label file.')
  19. parser.add_argument('--no_clutter', action='store_true', help='If true, donot count the clutter class')
  20. parser.add_argument('--visu', default='true', help='Whether to output OBJ file for prediction visualization.')
  21. FLAGS = parser.parse_args()
  22. BATCH_SIZE = FLAGS.batch_size
  23. NUM_POINT = FLAGS.num_point
  24. MODEL_PATH = FLAGS.model_path
  25. GPU_INDEX = FLAGS.gpu
  26. DUMP_DIR = FLAGS.dump_dir
  27. if not os.path.exists(DUMP_DIR): os.mkdir(DUMP_DIR)
  28. LOG_FOUT = open(os.path.join(DUMP_DIR, 'log_evaluate.txt'), 'w')
  29. LOG_FOUT.write(str(FLAGS)+'\n')
  30. ROOM_PATH_LIST = [os.path.join(ROOT_DIR,line.rstrip()) for line in open(FLAGS.room_data_filelist)]
  31. NUM_CLASSES = 13
  32. def log_string(out_str):
  33. LOG_FOUT.write(out_str+'\n')
  34. LOG_FOUT.flush()
  35. print(out_str)
  36. def evaluate():
  37. is_training = False
  38. with tf.device('/gpu:'+str(GPU_INDEX)):
  39. pointclouds_pl, labels_pl = placeholder_inputs(BATCH_SIZE, NUM_POINT)
  40. is_training_pl = tf.placeholder(tf.bool, shape=())
  41. # simple model
  42. pred = get_model(pointclouds_pl, is_training_pl)
  43. loss = get_loss(pred, labels_pl)
  44. pred_softmax = tf.nn.softmax(pred)
  45. # Add ops to save and restore all the variables.
  46. saver = tf.train.Saver()
  47. # Create a session
  48. config = tf.ConfigProto()
  49. config.gpu_options.allow_growth = True
  50. config.allow_soft_placement = True
  51. config.log_device_placement = True
  52. sess = tf.Session(config=config)
  53. # Restore variables from disk.
  54. saver.restore(sess, MODEL_PATH)
  55. log_string("Model restored.")
  56. ops = {'pointclouds_pl': pointclouds_pl,
  57. 'labels_pl': labels_pl,
  58. 'is_training_pl': is_training_pl,
  59. 'pred': pred,
  60. 'pred_softmax': pred_softmax,
  61. 'loss': loss}
  62. total_correct = 0
  63. total_seen = 0
  64. fout_out_filelist = open(FLAGS.output_filelist, 'w')
  65. for room_path in ROOM_PATH_LIST:
  66. out_data_label_filename = os.path.basename(room_path)[:-4] + '_pred.txt'
  67. out_data_label_filename = os.path.join(DUMP_DIR, out_data_label_filename)
  68. out_gt_label_filename = os.path.basename(room_path)[:-4] + '_gt.txt'
  69. out_gt_label_filename = os.path.join(DUMP_DIR, out_gt_label_filename)
  70. print(room_path, out_data_label_filename)
  71. a, b = eval_one_epoch(sess, ops, room_path, out_data_label_filename, out_gt_label_filename)
  72. total_correct += a
  73. total_seen += b
  74. fout_out_filelist.write(out_data_label_filename+'\n')
  75. fout_out_filelist.close()
  76. log_string('all room eval accuracy: %f'% (total_correct / float(total_seen)))
  77. def eval_one_epoch(sess, ops, room_path, out_data_label_filename, out_gt_label_filename):
  78. error_cnt = 0
  79. is_training = False
  80. total_correct = 0
  81. total_seen = 0
  82. loss_sum = 0
  83. total_seen_class = [0 for _ in range(NUM_CLASSES)]
  84. total_correct_class = [0 for _ in range(NUM_CLASSES)]
  85. if FLAGS.visu:
  86. fout = open(os.path.join(DUMP_DIR, os.path.basename(room_path)[:-4]+'_pred.obj'), 'w')
  87. fout_gt = open(os.path.join(DUMP_DIR, os.path.basename(room_path)[:-4]+'_gt.obj'), 'w')
  88. fout_data_label = open(out_data_label_filename, 'w')
  89. fout_gt_label = open(out_gt_label_filename, 'w')
  90. current_data, current_label = indoor3d_util.room2blocks_wrapper_normalized(room_path, NUM_POINT)
  91. current_data = current_data[:,0:NUM_POINT,:]
  92. current_label = np.squeeze(current_label)
  93. # Get room dimension..
  94. data_label = np.load(room_path)
  95. data = data_label[:,0:6]
  96. max_room_x = max(data[:,0])
  97. max_room_y = max(data[:,1])
  98. max_room_z = max(data[:,2])
  99. file_size = current_data.shape[0]
  100. num_batches = file_size // BATCH_SIZE
  101. print(file_size)
  102. for batch_idx in range(num_batches):
  103. start_idx = batch_idx * BATCH_SIZE
  104. end_idx = (batch_idx+1) * BATCH_SIZE
  105. cur_batch_size = end_idx - start_idx
  106. feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
  107. ops['labels_pl']: current_label[start_idx:end_idx],
  108. ops['is_training_pl']: is_training}
  109. loss_val, pred_val = sess.run([ops['loss'], ops['pred_softmax']],
  110. feed_dict=feed_dict)
  111. if FLAGS.no_clutter:
  112. pred_label = np.argmax(pred_val[:,:,0:12], 2) # BxN
  113. else:
  114. pred_label = np.argmax(pred_val, 2) # BxN
  115. # Save prediction labels to OBJ file
  116. for b in range(BATCH_SIZE):
  117. pts = current_data[start_idx+b, :, :]
  118. l = current_label[start_idx+b,:]
  119. pts[:,6] *= max_room_x
  120. pts[:,7] *= max_room_y
  121. pts[:,8] *= max_room_z
  122. pts[:,3:6] *= 255.0
  123. pred = pred_label[b, :]
  124. for i in range(NUM_POINT):
  125. color = indoor3d_util.g_label2color[pred[i]]
  126. color_gt = indoor3d_util.g_label2color[current_label[start_idx+b, i]]
  127. if FLAGS.visu:
  128. fout.write('v %f %f %f %d %d %d\n' % (pts[i,6], pts[i,7], pts[i,8], color[0], color[1], color[2]))
  129. fout_gt.write('v %f %f %f %d %d %d\n' % (pts[i,6], pts[i,7], pts[i,8], color_gt[0], color_gt[1], color_gt[2]))
  130. fout_data_label.write('%f %f %f %d %d %d %f %d\n' % (pts[i,6], pts[i,7], pts[i,8], pts[i,3], pts[i,4], pts[i,5], pred_val[b,i,pred[i]], pred[i]))
  131. fout_gt_label.write('%d\n' % (l[i]))
  132. correct = np.sum(pred_label == current_label[start_idx:end_idx,:])
  133. total_correct += correct
  134. total_seen += (cur_batch_size*NUM_POINT)
  135. loss_sum += (loss_val*BATCH_SIZE)
  136. for i in range(start_idx, end_idx):
  137. for j in range(NUM_POINT):
  138. l = current_label[i, j]
  139. total_seen_class[l] += 1
  140. total_correct_class[l] += (pred_label[i-start_idx, j] == l)
  141. log_string('eval mean loss: %f' % (loss_sum / float(total_seen/NUM_POINT)))
  142. log_string('eval accuracy: %f'% (total_correct / float(total_seen)))
  143. fout_data_label.close()
  144. fout_gt_label.close()
  145. if FLAGS.visu:
  146. fout.close()
  147. fout_gt.close()
  148. return total_correct, total_seen
  149. if __name__=='__main__':
  150. with tf.Graph().as_default():
  151. evaluate()
  152. LOG_FOUT.close()

 来对上面生成的四份npy数据做预测,执行结果如下:

 生成的结果数据在dump文件夹下

 详情过程可以debug去看,大概过程就是先加载npy文件,然后做数据和标签的拆分,然后对数据部分(点表示为6维)进行1mm*1mm的分块并采样(一个分块中随机采样4096个点),同时点的表示由之前的(x,y,z,r,g,b)转换为(x,y,z,r,g,b,x',y',z'),后3个是x,y,z相对于整体空间归一化的空间坐标。然后对所有块中的数据做预测,并把所有块预测的结果(x',y',z', r',g',b')写到obj中(带pred关键字的obj), 其中x',y',z'就是前面归一化的空间坐标,r' , g' , b'是有对应各标签对应的颜色索引值。同时,也保存了ground truth的结果,这样视觉上课直观做比较。如下是debug的记录结果:

这边可以看到对点云随机取点采样的代码

从保存的预测结果数据也可以看出每份点云是抽取了4096个点,所以30份点云文件共对应4096*30=122880个点。

 上传下这份点云语义分割的工程,链接如下:

链接: https://pan.baidu.com/s/18xdS-VYHaFCK69hPLpujYw 提取码: cbqe

 接下来,我们来可视化下obj文件,以来对比预测结果和ground truth

(3)可视化obj,对比预测结果和ground truth

->使用meshlab可视化

如下语句即可安装meshlab

sudo apt-get install meshlab

显示Area_6_openspace_1_gt.obj

 显示Area_6_openspace_1_pred.obj

->使用cloudcompare可视化

如下语句即可安装

sudo apt-get install cloudcompare

cloudcompare的使用可以参考博主之前的博客

CloudCompare配置介绍_cloudcompare二次开发_竹叶青lvye的博客-CSDN博客

CloudCompare中PLC插件开发介绍_cloudcompare 插件_竹叶青lvye的博客-CSDN博客

 但是丢失了颜色标签信息。

 三. 部件分割和预测

1. 训练

首先下载训练集,路径可以从part_seg目录下的download_data.sh中找到,也可以手动到如下网页上下载

https://shapenet.cs.stanford.edu/media/shapenet_part_seg_hdf5_data.zip

https://shapenet.cs.stanford.edu/ericyi/shapenetcore_partanno_v0.zip

下载完毕后,博主放在如下目录结构

 执行part_seg目录下的train.py文件,即开始训练

  1. import argparse
  2. import subprocess
  3. import tensorflow.compat.v1 as tf
  4. tf.compat.v1.disable_eager_execution()
  5. import numpy as np
  6. from datetime import datetime
  7. import json
  8. import os
  9. import sys
  10. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  11. sys.path.append(BASE_DIR)
  12. sys.path.append(os.path.dirname(BASE_DIR))
  13. import provider
  14. import pointnet_part_seg as model
  15. # DEFAULT SETTINGS
  16. parser = argparse.ArgumentParser()
  17. parser.add_argument('--gpu', type=int, default=1, help='GPU to use [default: GPU 0]')
  18. parser.add_argument('--batch', type=int, default=4, help='Batch Size during training [default: 32]')
  19. parser.add_argument('--epoch', type=int, default=200, help='Epoch to run [default: 50]')
  20. parser.add_argument('--point_num', type=int, default=2048, help='Point Number [256/512/1024/2048]')
  21. parser.add_argument('--output_dir', type=str, default='train_results', help='Directory that stores all training logs and trained models')
  22. parser.add_argument('--wd', type=float, default=0, help='Weight Decay [Default: 0.0]')
  23. FLAGS = parser.parse_args()
  24. hdf5_data_dir = os.path.join(BASE_DIR, './hdf5_data')
  25. # MAIN SCRIPT
  26. point_num = FLAGS.point_num
  27. batch_size = FLAGS.batch
  28. output_dir = FLAGS.output_dir
  29. if not os.path.exists(output_dir):
  30. os.mkdir(output_dir)
  31. color_map_file = os.path.join(hdf5_data_dir, 'part_color_mapping.json')
  32. color_map = json.load(open(color_map_file, 'r'))
  33. all_obj_cats_file = os.path.join(hdf5_data_dir, 'all_object_categories.txt')
  34. fin = open(all_obj_cats_file, 'r')
  35. lines = [line.rstrip() for line in fin.readlines()]
  36. all_obj_cats = [(line.split()[0], line.split()[1]) for line in lines]
  37. fin.close()
  38. all_cats = json.load(open(os.path.join(hdf5_data_dir, 'overallid_to_catid_partid.json'), 'r'))
  39. NUM_CATEGORIES = 16
  40. NUM_PART_CATS = len(all_cats)
  41. print('#### Batch Size: {0}'.format(batch_size))
  42. print('#### Point Number: {0}'.format(point_num))
  43. print('#### Training using GPU: {0}'.format(FLAGS.gpu))
  44. DECAY_STEP = 16881 * 20
  45. DECAY_RATE = 0.5
  46. LEARNING_RATE_CLIP = 1e-5
  47. BN_INIT_DECAY = 0.5
  48. BN_DECAY_DECAY_RATE = 0.5
  49. BN_DECAY_DECAY_STEP = float(DECAY_STEP * 2)
  50. BN_DECAY_CLIP = 0.99
  51. BASE_LEARNING_RATE = 0.001
  52. MOMENTUM = 0.9
  53. TRAINING_EPOCHES = FLAGS.epoch
  54. print('### Training epoch: {0}'.format(TRAINING_EPOCHES))
  55. TRAINING_FILE_LIST = os.path.join(hdf5_data_dir, 'train_hdf5_file_list.txt')
  56. TESTING_FILE_LIST = os.path.join(hdf5_data_dir, 'val_hdf5_file_list.txt')
  57. MODEL_STORAGE_PATH = os.path.join(output_dir, 'trained_models')
  58. if not os.path.exists(MODEL_STORAGE_PATH):
  59. os.mkdir(MODEL_STORAGE_PATH)
  60. LOG_STORAGE_PATH = os.path.join(output_dir, 'logs')
  61. if not os.path.exists(LOG_STORAGE_PATH):
  62. os.mkdir(LOG_STORAGE_PATH)
  63. SUMMARIES_FOLDER = os.path.join(output_dir, 'summaries')
  64. if not os.path.exists(SUMMARIES_FOLDER):
  65. os.mkdir(SUMMARIES_FOLDER)
  66. def printout(flog, data):
  67. print(data)
  68. flog.write(data + '\n')
  69. def placeholder_inputs():
  70. pointclouds_ph = tf.placeholder(tf.float32, shape=(batch_size, point_num, 3))
  71. input_label_ph = tf.placeholder(tf.float32, shape=(batch_size, NUM_CATEGORIES))
  72. labels_ph = tf.placeholder(tf.int32, shape=(batch_size))
  73. seg_ph = tf.placeholder(tf.int32, shape=(batch_size, point_num))
  74. return pointclouds_ph, input_label_ph, labels_ph, seg_ph
  75. def convert_label_to_one_hot(labels):
  76. label_one_hot = np.zeros((labels.shape[0], NUM_CATEGORIES))
  77. for idx in range(labels.shape[0]):
  78. label_one_hot[idx, labels[idx]] = 1
  79. return label_one_hot
  80. def train():
  81. with tf.Graph().as_default():
  82. with tf.device('/gpu:'+str(FLAGS.gpu)):
  83. pointclouds_ph, input_label_ph, labels_ph, seg_ph = placeholder_inputs()
  84. is_training_ph = tf.placeholder(tf.bool, shape=())
  85. batch = tf.Variable(0, trainable=False)
  86. learning_rate = tf.train.exponential_decay(
  87. BASE_LEARNING_RATE, # base learning rate
  88. batch * batch_size, # global_var indicating the number of steps
  89. DECAY_STEP, # step size
  90. DECAY_RATE, # decay rate
  91. staircase=True # Stair-case or continuous decreasing
  92. )
  93. learning_rate = tf.maximum(learning_rate, LEARNING_RATE_CLIP)
  94. bn_momentum = tf.train.exponential_decay(
  95. BN_INIT_DECAY,
  96. batch*batch_size,
  97. BN_DECAY_DECAY_STEP,
  98. BN_DECAY_DECAY_RATE,
  99. staircase=True)
  100. bn_decay = tf.minimum(BN_DECAY_CLIP, 1 - bn_momentum)
  101. lr_op = tf.summary.scalar('learning_rate', learning_rate)
  102. batch_op = tf.summary.scalar('batch_number', batch)
  103. bn_decay_op = tf.summary.scalar('bn_decay', bn_decay)
  104. labels_pred, seg_pred, end_points = model.get_model(pointclouds_ph, input_label_ph, \
  105. is_training=is_training_ph, bn_decay=bn_decay, cat_num=NUM_CATEGORIES, \
  106. part_num=NUM_PART_CATS, batch_size=batch_size, num_point=point_num, weight_decay=FLAGS.wd)
  107. # model.py defines both classification net and segmentation net, which share the common global feature extractor network.
  108. # In model.get_loss, we define the total loss to be weighted sum of the classification and segmentation losses.
  109. # Here, we only train for segmentation network. Thus, we set weight to be 1.0.
  110. loss, label_loss, per_instance_label_loss, seg_loss, per_instance_seg_loss, per_instance_seg_pred_res \
  111. = model.get_loss(labels_pred, seg_pred, labels_ph, seg_ph, 1.0, end_points)
  112. total_training_loss_ph = tf.placeholder(tf.float32, shape=())
  113. total_testing_loss_ph = tf.placeholder(tf.float32, shape=())
  114. label_training_loss_ph = tf.placeholder(tf.float32, shape=())
  115. label_testing_loss_ph = tf.placeholder(tf.float32, shape=())
  116. seg_training_loss_ph = tf.placeholder(tf.float32, shape=())
  117. seg_testing_loss_ph = tf.placeholder(tf.float32, shape=())
  118. label_training_acc_ph = tf.placeholder(tf.float32, shape=())
  119. label_testing_acc_ph = tf.placeholder(tf.float32, shape=())
  120. label_testing_acc_avg_cat_ph = tf.placeholder(tf.float32, shape=())
  121. seg_training_acc_ph = tf.placeholder(tf.float32, shape=())
  122. seg_testing_acc_ph = tf.placeholder(tf.float32, shape=())
  123. seg_testing_acc_avg_cat_ph = tf.placeholder(tf.float32, shape=())
  124. total_train_loss_sum_op = tf.summary.scalar('total_training_loss', total_training_loss_ph)
  125. total_test_loss_sum_op = tf.summary.scalar('total_testing_loss', total_testing_loss_ph)
  126. label_train_loss_sum_op = tf.summary.scalar('label_training_loss', label_training_loss_ph)
  127. label_test_loss_sum_op = tf.summary.scalar('label_testing_loss', label_testing_loss_ph)
  128. seg_train_loss_sum_op = tf.summary.scalar('seg_training_loss', seg_training_loss_ph)
  129. seg_test_loss_sum_op = tf.summary.scalar('seg_testing_loss', seg_testing_loss_ph)
  130. label_train_acc_sum_op = tf.summary.scalar('label_training_acc', label_training_acc_ph)
  131. label_test_acc_sum_op = tf.summary.scalar('label_testing_acc', label_testing_acc_ph)
  132. label_test_acc_avg_cat_op = tf.summary.scalar('label_testing_acc_avg_cat', label_testing_acc_avg_cat_ph)
  133. seg_train_acc_sum_op = tf.summary.scalar('seg_training_acc', seg_training_acc_ph)
  134. seg_test_acc_sum_op = tf.summary.scalar('seg_testing_acc', seg_testing_acc_ph)
  135. seg_test_acc_avg_cat_op = tf.summary.scalar('seg_testing_acc_avg_cat', seg_testing_acc_avg_cat_ph)
  136. train_variables = tf.trainable_variables()
  137. trainer = tf.train.AdamOptimizer(learning_rate)
  138. train_op = trainer.minimize(loss, var_list=train_variables, global_step=batch)
  139. saver = tf.train.Saver()
  140. config = tf.ConfigProto()
  141. config.gpu_options.allow_growth = True
  142. config.allow_soft_placement = True
  143. sess = tf.Session(config=config)
  144. init = tf.global_variables_initializer()
  145. sess.run(init)
  146. train_writer = tf.summary.FileWriter(SUMMARIES_FOLDER + '/train', sess.graph)
  147. test_writer = tf.summary.FileWriter(SUMMARIES_FOLDER + '/test')
  148. train_file_list = provider.getDataFiles(TRAINING_FILE_LIST)
  149. num_train_file = len(train_file_list)
  150. test_file_list = provider.getDataFiles(TESTING_FILE_LIST)
  151. num_test_file = len(test_file_list)
  152. fcmd = open(os.path.join(LOG_STORAGE_PATH, 'cmd.txt'), 'w')
  153. fcmd.write(str(FLAGS))
  154. fcmd.close()
  155. # write logs to the disk
  156. flog = open(os.path.join(LOG_STORAGE_PATH, 'log.txt'), 'w')
  157. def train_one_epoch(train_file_idx, epoch_num):
  158. is_training = True
  159. for i in range(num_train_file):
  160. cur_train_filename = os.path.join(hdf5_data_dir, train_file_list[train_file_idx[i]])
  161. printout(flog, 'Loading train file ' + cur_train_filename)
  162. cur_data, cur_labels, cur_seg = provider.loadDataFile_with_seg(cur_train_filename)
  163. cur_data, cur_labels, order = provider.shuffle_data(cur_data, np.squeeze(cur_labels))
  164. cur_seg = cur_seg[order, ...]
  165. cur_labels_one_hot = convert_label_to_one_hot(cur_labels)
  166. num_data = len(cur_labels)
  167. num_batch = num_data // batch_size
  168. total_loss = 0.0
  169. total_label_loss = 0.0
  170. total_seg_loss = 0.0
  171. total_label_acc = 0.0
  172. total_seg_acc = 0.0
  173. for j in range(num_batch):
  174. begidx = j * batch_size
  175. endidx = (j + 1) * batch_size
  176. feed_dict = {
  177. pointclouds_ph: cur_data[begidx: endidx, ...],
  178. labels_ph: cur_labels[begidx: endidx, ...],
  179. input_label_ph: cur_labels_one_hot[begidx: endidx, ...],
  180. seg_ph: cur_seg[begidx: endidx, ...],
  181. is_training_ph: is_training,
  182. }
  183. _, loss_val, label_loss_val, seg_loss_val, per_instance_label_loss_val, \
  184. per_instance_seg_loss_val, label_pred_val, seg_pred_val, pred_seg_res \
  185. = sess.run([train_op, loss, label_loss, seg_loss, per_instance_label_loss, \
  186. per_instance_seg_loss, labels_pred, seg_pred, per_instance_seg_pred_res], \
  187. feed_dict=feed_dict)
  188. per_instance_part_acc = np.mean(pred_seg_res == cur_seg[begidx: endidx, ...], axis=1)
  189. average_part_acc = np.mean(per_instance_part_acc)
  190. total_loss += loss_val
  191. total_label_loss += label_loss_val
  192. total_seg_loss += seg_loss_val
  193. per_instance_label_pred = np.argmax(label_pred_val, axis=1)
  194. total_label_acc += np.mean(np.float32(per_instance_label_pred == cur_labels[begidx: endidx, ...]))
  195. total_seg_acc += average_part_acc
  196. total_loss = total_loss * 1.0 / num_batch
  197. total_label_loss = total_label_loss * 1.0 / num_batch
  198. total_seg_loss = total_seg_loss * 1.0 / num_batch
  199. total_label_acc = total_label_acc * 1.0 / num_batch
  200. total_seg_acc = total_seg_acc * 1.0 / num_batch
  201. lr_sum, bn_decay_sum, batch_sum, train_loss_sum, train_label_acc_sum, \
  202. train_label_loss_sum, train_seg_loss_sum, train_seg_acc_sum = sess.run(\
  203. [lr_op, bn_decay_op, batch_op, total_train_loss_sum_op, label_train_acc_sum_op, \
  204. label_train_loss_sum_op, seg_train_loss_sum_op, seg_train_acc_sum_op], \
  205. feed_dict={total_training_loss_ph: total_loss, label_training_loss_ph: total_label_loss, \
  206. seg_training_loss_ph: total_seg_loss, label_training_acc_ph: total_label_acc, \
  207. seg_training_acc_ph: total_seg_acc})
  208. train_writer.add_summary(train_loss_sum, i + epoch_num * num_train_file)
  209. train_writer.add_summary(train_label_loss_sum, i + epoch_num * num_train_file)
  210. train_writer.add_summary(train_seg_loss_sum, i + epoch_num * num_train_file)
  211. train_writer.add_summary(lr_sum, i + epoch_num * num_train_file)
  212. train_writer.add_summary(bn_decay_sum, i + epoch_num * num_train_file)
  213. train_writer.add_summary(train_label_acc_sum, i + epoch_num * num_train_file)
  214. train_writer.add_summary(train_seg_acc_sum, i + epoch_num * num_train_file)
  215. train_writer.add_summary(batch_sum, i + epoch_num * num_train_file)
  216. printout(flog, '\tTraining Total Mean_loss: %f' % total_loss)
  217. printout(flog, '\t\tTraining Label Mean_loss: %f' % total_label_loss)
  218. printout(flog, '\t\tTraining Label Accuracy: %f' % total_label_acc)
  219. printout(flog, '\t\tTraining Seg Mean_loss: %f' % total_seg_loss)
  220. printout(flog, '\t\tTraining Seg Accuracy: %f' % total_seg_acc)
  221. def eval_one_epoch(epoch_num):
  222. is_training = False
  223. total_loss = 0.0
  224. total_label_loss = 0.0
  225. total_seg_loss = 0.0
  226. total_label_acc = 0.0
  227. total_seg_acc = 0.0
  228. total_seen = 0
  229. total_label_acc_per_cat = np.zeros((NUM_CATEGORIES)).astype(np.float32)
  230. total_seg_acc_per_cat = np.zeros((NUM_CATEGORIES)).astype(np.float32)
  231. total_seen_per_cat = np.zeros((NUM_CATEGORIES)).astype(np.int32)
  232. for i in range(num_test_file):
  233. cur_test_filename = os.path.join(hdf5_data_dir, test_file_list[i])
  234. printout(flog, 'Loading test file ' + cur_test_filename)
  235. cur_data, cur_labels, cur_seg = provider.loadDataFile_with_seg(cur_test_filename)
  236. cur_labels = np.squeeze(cur_labels)
  237. cur_labels_one_hot = convert_label_to_one_hot(cur_labels)
  238. num_data = len(cur_labels)
  239. num_batch = num_data // batch_size
  240. for j in range(num_batch):
  241. begidx = j * batch_size
  242. endidx = (j + 1) * batch_size
  243. feed_dict = {
  244. pointclouds_ph: cur_data[begidx: endidx, ...],
  245. labels_ph: cur_labels[begidx: endidx, ...],
  246. input_label_ph: cur_labels_one_hot[begidx: endidx, ...],
  247. seg_ph: cur_seg[begidx: endidx, ...],
  248. is_training_ph: is_training,
  249. }
  250. loss_val, label_loss_val, seg_loss_val, per_instance_label_loss_val, \
  251. per_instance_seg_loss_val, label_pred_val, seg_pred_val, pred_seg_res \
  252. = sess.run([loss, label_loss, seg_loss, per_instance_label_loss, \
  253. per_instance_seg_loss, labels_pred, seg_pred, per_instance_seg_pred_res], \
  254. feed_dict=feed_dict)
  255. per_instance_part_acc = np.mean(pred_seg_res == cur_seg[begidx: endidx, ...], axis=1)
  256. average_part_acc = np.mean(per_instance_part_acc)
  257. total_seen += 1
  258. total_loss += loss_val
  259. total_label_loss += label_loss_val
  260. total_seg_loss += seg_loss_val
  261. per_instance_label_pred = np.argmax(label_pred_val, axis=1)
  262. total_label_acc += np.mean(np.float32(per_instance_label_pred == cur_labels[begidx: endidx, ...]))
  263. total_seg_acc += average_part_acc
  264. for shape_idx in range(begidx, endidx):
  265. total_seen_per_cat[cur_labels[shape_idx]] += 1
  266. total_label_acc_per_cat[cur_labels[shape_idx]] += np.int32(per_instance_label_pred[shape_idx-begidx] == cur_labels[shape_idx])
  267. total_seg_acc_per_cat[cur_labels[shape_idx]] += per_instance_part_acc[shape_idx - begidx]
  268. total_loss = total_loss * 1.0 / total_seen
  269. total_label_loss = total_label_loss * 1.0 / total_seen
  270. total_seg_loss = total_seg_loss * 1.0 / total_seen
  271. total_label_acc = total_label_acc * 1.0 / total_seen
  272. total_seg_acc = total_seg_acc * 1.0 / total_seen
  273. test_loss_sum, test_label_acc_sum, test_label_loss_sum, test_seg_loss_sum, test_seg_acc_sum = sess.run(\
  274. [total_test_loss_sum_op, label_test_acc_sum_op, label_test_loss_sum_op, seg_test_loss_sum_op, seg_test_acc_sum_op], \
  275. feed_dict={total_testing_loss_ph: total_loss, label_testing_loss_ph: total_label_loss, \
  276. seg_testing_loss_ph: total_seg_loss, label_testing_acc_ph: total_label_acc, seg_testing_acc_ph: total_seg_acc})
  277. test_writer.add_summary(test_loss_sum, (epoch_num+1) * num_train_file-1)
  278. test_writer.add_summary(test_label_loss_sum, (epoch_num+1) * num_train_file-1)
  279. test_writer.add_summary(test_seg_loss_sum, (epoch_num+1) * num_train_file-1)
  280. test_writer.add_summary(test_label_acc_sum, (epoch_num+1) * num_train_file-1)
  281. test_writer.add_summary(test_seg_acc_sum, (epoch_num+1) * num_train_file-1)
  282. printout(flog, '\tTesting Total Mean_loss: %f' % total_loss)
  283. printout(flog, '\t\tTesting Label Mean_loss: %f' % total_label_loss)
  284. printout(flog, '\t\tTesting Label Accuracy: %f' % total_label_acc)
  285. printout(flog, '\t\tTesting Seg Mean_loss: %f' % total_seg_loss)
  286. printout(flog, '\t\tTesting Seg Accuracy: %f' % total_seg_acc)
  287. for cat_idx in range(NUM_CATEGORIES):
  288. if total_seen_per_cat[cat_idx] > 0:
  289. printout(flog, '\n\t\tCategory %s Object Number: %d' % (all_obj_cats[cat_idx][0], total_seen_per_cat[cat_idx]))
  290. printout(flog, '\t\tCategory %s Label Accuracy: %f' % (all_obj_cats[cat_idx][0], total_label_acc_per_cat[cat_idx]/total_seen_per_cat[cat_idx]))
  291. printout(flog, '\t\tCategory %s Seg Accuracy: %f' % (all_obj_cats[cat_idx][0], total_seg_acc_per_cat[cat_idx]/total_seen_per_cat[cat_idx]))
  292. if not os.path.exists(MODEL_STORAGE_PATH):
  293. os.mkdir(MODEL_STORAGE_PATH)
  294. for epoch in range(TRAINING_EPOCHES):
  295. printout(flog, '\n<<< Testing on the test dataset ...')
  296. eval_one_epoch(epoch)
  297. printout(flog, '\n>>> Training for the epoch %d/%d ...' % (epoch, TRAINING_EPOCHES))
  298. train_file_idx = np.arange(0, len(train_file_list))
  299. np.random.shuffle(train_file_idx)
  300. train_one_epoch(train_file_idx, epoch)
  301. if (epoch+1) % 10 == 0:
  302. cp_filename = saver.save(sess, os.path.join(MODEL_STORAGE_PATH, 'epoch_' + str(epoch+1)+'.ckpt'))
  303. printout(flog, 'Successfully store the checkpoint model into ' + cp_filename)
  304. flog.flush()
  305. flog.close()
  306. if __name__=='__main__':
  307. train()

训练模型保存在工程如下位置(只训练了20次):

 我们看下训练这个模型需要准备什么样的数据:

假设batchsize为1,即只一次训练只要一份点云(2048个点),可看到需要云数据cur_data,大小为1*2048*3;点云的分类类别cur_labels,大小为(1,); 点云属于每个类别的概率cur_labels_one_hot,大小为(1,16);点云的语义分割标签,即点云中每个点属于具体哪个部件cur_seg,大小为(1,2048)。可看到物体总类别有16种

 所有类别中总部件共有50种

假设batch size为1,此外还有这几个变量 seg_pred,大小为(1,2048,50),label_pred,大小为(1,16),per_instance_seg_pred_red,大小为(1,2048)。这些变量是在如下语句时候获取的。

接下来用训练获得的模型来对点云进行预测。

2. 预测阶段

将前面下载下来的shapenetcore_partanno_v0.zip解压放到如下工程目录结构下,然后跑下part_seg目录下的test.py

  1. import argparse
  2. import tensorflow.compat.v1 as tf
  3. tf.compat.v1.disable_eager_execution()
  4. import json
  5. import numpy as np
  6. import os
  7. import sys
  8. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  9. sys.path.append(BASE_DIR)
  10. sys.path.append(os.path.dirname(BASE_DIR))
  11. import provider
  12. import pointnet_part_seg as model
  13. parser = argparse.ArgumentParser()
  14. parser.add_argument('--model_path', default='train_results/trained_models/epoch_20.ckpt', help='Model checkpoint path')
  15. FLAGS = parser.parse_args()
  16. # DEFAULT SETTINGS
  17. pretrained_model_path = FLAGS.model_path # os.path.join(BASE_DIR, './pretrained_model/model.ckpt')
  18. hdf5_data_dir = os.path.join(BASE_DIR, './hdf5_data')
  19. ply_data_dir = os.path.join(BASE_DIR, './PartAnnotation')
  20. gpu_to_use = 0
  21. output_dir = os.path.join(BASE_DIR, './test_results')
  22. output_verbose = True # If true, output all color-coded part segmentation obj files
  23. # MAIN SCRIPT
  24. point_num = 3000 # the max number of points in the all testing data shapes
  25. batch_size = 1
  26. test_file_list = os.path.join(BASE_DIR, 'testing_ply_file_list.txt')
  27. oid2cpid = json.load(open(os.path.join(hdf5_data_dir, 'overallid_to_catid_partid.json'), 'r'))
  28. object2setofoid = {}
  29. for idx in range(len(oid2cpid)):
  30. objid, pid = oid2cpid[idx]
  31. if not objid in object2setofoid.keys():
  32. object2setofoid[objid] = []
  33. object2setofoid[objid].append(idx)
  34. all_obj_cat_file = os.path.join(hdf5_data_dir, 'all_object_categories.txt')
  35. fin = open(all_obj_cat_file, 'r')
  36. lines = [line.rstrip() for line in fin.readlines()]
  37. objcats = [line.split()[1] for line in lines]
  38. objnames = [line.split()[0] for line in lines]
  39. on2oid = {objcats[i]:i for i in range(len(objcats))}
  40. fin.close()
  41. color_map_file = os.path.join(hdf5_data_dir, 'part_color_mapping.json')
  42. color_map = json.load(open(color_map_file, 'r'))
  43. NUM_OBJ_CATS = 16
  44. NUM_PART_CATS = 50
  45. cpid2oid = json.load(open(os.path.join(hdf5_data_dir, 'catid_partid_to_overallid.json'), 'r'))
  46. def printout(flog, data):
  47. print(data)
  48. flog.write(data + '\n')
  49. def output_color_point_cloud(data, seg, out_file):
  50. with open(out_file, 'w') as f:
  51. l = len(seg)
  52. for i in range(l):
  53. color = color_map[seg[i]]
  54. f.write('v %f %f %f %f %f %f\n' % (data[i][0], data[i][1], data[i][2], color[0], color[1], color[2]))
  55. def output_color_point_cloud_red_blue(data, seg, out_file):
  56. with open(out_file, 'w') as f:
  57. l = len(seg)
  58. for i in range(l):
  59. if seg[i] == 1:
  60. color = [0, 0, 1]
  61. elif seg[i] == 0:
  62. color = [1, 0, 0]
  63. else:
  64. color = [0, 0, 0]
  65. f.write('v %f %f %f %f %f %f\n' % (data[i][0], data[i][1], data[i][2], color[0], color[1], color[2]))
  66. def pc_normalize(pc):
  67. l = pc.shape[0]
  68. centroid = np.mean(pc, axis=0)
  69. pc = pc - centroid
  70. m = np.max(np.sqrt(np.sum(pc**2, axis=1)))
  71. pc = pc / m
  72. return pc
  73. def placeholder_inputs():
  74. pointclouds_ph = tf.placeholder(tf.float32, shape=(batch_size, point_num, 3))
  75. input_label_ph = tf.placeholder(tf.float32, shape=(batch_size, NUM_OBJ_CATS))
  76. return pointclouds_ph, input_label_ph
  77. def output_color_point_cloud(data, seg, out_file):
  78. with open(out_file, 'w') as f:
  79. l = len(seg)
  80. for i in range(l):
  81. color = color_map[seg[i]]
  82. f.write('v %f %f %f %f %f %f\n' % (data[i][0], data[i][1], data[i][2], color[0], color[1], color[2]))
  83. def load_pts_seg_files(pts_file, seg_file, catid):
  84. with open(pts_file, 'r') as f:
  85. pts_str = [item.rstrip() for item in f.readlines()]
  86. pts = np.array([np.float32(s.split()) for s in pts_str], dtype=np.float32)
  87. with open(seg_file, 'r') as f:
  88. part_ids = np.array([int(item.rstrip()) for item in f.readlines()], dtype=np.uint8)
  89. seg = np.array([cpid2oid[catid+'_'+str(x)] for x in part_ids])
  90. return pts, seg
  91. def pc_augment_to_point_num(pts, pn):
  92. assert(pts.shape[0] <= pn)
  93. cur_len = pts.shape[0]
  94. res = np.array(pts)
  95. while cur_len < pn:
  96. res = np.concatenate((res, pts))
  97. cur_len += pts.shape[0]
  98. return res[:pn, :]
  99. def convert_label_to_one_hot(labels):
  100. label_one_hot = np.zeros((labels.shape[0], NUM_OBJ_CATS))
  101. for idx in range(labels.shape[0]):
  102. label_one_hot[idx, labels[idx]] = 1
  103. return label_one_hot
  104. def predict():
  105. is_training = False
  106. with tf.device('/gpu:'+str(gpu_to_use)):
  107. pointclouds_ph, input_label_ph = placeholder_inputs()
  108. is_training_ph = tf.placeholder(tf.bool, shape=())
  109. # simple model
  110. pred, seg_pred, end_points = model.get_model(pointclouds_ph, input_label_ph, \
  111. cat_num=NUM_OBJ_CATS, part_num=NUM_PART_CATS, is_training=is_training_ph, \
  112. batch_size=batch_size, num_point=point_num, weight_decay=0.0, bn_decay=None)
  113. # Add ops to save and restore all the variables.
  114. saver = tf.train.Saver()
  115. # Later, launch the model, use the saver to restore variables from disk, and
  116. # do some work with the model.
  117. config = tf.ConfigProto()
  118. config.gpu_options.allow_growth = True
  119. config.allow_soft_placement = True
  120. with tf.Session(config=config) as sess:
  121. if not os.path.exists(output_dir):
  122. os.mkdir(output_dir)
  123. flog = open(os.path.join(output_dir, 'log.txt'), 'w')
  124. # Restore variables from disk.
  125. printout(flog, 'Loading model %s' % pretrained_model_path)
  126. saver.restore(sess, pretrained_model_path)
  127. printout(flog, 'Model restored.')
  128. # Note: the evaluation for the model with BN has to have some statistics
  129. # Using some test datas as the statistics
  130. batch_data = np.zeros([batch_size, point_num, 3]).astype(np.float32)
  131. total_acc = 0.0
  132. total_seen = 0
  133. total_acc_iou = 0.0
  134. total_per_cat_acc = np.zeros((NUM_OBJ_CATS)).astype(np.float32)
  135. total_per_cat_iou = np.zeros((NUM_OBJ_CATS)).astype(np.float32)
  136. total_per_cat_seen = np.zeros((NUM_OBJ_CATS)).astype(np.int32)
  137. ffiles = open(test_file_list, 'r')
  138. lines = [line.rstrip() for line in ffiles.readlines()]
  139. pts_files = [line.split()[0] for line in lines]
  140. seg_files = [line.split()[1] for line in lines]
  141. labels = [line.split()[2] for line in lines]
  142. ffiles.close()
  143. len_pts_files = len(pts_files)
  144. for shape_idx in range(len_pts_files):
  145. if shape_idx % 100 == 0:
  146. printout(flog, '%d/%d ...' % (shape_idx, len_pts_files))
  147. cur_gt_label = on2oid[labels[shape_idx]]
  148. cur_label_one_hot = np.zeros((1, NUM_OBJ_CATS), dtype=np.float32)
  149. cur_label_one_hot[0, cur_gt_label] = 1
  150. pts_file_to_load = os.path.join(ply_data_dir, pts_files[shape_idx])
  151. seg_file_to_load = os.path.join(ply_data_dir, seg_files[shape_idx])
  152. pts, seg = load_pts_seg_files(pts_file_to_load, seg_file_to_load, objcats[cur_gt_label])
  153. ori_point_num = len(seg)
  154. batch_data[0, ...] = pc_augment_to_point_num(pc_normalize(pts), point_num)
  155. label_pred_val, seg_pred_res = sess.run([pred, seg_pred], feed_dict={
  156. pointclouds_ph: batch_data,
  157. input_label_ph: cur_label_one_hot,
  158. is_training_ph: is_training,
  159. })
  160. label_pred_val = np.argmax(label_pred_val[0, :])
  161. seg_pred_res = seg_pred_res[0, ...]
  162. iou_oids = object2setofoid[objcats[cur_gt_label]]
  163. non_cat_labels = list(set(np.arange(NUM_PART_CATS)).difference(set(iou_oids)))
  164. mini = np.min(seg_pred_res)
  165. seg_pred_res[:, non_cat_labels] = mini - 1000
  166. seg_pred_val = np.argmax(seg_pred_res, axis=1)[:ori_point_num]
  167. seg_acc = np.mean(seg_pred_val == seg)
  168. total_acc += seg_acc
  169. total_seen += 1
  170. total_per_cat_seen[cur_gt_label] += 1
  171. total_per_cat_acc[cur_gt_label] += seg_acc
  172. mask = np.int32(seg_pred_val == seg)
  173. total_iou = 0.0
  174. iou_log = ''
  175. for oid in iou_oids:
  176. n_pred = np.sum(seg_pred_val == oid)
  177. n_gt = np.sum(seg == oid)
  178. n_intersect = np.sum(np.int32(seg == oid) * mask)
  179. n_union = n_pred + n_gt - n_intersect
  180. iou_log += '_' + str(n_pred)+'_'+str(n_gt)+'_'+str(n_intersect)+'_'+str(n_union)+'_'
  181. if n_union == 0:
  182. total_iou += 1
  183. iou_log += '_1\n'
  184. else:
  185. total_iou += n_intersect * 1.0 / n_union
  186. iou_log += '_'+str(n_intersect * 1.0 / n_union)+'\n'
  187. avg_iou = total_iou / len(iou_oids)
  188. total_acc_iou += avg_iou
  189. total_per_cat_iou[cur_gt_label] += avg_iou
  190. if output_verbose:
  191. output_color_point_cloud(pts, seg, os.path.join(output_dir, str(shape_idx)+'_gt.obj'))
  192. output_color_point_cloud(pts, seg_pred_val, os.path.join(output_dir, str(shape_idx)+'_pred.obj'))
  193. output_color_point_cloud_red_blue(pts, np.int32(seg == seg_pred_val),
  194. os.path.join(output_dir, str(shape_idx)+'_diff.obj'))
  195. with open(os.path.join(output_dir, str(shape_idx)+'.log'), 'w') as fout:
  196. fout.write('Total Point: %d\n\n' % ori_point_num)
  197. fout.write('Ground Truth: %s\n' % objnames[cur_gt_label])
  198. fout.write('Predict: %s\n\n' % objnames[label_pred_val])
  199. fout.write('Accuracy: %f\n' % seg_acc)
  200. fout.write('IoU: %f\n\n' % avg_iou)
  201. fout.write('IoU details: %s\n' % iou_log)
  202. printout(flog, 'Accuracy: %f' % (total_acc / total_seen))
  203. printout(flog, 'IoU: %f' % (total_acc_iou / total_seen))
  204. for cat_idx in range(NUM_OBJ_CATS):
  205. printout(flog, '\t ' + objcats[cat_idx] + ' Total Number: ' + str(total_per_cat_seen[cat_idx]))
  206. if total_per_cat_seen[cat_idx] > 0:
  207. printout(flog, '\t ' + objcats[cat_idx] + ' Accuracy: ' + \
  208. str(total_per_cat_acc[cat_idx] / total_per_cat_seen[cat_idx]))
  209. printout(flog, '\t ' + objcats[cat_idx] + ' IoU: '+ \
  210. str(total_per_cat_iou[cat_idx] / total_per_cat_seen[cat_idx]))
  211. with tf.Graph().as_default():
  212. predict()

运行结果如下:

 会对2874份点云逐一预测,测试结果保存在如下目录下:

我们用meshlab来看下第3个点云的结果,首先是2_gt.obj

 其次是2_pred.obj

 最后是2_diff.obj

 seg_img下对应的2D图如下:

 再看下2.log中的数据

 下面我们debug分析下程序,如下是一些占位符变量的维度

 如下是预测一份点云要准备的数据以及预测返回的值。batch_data是一份点云数据,大小为(1,3000,3),cur_Label_one_hot是该点云的类别信息,大小为(1,16)。返回label_pred_val是点云的预测分类信息,大小为(1,16),属于某种类型的概率值都可以获得。seg_pred_res是点云中每个点的部件分割信息,大小为(1,3000,50)

 如下变量可以获取到该测试点云中含有哪些部件类型

 同时看到为了计算预测值和groundth的差异表征数据,所以还需要加载一些groundth值,如下:

pts是实际加载的数据,seg是每个点的标签的值(部件类型)

之所以如下操作,是保证该份点云数据中每个点的预测值都是在这个类所包含的部件类型内。接下来会获得如下表征值:(1)点云中各点的部件类别不一致的(预测值和groundth比较)  (2)每个部件的单独正确率统计 (3)。程序最后又保存了带颜色信息的(根据部件类型去索引颜色值)的预测值,groundth值,两者之间的差异值

 最后再看下PartAnnotation目录结构,主要涉及到的文件夹和文件上面已经提到

以上几个功能的完整测试工程链接如下:

链接: https://pan.baidu.com/s/1xf_LHu0-FxGXZA_ojUufjg 提取码: r4df

到此,此篇就结束啦!

附:常用3D数据集的介绍可参考如下博客

点云数据集_爱学习的小菜鸡的博客-CSDN博客

大场景室内点云标注数据集S3DIS介绍_stanford3ddataset_v1.2_aligned_version_lucky li的博客-CSDN博客

Stanford点云公开数据集:S3DIS - 知乎

PointNet语义分割使用说明 - 知乎

Stanford3dDataset_v1.2_Aligned_Version数据集的目录结构如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/360321
推荐阅读
相关标签
  

闽ICP备14008679号