点云的分类是将点云分类到不同的点云集。同一个点云集具有相似或相同的属性,例如地面、树木、人等;点云分割是根据空间、几何和纹理等特征点进行划分,同一划分内的点云拥有相似的特征。
基于半径选择局部区域,针对得到的每个区域进行特征提取,关键核心原理:
def farthest_point_sample(xyz, npoint):
"""
Input:
xyz: pointcloud data, [B, N, 3]
npoint: number of samples
Return:
centroids: sampled pointcloud index, [B, npoint]
"""
device = xyz.device
B, N, C = xyz.shape
centroids = torch.zeros(B, npoint, dtype=torch.long).to(device)#8*512
distance = torch.ones(B, N).to(device) * 1e10 #8*1024
farthest = torch.randint(0, N, (B,), dtype=torch.long).to(device)#batch里每个样本随机初始化一个最远点的索引
batch_indices = torch.arange(B, dtype=torch.long).to(device)
for i in range(npoint):
centroids[:, i] = farthest #第一个采样点选随机初始化的索引
centroid = xyz[batch_indices, farthest, :].view(B, 1, 3)#得到当前采样点的坐标 B*3
dist = torch.sum((xyz - centroid) ** 2, -1)#计算当前采样点与其他点的距离
mask = dist < distance#选择距离最近的来更新距离(更新维护这个表)
distance[mask] = dist[mask]#
farthest = torch.max(distance, -1)[1]#重新计算得到最远点索引(在更新的表中选择距离最大的那个点)
return centroids
def query_ball_point(radius, nsample, xyz, new_xyz):
"""
Input:
radius: local region radius
nsample: max sample number in local region
xyz: all points, [B, N, 3]
new_xyz: query points, [B, S, 3]
Return:
group_idx: grouped points index, [B, S, nsample]
"""
device = xyz.device
B, N, C = xyz.shape
_, S, _ = new_xyz.shape
group_idx = torch.arange(N, dtype=torch.long).to(device).view(1, 1, N).repeat([B, S, 1])
sqrdists = square_distance(new_xyz, xyz)#得到B N M (就是N个点中每一个和M中每一个的欧氏距离)
group_idx[sqrdists > radius ** 2] = N #找到距离大于给定半径的设置成一个N值(1024)索引
group_idx = group_idx.sort(dim=-1)[0][:, :, :nsample]#做升序排序,后面的都是大的值(1024)
group_first = group_idx[:, :, 0].view(B, S, 1).repeat([1, 1, nsample])#如果半径内的点没那么多,就直接用第一个点来代替了。。。
mask = group_idx == N
group_idx[mask] = group_first[mask]
return group_idx
特征提取代码实现如下:
def forward(self, xyz, points):
"""
Input:
xyz: input points position data, [B, C, N]
points: input points data, [B, D, N]
Return:
new_xyz: sampled points position data, [B, C, S]
new_points_concat: sample points feature data, [B, D', S]
"""
xyz = xyz.permute(0, 2, 1) #就是坐标点位置特征
print(xyz.shape)
if points is not None:
points = points.permute(0, 2, 1) ##就是额外提取的特征,第一次的时候就是那个法向量特征
print(points.shape)
B, N, C = xyz.shape
S = self.npoint
new_xyz = index_points(xyz, farthest_point_sample(xyz, S))
print(new_xyz.shape)
new_points_list = []
for i, radius in enumerate(self.radius_list):
K = self.nsample_list[i]
group_idx = query_ball_point(radius, K, xyz, new_xyz)#返回的是索引
grouped_xyz = index_points(xyz, group_idx)#得到各个组中实际点
grouped_xyz -= new_xyz.view(B, S, 1, C)#去mean new_xyz相当于簇的中心点
if points is not None:
grouped_points = index_points(points, group_idx)
grouped_points = torch.cat([grouped_points, grouped_xyz], dim=-1)
print(grouped_points.shape)
else:
grouped_points = grouped_xyz
grouped_points = grouped_points.permute(0, 3, 2, 1) # [B, D, K, S]
print(grouped_points.shape)
for j in range(len(self.conv_blocks[i])):
conv = self.conv_blocks[i][j]
bn = self.bn_blocks[i][j]
grouped_points = F.relu(bn(conv(grouped_points)))
print(grouped_points.shape)
new_points = torch.max(grouped_points, 2)[0] # [B, D', S] 就是pointnet里的maxpool操作
print(new_points.shape)
new_points_list.append(new_points)
new_xyz = new_xyz.permute(0, 2, 1)
new_points_concat = torch.cat(new_points_list, dim=1)
print(new_points_concat.shape)
return new_xyz, new_points_concat
点云分割算法主要基于从几何约束和统计规则出发制定的严格的人工设计的特征。点云分割的主要过程是将原始3D点云分组为非重叠区域。
pointnet得到最终整体特征,再进行分割
如果需要本文完整项目代码,以上算法论文或者点云数据集的小伙伴可以私信我哦!