• 最远点采样(Farthest Point Sampling,FPS)算法详解


      最远点采样(FSP)是一种常用的采样算法,主要用于点云数据(如激光雷达点云数据、分子坐标等)的采样。

    一:算法原理

      最远点采样的研究对象是点云数据,即一堆离散的坐标点。广义上其它许多样本数据类型也可以使用FPS算法并进行最远点采样,如我们经常使用的iris、dry bean dataset等数据集的数据类型,这些数据可以把每一条看做p维空间中的一个点,并且也可以用各种距离度量方法计算各条数据之间的距离。兔兔在这里为了方便,只针对三维点云数据进行实例讲解。

      FPS的核心思想是使得所有采样点之间的距离尽可能的远,也就是数据尽可能的离散均匀。例如对于数据(1,2,3,4,5,6,7,8,9),我们若需要采集3个点,第一个点为1,那么第二个点就需要选择与1最远的点,即9,第三个点需要与1和9都尽可能的远,即5。当然,如果我们继续取点,则需要与1,5,7距离都尽可能的远,即3或7。这个过程是我们的一种直观理解,对于这一类问题,我们需要一种确定的算法来进行采样。

      1.首先,对于三维点云数据,我们一般采取欧式距离度量点之间距离,即空间中两点直线距离。

      2.在对第一个点采样时,理论上我们可以随机从数据中选取一个点。另一种规范的做法是:求整个数据点(点云)的重心(即所有点坐标求和平均得到的坐标点),选取距离重心最远的点,记为P0。

      3. 然后,我们继续选取剩余的所有点中距离P0最远的点,记为P1。

      4. 对于剩下的每个点,分别计算到P0和P1的距离,并选取最短的那个作为这个点到P0,P1整体的距离。计算这些距离后选择距离最大的那个点,记为P2。

      5. 依次重复操作,直到选取所需数目的点。例如:继续选取点,分别计算剩余各点到P0,P1,P2的距离,并选取最短的那个距离作为某点到P1,P2,P3整体的距离,然后选取这些点中距离最大的那个点,记为P3。

     在算法实现的过程中,在第3步计算所有点到P0距离时,我们不妨定义一个一维数据L,L中各个位置的值表示各点到P0的距离,L中值最大的那个索引对应的点即为P1。寻找P2时,我们计算各点到P1距离,如果距离小于到P0的距离,就更新L数组中相应位置的值,否则不更改,计算好后L中值最大的那个索引对应的点即为P2,以此类推。

    二:算法实现

    1. import numpy as np
    2. def distance(p1,p2):
    3. return np.sqrt(np.sum((p1-p2)**2))
    4. def FPS(sample,num):
    5. '''sample:采样点云数据,
    6. num:需要采样的数据点个数'''
    7. n=sample.shape[0]
    8. center=np.mean(sample,axis=0) #点云重心
    9. select_p=[] #储存采集点索引
    10. L=[]
    11. for i in range(n):
    12. L.append(distance(sample[i],center))
    13. p0=np.argmax(L)
    14. select_p.append(p0) #选距离重心最远点p0
    15. L=np.zeros(shape=n)
    16. for i in range(num-1):
    17. for p in range(n):
    18. d = []
    19. for j in select_p:
    20. d.append(distance(sample[j],sample[p]))
    21. d=min(d)
    22. L[p]=d
    23. select_p.append(np.argmax(L))
    24. return select_p,sample[select_p]
    25. point=np.random.randint(0,20,size=(200,3))
    26. index,select_sample=FPS(sample=point,num=100)
    27. print(index,select_sample)

    当然,上述代码虽然比较直观,并且能够实现FPS采样,但是每一次都需要重复计算点到各采样点的值,这样会使程序运行速度慢,兔兔运行时需要8.52s。

    1. import numpy as np
    2. def distance(p1,p2):
    3. return np.sqrt(np.sum((p1-p2)**2))
    4. def FPS(sample,num):
    5. '''sample:采样点云数据,
    6. num:需要采样的数据点个数'''
    7. n=sample.shape[0]
    8. center=np.mean(sample,axis=0) #点云重心
    9. select_p=[] #储存采集点索引
    10. L=[]
    11. for i in range(n):
    12. L.append(distance(sample[i],center))
    13. p0=np.argmax(L)
    14. select_p.append(p0) #选距离重心最远点p0
    15. L=[]
    16. for i in range(n):
    17. L.append(distance(p0,sample[i]))
    18. select_p.append(np.argmax(L))
    19. for i in range(num-2):
    20. for p in range(n):
    21. d=distance(sample[select_p[-1]],sample[p])
    22. if d<=L[p]:
    23. L[p]=d
    24. select_p.append(np.argmax(L))
    25. return select_p,sample[select_p]
    26. index,select_sample=FPS(sample=point,num=100)
    27. print(index,select_sample)

    改进后代码的运行时间约为0.24s,时间明显缩短许多。

    三:总结

    FPS作为一种采样算法,够对一些数据进行有效的采样,在实际问题中具有广泛应用,例如:当今十分热门的PointNet++中也使用了这种方法,我想很多同学也是因为PointNet++等模型来学习FPS算法,但是FPS的应用远远不止局限于此,它的这种方法与思想仍可以更广泛地应用于其它方面。

  • 相关阅读:
    Jina AI正式将DocArray捐赠给Linux基金会
    实现IP地址归属地显示功能、号码归属地查询
    使用Docker部署Gitlab的记录
    【如何学习CAN总线测试】——AUTOSAR网络管理测试
    来吧元宇宙,果然这热度一时半会儿过不去了
    CY3-NHS ester良好的光稳定性介绍1032678-38-8
    从零开始!Jupyter Notebook的安装教程
    多智能体强化学习整理
    类别不均衡,离群点以及分布改变
    LeetCode 2562. 找出数组的串联值:模拟(双指针)
  • 原文地址:https://blog.csdn.net/weixin_60737527/article/details/127432708