当前位置:   article > 正文

点云处理:PointNet分割任务_shapenet_part_seg_hdf5_data

shapenet_part_seg_hdf5_data

项目说明

①数据集

    本次用到的数据集是ShapeNet(16881个形状,50种零件)储存格式是.h5文件。

    .h5储存的key值分别为:

    1、data:这一份数据中所有点的xyz坐标,

    2、label:这一份数据所属类别,如airplane等,

    3、pid:这一份数据中所有点所属的类型,如这一份数据属airplane类,则它包含的所有点的类型有机翼、机身等类型。

  1. !unzip data/data67117/shapenet_part_seg_hdf5_data.zip
  2. !mv hdf5_data dataset

②PointNet简介

    一、PointNet是斯坦福大学研究人员提出的一个点云处理网络,与先前工作的不同在于这一网络可以直接输入无序点云进行处理,而无序将数据处理成规则的3Dvoxel形式进行处理。输入点云顺序对于网络的输出结果没有影响,同时也可以处理旋转平移后的点云数据。

    二、几个重要的知识点:

    1、将点云体素化会改变点云数据的原始特征,造成不必要的数据损失,并且额外增加了工作量,而 PointNet 采用了原始点云的输入方式,最大限度地保留了点云的空间特征,并在最终的测试中取得了很好的效果。

    2、基本思想:对输入点云中的每一个点学习其对应的空间编码,之后再利用所有点的特征,得到一个全局的点云特征。

    3、第一次input transform(T-Net)是对空间中点云进行调整,直观上理解是旋转出一个有利于分类或分割的角度(点云的刚性变化),第二次feature transform(T-Net)是对提取出的特征进行变换,类似点的刚性变化,想利用这个得到一个有利于分类的特征角度。

    4、分割任务的完成:将n * 64局域特征和 1024 维的全局特征结合在一起进行融合,在每一个点的 64 维特征后接续 1024 全局特征,随后利用一个 mlp(512,512,128) 对n ∗ 1088维的特征维度进行学习,生成n ∗ 128的向量,再利用 (128,m) 的感知机对最后的特征进行分类(分割任务实质上是对点进行分类)从而完成分割任务。

    5、分割任务依赖于点云中点与点之间的位置关系,所以输入点的xyz入网络,充分挖掘点坐标之间的关系进行分割。

 三、网络结构:

项目主体 

 ①导入需要的库

  1. import os
  2. import numpy as np
  3. import random
  4. import h5py #对h5文件进行读写操作的Ptython包
  5. import paddle
  6. import paddle.nn.functional as F
  7. from paddle.nn import Conv2D, MaxPool2D, Linear, BatchNorm, Dropout, ReLU, Softmax, Sequential

②数据处理

1、生成训练和测试样本的list

  1. train_list = ['ply_data_train0.h5', 'ply_data_train1.h5', 'ply_data_train2.h5', 'ply_data_train3.h5', 'ply_data_train4.h5', 'ply_data_train5.h5']
  2. test_list = ['ply_data_test0.h5', 'ply_data_test1.h5']
  3. val_list = ['ply_data_val0.h5']

补充注释:

h5是HDF5文件格式的后缀。 

h5文件中有两个核心的概念:组“group”和数据集“dataset”。

dataset :简单来讲类似数组组织形式的数据集合,像 numpy 数组一样工作,一个dataset即一个numpy.ndarray。具体的dataset可以是图像、表格,甚至是pdf文件和excel。

group:包含了其它 dataset(数组) 和 其它 group ,像字典一样工作。
一个h5文件被像linux文件系统一样被组织起来:dataset是文件,group是文件夹,它下面可以包含多个文件夹(group)和多个文件(dataset)。

  1. # Reading h5 file
  2. import h5py
  3. with h5py.File('cat_dog.h5',"r") as f:
  4. for key in f.keys():
  5. #print(f[key], key, f[key].name, f[key].value) # 因为这里有group对象它是没有value属性的,故会异常。另外字符串读出来是字节流,需要解码成字符串。
  6. print(f[key], key, f[key].name) # f[key] means a dataset or a group object. f[key].value visits dataset' value,except group object.
  7. """
  8. 结果:
  9. <HDF5 group "/dogs" (1 members)> dogs /dogs
  10. <HDF5 dataset "list_classes": shape (2,), type "|S7"> list_classes /list_classes
  11. <HDF5 dataset "train_set_x": shape (209, 64, 64, 3), type "|u1"> train_set_x /train_set_x
  12. <HDF5 dataset "train_set_y": shape (209,), type "<i8"> train_set_y /train_set_y
  13. 代码解析:
  14. 文件对象f它表示h5文件的根目录(root group),前面说了group是按字典的方式工作的,通过f.keys()来找到根目录下的所有dataset和group的key,然后通过key
  15. 来访问各个dataset或group对象。
  16. 结果解析:
  17. 1.我们可以发现这个h5文件下有1个叫dogs的文件夹(group)和3个文件(dataset)它们分别叫list_classes,train_set_x,train_set_y它们的shape都可知。
  18. dogs group下有一个成员但我们不知道它是group还是dataset。
  19. 2.我们可以发现key和name的区别:
  20. 上层group对象是通过key来访问下层dataset或group的而不是通过name来访问的;
  21. 因为name属性它是dataset或group的绝对路径并非是真正的"name",key才是真正的"name"。
  22. name绝对路径:比如下文中访问name得到:/dogs/husky,它表示根目录下有dogs这个挂载点,dogs下又挂载了husky。
  23. """
  24. dogs_group = f["dogs"]
  25. for key in dogs_group.keys():
  26. print(dogs_group[key], dogs_group[key].name)
  27. """
  28. 结果:
  29. <HDF5 dataset "husky": shape (64, 64, 3), type "<f8"> /dogs/husky
  30. 可见dogs文件夹下有个key为husky的文件dataset
  31. """

2、数据读取 

  1. def pointDataLoader(mode='train'):
  2. path = './dataset/'
  3. BATCHSIZE = 64
  4. MAX_POINT = 1024
  5. datas = []
  6. labels = []
  7. if mode == 'train':
  8. for file_list in train_list:
  9. f = h5py.File(os.path.join(path, file_list), 'r') #例如./dataset/ply_data_train0.h5,
  10. #读取h5文件
  11. #for key in f.keys():
  12. #print("aaaaaaaaaaaaaaaaaaaaaaaa")
  13. #print(f[key], key, f[key].name)
  14. #break
  15. datas.extend(f['data'][:, :1024, :]) #(2048,1024,3)的列表
  16. labels.extend(f['pid'][:, :1024]) #(2048,1024)的列表
  17. f.close()
  18. elif mode == 'test':
  19. for file_list in test_list:
  20. f = h5py.File(os.path.join(path, file_list), 'r')
  21. datas.extend(f['data'][:, :1024, :])
  22. labels.extend(f['pid'][:, :1024])
  23. f.close()
  24. else:
  25. for file_list in val_list:
  26. f = h5py.File(os.path.join(path, file_list), 'r')
  27. datas.extend(f['data'][:, :1024, :])
  28. labels.extend(f['pid'][:, :1024])
  29. f.close()
  30. datas = np.array(datas)
  31. labels = np.array(labels)
  32. index_list = list(range(len(datas)))
  33. def pointDataGenerator():
  34. if mode == 'train':
  35. random.shuffle(index_list)
  36. datas_list = []
  37. labels_list = []
  38. for i in index_list:
  39. data = np.reshape(datas[i], [1, 1024, 3]).astype('float32')
  40. label = np.reshape(labels[i], [1024]).astype('int64')
  41. datas_list.append(data)
  42. labels_list.append(label)
  43. if len(datas_list) == BATCHSIZE: #64个样本为一批
  44. #print(np.array(datas_list).shape) #(64,1,1024,3)
  45. #print(np.array(labels_list).shape) #(64,1024)
  46. yield np.array(datas_list), np.array(labels_list)
  47. datas_list = []
  48. labels_list = []
  49. if len(datas_list) > 0:
  50. yield np.array(datas_list), np.array(labels_list)
  51. return pointDataGenerator

补充注释:

1.

  1. for key in f.keys():
  2.    print("aaaaaaaaaaaaaaaaaaaaaaaa")
  3.    print(f[key], key, f[key].name)
aaaaaaaaaaaaaaaaaaaaaaaa
<HDF5 dataset "data": shape (2048, 2048, 3), type "<f4"> data /data
aaaaaaaaaaaaaaaaaaaaaaaa
<HDF5 dataset "label": shape (2048, 1), type "|u1"> label /label
aaaaaaaaaaaaaaaaaaaaaaaa
<HDF5 dataset "pid": shape (2048, 2048), type "|u1"> pid /pid

np.array(datas)  np.array(labels)的维度变化情况:

训练时: 

测试时:

预测时:

2.extend() 函数用于在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)。

③定义网络

1、定义网络

  1. class PointNet(paddle.nn.Layer):
  2. def __init__(self, name_scope='PointNet_', num_classes=50, num_point=1024):
  3. super(PointNet, self).__init__()
  4. self.num_point = num_point
  5. self.input_transform_net = Sequential(
  6. Conv2D(1, 64, (1, 3)),
  7. BatchNorm(64),
  8. ReLU(),
  9. Conv2D(64, 128, (1, 1)),
  10. BatchNorm(128),
  11. ReLU(),
  12. Conv2D(128, 1024, (1, 1)),
  13. BatchNorm(1024),
  14. ReLU(),
  15. MaxPool2D((num_point, 1))
  16. )
  17. self.input_fc = Sequential(
  18. Linear(1024, 512),
  19. ReLU(),
  20. Linear(512, 256),
  21. ReLU(),
  22. Linear(256, 9,
  23. weight_attr=paddle.framework.ParamAttr(initializer=paddle.nn.initializer.Assign(paddle.zeros((256, 9)))),
  24. bias_attr=paddle.framework.ParamAttr(initializer=paddle.nn.initializer.Assign(paddle.reshape(paddle.eye(3), [-1])))
  25. )
  26. )
  27. self.mlp_1 = Sequential(
  28. Conv2D(1, 64, (1, 3)),
  29. BatchNorm(64),
  30. ReLU(),
  31. Conv2D(64, 64,(1, 1)),
  32. BatchNorm(64),
  33. ReLU(),
  34. )
  35. self.feature_transform_net = Sequential(
  36. Conv2D(64, 64, (1, 1)),
  37. BatchNorm(64),
  38. ReLU(),
  39. Conv2D(64, 128, (1, 1)),
  40. BatchNorm(128),
  41. ReLU(),
  42. Conv2D(128, 1024, (1, 1)),
  43. BatchNorm(1024),
  44. ReLU(),
  45. MaxPool2D((num_point, 1))
  46. )
  47. self.feature_fc = Sequential(
  48. Linear(1024, 512),
  49. ReLU(),
  50. Linear(512, 256),
  51. ReLU(),
  52. Linear(256, 64*64)
  53. )
  54. self.mlp_2 = Sequential(
  55. Conv2D(64, 64, (1, 1)),
  56. BatchNorm(64),
  57. ReLU(),
  58. Conv2D(64, 128,(1, 1)),
  59. BatchNorm(128),
  60. ReLU(),
  61. Conv2D(128, 1024,(1, 1)),
  62. BatchNorm(1024),
  63. ReLU(),
  64. )
  65. self.seg_net = Sequential(
  66. Conv2D(1088, 512, (1, 1)),
  67. BatchNorm(512),
  68. ReLU(),
  69. Conv2D(512, 256, (1, 1)),
  70. BatchNorm(256),
  71. ReLU(),
  72. Conv2D(256, 128, (1, 1)),
  73. BatchNorm(128),
  74. ReLU(),
  75. Conv2D(128, 128, (1, 1)),
  76. BatchNorm(128),
  77. ReLU(),
  78. Conv2D(128, num_classes, (1, 1)),
  79. Softmax(axis=1)
  80. )
  81. def forward(self, inputs): #(64,1,1024,3)
  82. batchsize = inputs.shape[0]
  83. t_net = self.input_transform_net(inputs) #(64,1024,1,1)
  84. t_net = paddle.squeeze(t_net) #(64,1024)
  85. t_net = self.input_fc(t_net) #(64,9)
  86. t_net = paddle.reshape(t_net, [batchsize, 3, 3])#(64,3,3)
  87. x = paddle.squeeze(inputs)#(64,1024,3)
  88. x = paddle.matmul(x, t_net)#(64,1024,3)
  89. x = paddle.unsqueeze(x, axis=1)#(64,1,1024,3)
  90. x = self.mlp_1(x) #(64,64,1024,1)
  91. t_net = self.feature_transform_net(x)#(64,1024,1,1)
  92. t_net = paddle.squeeze(t_net)#(64,1024)
  93. t_net = self.feature_fc(t_net) #(64,64*64)
  94. t_net = paddle.reshape(t_net, [batchsize, 64, 64])#(64,64,64)
  95. x = paddle.squeeze(x)#(64,64,1024)
  96. x = paddle.transpose(x, (0, 2, 1))#(64,1024,64)
  97. x = paddle.matmul(x, t_net)#(64,1024,64)
  98. x = paddle.transpose(x, (0, 2, 1))#(64,64,1024)
  99. x = paddle.unsqueeze(x, axis=-1)#(64,64,1024,1)
  100. point_feat = x #点的局部特征
  101. x = self.mlp_2(x) #(64,1024,1024,1)
  102. x = paddle.max(x, axis=2) #(64,1024,1)#全局特征
  103. global_feat_expand = paddle.tile(paddle.unsqueeze(x, axis=1),
  104. [1, self.num_point, 1, 1]) #(64,1024,1024,1)
  105. x = paddle.concat([point_feat, global_feat_expand], axis=1)#(64,1088,1024,1)
  106. x = self.seg_net(x)#(64,50,1024,1)
  107. x = paddle.squeeze(x, axis=-1) #(64,50,1024)
  108. x = paddle.transpose(x, (0, 2, 1)) #(64,1024,50)
  109. return x

补充注释:

1.paddle.tile(x, repeat_times, name=None)

根据参数 repeat_times 对输入 x 的各维度进行复制。 平铺后,输出的第 i 个维度的值等于 x.shape[i]*repeat_times[i] 。

2、模型结构可视化

  1. pointnet = PointNet()
  2. paddle.summary(pointnet, (64, 1, 1024, 3))

⑤训练 

  1. def train():
  2. train_loader = pointDataLoader(mode='train')
  3. model = PointNet()
  4. model.train()
  5. optim = paddle.optimizer.Adam(parameters=model.parameters(), weight_decay=0.001)
  6. loss_fn = paddle.nn.CrossEntropyLoss()
  7. epoch_num = 200
  8. for epoch in range(epoch_num):
  9. for batch_id, data in enumerate(train_loader()):
  10. inputs = paddle.to_tensor(data[0])
  11. #print(data[0].shape) #(64,1,1024,3)
  12. labels = paddle.to_tensor(data[1])
  13. #print(data[1].shape) #(64,1024)
  14. predicts = model(inputs)
  15. loss = loss_fn(predicts, labels)
  16. #print("ggggggggggggggggggggggg")
  17. #print((paddle.argmax(predicts, axis=-1)).shape) #(64,1024)
  18. #mean_iou第一项维度为(64,1024,1),第二项也为(64,1024,1)
  19. miou, _, _ = paddle.fluid.layers.mean_iou(paddle.unsqueeze(paddle.argmax(predicts, axis=-1), axis=-1), paddle.unsqueeze(labels, axis=-1), 50)
  20. loss.backward()
  21. if batch_id % 100 == 0:
  22. print("epoch: {}, batch_id: {}, loss is: {}, miou is: {}".format(epoch, batch_id, loss.numpy(), miou.numpy()))
  23. optim.step()
  24. optim.clear_grad()
  25. if epoch % 20 == 0:
  26. paddle.save(model.state_dict(), './model/PointNet.pdparams')
  27. paddle.save(optim.state_dict(), './model/PointNet.pdopt')
  28. if __name__ == '__main__':
  29. train()

⑥评估 

  1. def evaluation():
  2. test_loader = pointDataLoader(mode='test')
  3. model = PointNet()
  4. model_state_dict = paddle.load('./model/PointNet.pdparams')
  5. model.load_dict(model_state_dict)
  6. for batch_id, data in enumerate(test_loader()):
  7. inputs = paddle.to_tensor(data[0])
  8. labels = paddle.to_tensor(data[1])
  9. predicts = model(inputs)
  10. loss = F.cross_entropy(predicts, labels)
  11. miou, _, _ = paddle.fluid.layers.mean_iou(paddle.unsqueeze(paddle.argmax(predicts, axis=-1), axis=-1), paddle.unsqueeze(labels, axis=-1), 50)
  12. # 打印信息
  13. if batch_id % 100 == 0:
  14. print("batch_id: {}, loss is: {}, miou is: {}".format(batch_id, loss.numpy(), miou.numpy()))
  15. if __name__ == '__main__':
  16. evaluation()

 ⑦预测

1、可视化预测样本

  1. zdata = []
  2. xdata = []
  3. ydata = []
  4. label = []
  5. f = h5py.File('./dataset/ply_data_val0.h5','r')
  6. for i in f['data'][0]:#(1024,3)
  7. xdata.append(i[0])
  8. ydata.append(i[1])
  9. zdata.append(i[2])
  10. for i in f['pid'][0]:#(1024)
  11. label.append(i)
  12. f.close()
  13. map_color = {0:'r', 1:'g', 2:'b', 3:'y'}
  14. Color = list(map(lambda x: map_color[x], label))#让不同类别的点绘制为不同颜色。
  15. xdata = np.array(xdata)
  16. ydata = np.array(ydata)
  17. zdata = np.array(zdata)
  18. from mpl_toolkits import mplot3d
  19. %matplotlib inline
  20. import matplotlib.pyplot as plt
  21. import numpy as np
  22. ax = plt.axes(projection='3d')#创建三维坐标轴
  23. ax.scatter3D(xdata, ydata, zdata, c=Color)#3D散点图,c为颜色
  24. plt.show()

补充注释:

1.     lambda x(传入的参数):要执行的语句(这条语句执行完之后,就会返回一个值,也就是函数的返回值) 

2、开始预测

  1. def test():
  2. test_loader = pointDataLoader(mode='val')
  3. model = PointNet()
  4. model_state_dict = paddle.load('./model/PointNet.pdparams')
  5. model.load_dict(model_state_dict)
  6. for batch_id, data in enumerate(test_loader()):
  7. predictdata = paddle.to_tensor(data[0]) #(64,1,1024,3)
  8. label = paddle.to_tensor(data[1]) #(64,1024)
  9. predict = model(predictdata)#(64,1024,50)
  10. # print(np.argmax(predict[0].numpy(), 1).shape) #(1024)
  11. break
  12. zdata = []
  13. xdata = []
  14. ydata = []
  15. label = []
  16. for i in data[0][0][0]:#(1024,3)
  17. xdata.append(i[0])
  18. ydata.append(i[1])
  19. zdata.append(i[2])
  20. for i in np.argmax(predict[0].numpy(), 1):#(1024)
  21. label.append(i)
  22. f.close()
  23. map_color = {0:'r', 1:'g', 2:'b', 3:'y'}
  24. Color = list(map(lambda x: map_color[x], label))
  25. xdata = np.array(xdata)
  26. ydata = np.array(ydata)
  27. zdata = np.array(zdata)
  28. from mpl_toolkits import mplot3d
  29. %matplotlib inline
  30. import matplotlib.pyplot as plt
  31. ax = plt.axes(projection='3d')
  32. ax.scatter3D(xdata, ydata, zdata, c=Color)
  33. plt.show()
  34. if __name__ == '__main__':
  35. test()

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号