最远点采样(Farthest Point Sampling)介绍

最远点采样(Farthest Point Sampling)是一种非常常用的采样算法,由于能够保证对样本的均匀采样,被广泛使用,像3D点云深度学习框架中的PointNet++对样本点进行FPS采样再聚类作为感受野,3D目标检测网络VoteNet对投票得到的散乱点进行FPS采样再进行聚类,6D位姿估计算法PVN3D中用于选择物体的8个特征点进行投票并计算位姿。

对于FPS算法的原理,两篇博文:PointNet++的pytorch实现代码阅读,以及 PointNet++详解与代码
已经介绍的非常详细了,一句话概括就是不断迭代地选择距离已有采样点集合的最远点。这里再介绍一遍,并实测分析一下;

FPS算法原理:

  1. 输入点云有N个点,从点云中选取一个点P0作为起始点,得到采样点集合S={P0};
  2. 计算所有点到P0的距离,构成N维数组L,从中选择最大值对应的点作为P1,更新采样点集合S={P0,P1};
  3. 计算所有点到P1的距离,对于每一个点Pi,其距离P1的距离如果小于L[i],则更新L[i] = d(Pi, P1),因此,数组L中存储的一直是每一个点到采样点集合S的最近距离;
  4. 选取L中最大值对应的点作为P2,更新采样点集合S={P0,P1,P2};
  5. 重复2-4步,一直采样到N’个目标采样点为止。

有两个问题,一个是初始点选择,一个是采用的距离度量;

  • 初始点选择:
    • 随机选择一个点,每次结果不同;
    • 选择距离点云重心的最远点,每次结果相同,一般位于局部极值点,具有刻画能力;
  • 距离度量
    • 欧氏距离:主要对于点云,在3D体空间均匀采样;
    • 测地距离:主要对于三角网格,在三角网格面上进行均匀采样;

下面给出测试样例,也来自上面两篇博文:

from __future__ import print_function
import torch
from torch.autograd import Variable

def farthest_point_sample(xyz, npoint): 

    """
    Input:
        xyz: pointcloud data, [B, N, 3]
        npoint: number of samples
    Return:
        centroids: sampled pointcloud index, [B, npoint]
    """

    xyz = xyz.transpose(2,1)
    device = xyz.device
    B, N, C = xyz.shape
    
    centroids = torch.zeros(B, npoint, dtype=torch.long).to(device)     # 采样点矩阵(B, npoint)
    distance = torch.ones(B, N).to(device) * 1e10                       # 采样点到所有点距离(B, N)

    batch_indices = torch.arange(B, dtype=torch.long).to(device)        # batch_size 数组

    #farthest = torch.randint(0, N, (B,), dtype=torch.long).to(device)  # 初始时随机选择一点
    
    barycenter = torch.sum((xyz), 1)                                    #计算重心坐标 及 距离重心最远的点
    barycenter = barycenter/xyz.shape[1]
    barycenter = barycenter.view(B, 1, 3)

    dist = torch.sum((xyz - barycenter) ** 2, -1)
    farthest = torch.max(dist,1)[1]                                     #将距离重心最远的点作为第一个点

    for i in range(npoint):
        print("-------------------------------------------------------")
        print("The %d farthest pts %s " % (i, farthest))
        centroids[:, i] = farthest                                      # 更新第i个最远点
        centroid = xyz[batch_indices, farthest, :].view(B, 1, 3)        # 取出这个最远点的xyz坐标
        dist = torch.sum((xyz - centroid) ** 2, -1)                     # 计算点集中的所有点到这个最远点的欧式距离
        print("dist    : ", dist)
        mask = dist < distance
        print("mask %i : %s" % (i,mask))
        distance[mask] = dist[mask]                                     # 更新distance,记录样本中每个点距离所有已出现的采样点的最小距离
        print("distance: ", distance)

        farthest = torch.max(distance, -1)[1]                           # 返回最远点索引
 
    return centroids

if __name__ == '__main__':

    sim_data = Variable(torch.rand(1,3,8))
    print(sim_data)

    centroids = farthest_point_sample(sim_data, 4)
    
    print("Sampled pts: ", centroids)

输出结果如下,可以看到得到的mask只有True的情况下,才会用当前计算的dist替换distance,这样distance中存储的一直是每一个点到采样点集合的最近距离,从中选择最大值就找到了下一个采样点。

tensor([[[0.4442, 0.7545, 0.8402, 0.1476, 0.6654, 0.1622, 0.1818, 0.3758],
         [0.0328, 0.3010, 0.0910, 0.4768, 0.6825, 0.3810, 0.1923, 0.4230],
         [0.5849, 0.4576, 0.5159, 0.0993, 0.3962, 0.1519, 0.0429, 0.3123]]])
-------------------------------------------------------
The 0 farthest pts tensor([2]) 
dist    :  tensor([[0.1649, 0.0548, 0.0000, 0.8021, 0.3947, 0.6762, 0.6675, 0.3673]])
mask 0 : tensor([[True, True, True, True, True, True, True, True]])
distance:  tensor([[0.1649, 0.0548, 0.0000, 0.8021, 0.3947, 0.6762, 0.6675, 0.3673]])
-------------------------------------------------------
The 1 farthest pts tensor([3]) 
dist    :  tensor([[0.5210, 0.5276, 0.8021, 0.0000, 0.3985, 0.0122, 0.0853, 0.1003]])
mask 1 : tensor([[False, False, False,  True, False,  True,  True,  True]])
distance:  tensor([[0.1649, 0.0548, 0.0000, 0.0000, 0.3947, 0.0122, 0.0853, 0.1003]])
-------------------------------------------------------
The 2 farthest pts tensor([4]) 
dist    :  tensor([[0.5067, 0.1572, 0.3947, 0.3985, 0.0000, 0.4038, 0.5989, 0.1583]])
mask 2 : tensor([[False, False, False, False,  True, False, False, False]])
distance:  tensor([[0.1649, 0.0548, 0.0000, 0.0000, 0.0000, 0.0122, 0.0853, 0.1003]])
-------------------------------------------------------
The 3 farthest pts tensor([0]) 
dist    :  tensor([[0.0000, 0.1844, 0.1649, 0.5210, 0.5067, 0.3882, 0.3881, 0.2312]])
mask 3 : tensor([[ True, False, False, False, False, False, False, False]])
distance:  tensor([[0.0000, 0.0548, 0.0000, 0.0000, 0.0000, 0.0122, 0.0853, 0.1003]])
Sampled pts:  tensor([[2, 3, 4, 0]])

猜你喜欢

转载自blog.csdn.net/dsoftware/article/details/107184116