当前位置:   article > 正文

[PointNet代码详解]PointNet各模块代码实现超详细注释

pointnet代码

pointnet.py pointnet模型各个模块的实现

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.parallel
  4. import torch.utils.data
  5. from torch.autograd import Variable
  6. import numpy as np
  7. import torch.nn.functional as F
  8. # STN3d: T-Net 3*3 transform
  9. # 类似一个mini-PointNet
  10. class STN3d(nn.Module):
  11.   def __init__(self, channel):
  12.       super(STN3d, self).__init__()
  13.       # torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
  14.       self.conv1 = torch.nn.Conv1d(channel, 64, 1)
  15.       self.conv2 = torch.nn.Conv1d(64, 128, 1)#channel为输入他通道数, 为3指的是输入点云特征的三个通道(X,Y,Z),channel=6表示6个通道(X.Y.Z.和X,Y,Z方向的法向量)
  16.       self.conv3 = torch.nn.Conv1d(128, 1024, 1)
  17.       self.fc1 = nn.Linear(1024, 512)
  18.       self.fc2 = nn.Linear(512, 256)
  19.       self.fc3 = nn.Linear(256, 9) # 9=3*3
  20.       self.relu = nn.ReLU()
  21.       self.bn1 = nn.BatchNorm1d(64)#输入特征的尺度会影响梯度下降算法的迭代步数以及梯度更新的难度,从而影响训练的收敛性。因此,我们需要对特征进行归一化,即使得各个特征有相似的尺度。
  22.       self.bn2 = nn.BatchNorm1d(128)
  23.       self.bn3 = nn.BatchNorm1d(1024)
  24.       self.bn4 = nn.BatchNorm1d(512)
  25.       self.bn5 = nn.BatchNorm1d(256)
  26.   def forward(self, x):#前向传播函数
  27.       batchsize = x.size()[0]
  28.       x = F.relu(self.bn1(self.conv1(x)))
  29.       x = F.relu(self.bn2(self.conv2(x)))
  30.       x = F.relu(self.bn3(self.conv3(x)))
  31.       # Symmetric function: max pooling
  32.       x = torch.max(x, 2, keepdim=True)[0]#最大池化 这里得到了一个全局的特征
  33.       # x参数展平(拉直)
  34.       x = x.view(-1, 1024) #将全局特征展成一个1024列的特征,其中-1代表相应的行
  35.       x = F.relu(self.bn4(self.fc1(x)))#1024降维到512后对512维的特征做相应的bn4(512)归一化之后进行relu的激活函数进行非线性。
  36.       x = F.relu(self.bn5(self.fc2(x)))
  37.       x = self.fc3(x)#9个元素
  38.       # 展平的对角矩阵:np.array([1, 0, 0, 0, 1, 0, 0, 0, 1])
  39.       iden = Variable(torch.from_numpy(np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]).astype(np.float32))).view(1, 9).repeat(
  40.           batchsize, 1)
  41.       if x.is_cuda:
  42.           iden = iden.cuda()
  43.       x = x + iden # affine transformation,仿射变换,简单来说,“仿射变换”就是:“线性变换”+“平移”。
  44.       # 用view,转换成batchsize*3*3的数组
  45.       x = x.view(-1, 3, 3)
  46.       return x
  47. # STNkd: T-Net 64*64 transform,k默认是64
  48. class STNkd(nn.Module):
  49.   def __init__(self, k=64):
  50.       super(STNkd, self).__init__()
  51.       self.conv1 = torch.nn.Conv1d(k, 64, 1)
  52.       self.conv2 = torch.nn.Conv1d(64, 128, 1)
  53.       self.conv3 = torch.nn.Conv1d(128, 1024, 1)
  54.       self.fc1 = nn.Linear(1024, 512)
  55.       self.fc2 = nn.Linear(512, 256)
  56.       self.fc3 = nn.Linear(256, k * k)
  57.       self.relu = nn.ReLU()
  58.       self.bn1 = nn.BatchNorm1d(64)
  59.       self.bn2 = nn.BatchNorm1d(128)
  60.       self.bn3 = nn.BatchNorm1d(1024)
  61.       self.bn4 = nn.BatchNorm1d(512)
  62.       self.bn5 = nn.BatchNorm1d(256)
  63.       self.k = k
  64.   def forward(self, x):
  65.       batchsize = x.size()[0]
  66.       x = F.relu(self.bn1(self.conv1(x)))
  67.       x = F.relu(self.bn2(self.conv2(x)))
  68.       x = F.relu(self.bn3(self.conv3(x)))
  69.       # Symmetric function: max pooling
  70.       x = torch.max(x, 2, keepdim=True)[0]
  71.       # 参数拉直(展平)
  72.       x = x.view(-1, 1024)
  73.       x = F.relu(self.bn4(self.fc1(x)))
  74.       x = F.relu(self.bn5(self.fc2(x)))
  75.       x = self.fc3(x)
  76.       # 展平的对角矩阵
  77.       iden = Variable(torch.from_numpy(np.eye(self.k).flatten().astype(np.float32))).view(1, self.k * self.k).repeat(
  78.           batchsize, 1)
  79.       if x.is_cuda:
  80.           iden = iden.cuda()
  81.       x = x + iden # affine transformation
  82.       x = x.view(-1, self.k, self.k)
  83.       return x
  84. # PointNet编码器
  85. class PointNetEncoder(nn.Module):
  86.   def __init__(self, global_feat=True, feature_transform=False, channel=3):
  87.       super(PointNetEncoder, self).__init__()
  88.       self.stn = STN3d(channel) # STN3d: T-Net 3*3 transform
  89.       self.conv1 = torch.nn.Conv1d(channel, 64, 1)
  90.       self.conv2 = torch.nn.Conv1d(64, 128, 1)
  91.       self.conv3 = torch.nn.Conv1d(128, 1024, 1)
  92.       self.bn1 = nn.BatchNorm1d(64)
  93.       self.bn2 = nn.BatchNorm1d(128)
  94.       self.bn3 = nn.BatchNorm1d(1024)
  95.       self.global_feat = global_feat #是否需要全局的特征
  96.       self.feature_transform = feature_transform#是否需要特征变换
  97.       if self.feature_transform:
  98.           self.fstn = STNkd(k=64) # STNkd: T-Net 64*64 transform
  99.   def forward(self, x):
  100.       B, D, N = x.size() # batchsize,3(xyz坐标)或6(xyz坐标+法向量),1024(一个物体所取的点的数目)
  101.       trans = self.stn(x) # STN3d T-Net
  102.       x = x.transpose(2, 1) # 交换一个tensor的两个维度
  103.       if D >3 :
  104.           x, feature = x.split(3,dim=2)
  105.       # 对输入的点云进行输入转换(input transform)    
  106.       # input transform: 计算两个tensor的矩阵乘法
  107.       # bmm是两个三维张量相乘, 两个输入tensor维度是(b×n×m)和(b×m×p),
  108.       # 第一维b代表batch size,输出为(b×n×p)
  109.       x = torch.bmm(x, trans)
  110.       if D > 3:
  111.           x = torch.cat([x,feature],dim=2) #矩阵的拼接,0代表竖着拼接,1代表横着拼接
  112.       x = x.transpose(2, 1)
  113.       x = F.relu(self.bn1(self.conv1(x))) # MLP
  114.       if self.feature_transform:
  115.           trans_feat = self.fstn(x) # STNkd T-Net
  116.           x = x.transpose(2, 1)
  117.           # 对输入的点云进行特征转换(feature transform)
  118.           # feature transform: 计算两个tensor的矩阵乘法
  119.           x = torch.bmm(x, trans_feat)
  120.           x = x.transpose(2, 1)
  121.       else:
  122.           trans_feat = None
  123.       pointfeat = x # 局部特征
  124.       x = F.relu(self.bn2(self.conv2(x))) # MLP
  125.       x = self.bn3(self.conv3(x)) # MLP
  126.       x = torch.max(x, 2, keepdim=True)[0] # 最大池化得到全局特征
  127.       x = x.view(-1, 1024) # 展平
  128.       if self.global_feat: # 需要返回的是否是全局特征?
  129.           return x, trans, trans_feat # 返回全局特征
  130.       else:
  131.           x = x.view(-1, 1024, 1).repeat(1, 1, N)
  132.           # 返回局部特征与全局特征的拼接
  133.           return torch.cat([x, pointfeat], 1), trans, trans_feat
  134. # 对特征转换矩阵做正则化:
  135. # constrain the feature transformation matrix to be close to orthogonal matrix
  136. def feature_transform_reguliarzer(trans): #让feature transform接近于一个正交的矩阵
  137.   d = trans.size()[1]
  138.   I = torch.eye(d)[None, :, :] # torch.eye(n, m=None, out=None) 返回一个2维张量,对角线位置全1,其它位置全0。单位阵
  139.   if trans.is_cuda:
  140.       I = I.cuda()
  141.        
  142.   # 正则化损失函数
  143.   loss = torch.mean(torch.norm(torch.bmm(trans, trans.transpose(2, 1) - I), dim=(1, 2))) #a*a的转置-单位阵后求范数后再平均=正则化的loss
  144.   return loss#用来将特征转换矩阵做正则化的约束
无序的。输入是欧几里德空间中点的子集。它有三个主要的属性:与图像中的像素阵列或体素网格中的体素阵列不同,点云是一组没有特定顺序的点。

点之间的交互作用。这些点来自一个有距离度量的空间。它意味着点不是孤立的,相邻点形成一个有意义的子集。因此,该模型需要能够从附近的点捕获局部结构,以及局部结构之间的组合相互作用。

变换不变性。作为一个几何对象,学习到的点集表示应该对某些变换是不变的。例如,同时旋转和平移点不应该改变全局点云的类别,也不应该改变点的分割。

它采用了两次STN(Spatial Transformer Networks),第一次input transform是对空间中点云进行调整,直观上理解是旋转出一个更有利于分类或分割的角度,比如把物体转到正面;第二次feature transform是对提取出的64维特征进行对齐,即在特征层面对点云进行变换。

网络分成了分类网络和分割网络两个部分,大体思路类似,都是设计表征的过程分类网络设计global feature,分割网络设计point-wise feature两者都是为了让表征尽可能discriminative,也就是同类的能分到一类,不同类的距离能拉开。

conclusion

第一篇直接用于点云数据处理的三为点云分割网络,结构简单,逻辑清晰,秒杀之前多视图,体素等方法。不用过多的去进行数据预处理。大大降低了点云模型的复杂度。影响力颇深。

2023.3.27 记录James.King缺席13场比赛后替补复出。出战29’31‘’拿下19分8篮板3助攻惜败公牛

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

闽ICP备14008679号