一、算法原理
1、算法概述
Farthest Point Sampling (FPS):顾名思义,也就是每次采样的时候都选择离之前采样得到的 k − 1 k-1 k−1个点距离最远的点。 F P S FPS FPS能够比较好地保证采样后的点具有较好的覆盖率,因而在点云分割领域被广泛地使用(e.g., PointNet++, PointCNN, PointConv, PointWeb)。然而,FPS的计算复杂度是 ,计算量与输入点云的点数呈平方相关。这表明从FPS可能不适合用来处理大规模点云。举例来说,当输入一个具有百万量级点的大场景点云时,使用FPS将其降采样到原始规模的10%需要多达200秒。
2、实现流程
- 从点云中随机挑选一个点作为初始点;
- 迭代计算距离已选点的距离之和最远的点,将其选入FPS集合。
3、参考文献
[1] Hu Q , Yang B , Xie L , et al. RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds[J]. 2019.
二、代码实现
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
using namespace std;
// 获取最远采样点的索引
template<typename PointT> inline int
getMaxDistanceIdx(const pcl::PointCloud<PointT>& cloud, const Eigen::Vector3f& pivot_pt3)
{
float max_dist = -FLT_MAX;
int max_idx = -1;
float dist;
for (std::size_t i = 0; i < cloud.size(); ++i)
{
pcl::Vector3fMapConst pt = cloud[i].getVector3fMap();
dist = (pivot_pt3 - pt).norm();
if (dist > max_dist)
{
max_idx = int(i);
max_dist = dist;
}
}
return max_idx;
}
int main(int argc, char** argv)
{
// ---------------------------加载点云数据--------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile<pcl::PointXYZ>("data//dragon1.pcd", *cloud);
cout << "从点云数据中读取" << cloud->points.size() << "个点" << endl;
// 输入采样点的个数
int sample_num = 9000;
// ----------------------------最远点采样---------------------------------------
if (sample_num >= cloud->points.size())
{
PCL_ERROR("采样点个数大于原始点云的点数,无法进行采样!!!");
std::exit;
}
pcl::PointCloud<pcl::PointXYZ>::Ptr sample_cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 随机选取一个点
pcl::IndicesPtr indices(new pcl::Indices(cloud->size()));
const auto nr_points = indices->size();
srand((unsigned)time(NULL));
auto* index = &(*indices)[rand() % nr_points];
Eigen::Vector3f samplePoint = cloud->points[*index].getVector3fMap();
cloud->erase(cloud->begin() + *index);
sample_cloud->push_back(cloud->points[*index]);
// 遍历获取彼此距离最远的点
for (int j = 0; j < sample_num - 1; ++j)
{
const Eigen::Vector3f pivot_pt31 = sample_cloud->points[j].getVector3fMap();
int max_idx = getMaxDistanceIdx(*cloud, pivot_pt31);
cloud->erase(cloud->begin() + max_idx);
sample_cloud->push_back(cloud->points[max_idx]);
}
// 保存结果
pcl::io::savePCDFileBinary("FPSsample.pcd", *sample_cloud);
return (0);
}