1. 点云深度学习中的新下采样方法 (CSDN)
现在比较常见的下采样算法有:farthest point sampling(PointNet++,ShellNet)、random sampling(RandLA-Net)、grid sampling(KPConv,Grid-GCN)等。它们各有特点:
farthest point sampling(FPS):采样点分布均匀,时间复杂度高 (Farthest Point Sampling in 3D Object Detection) ((FPS)算法核心思想解析) (Fast Marching farthest point sampling) (图解点云深度学习中FPS–最远点采样算法)
grid sampling(GS):采样点分布较为均匀,时间复杂度一般,采样点个数具有不确定性
random sampling(RS):采样点分布具有随机性,时间复杂度低
(1) Adaptive Hierarchical Down-Sampling (CVPR2020) (arxiv)
a. Critical Points Layer (CPL)
b. Weighted Critical Points Layer (WCPL)
(2) Adaptive Sampling (CVPR2020) (arxiv)
首先利用FPS进行采样,然后根据利用KNN获取邻域点,之后对k个邻域点进行自适应加权(attention的思想,权重的计算是直接对每个点应用MLP),求取加权平均值即为新的关键点。这种方式提升了对噪声点的鲁棒性,同时将邻域的几何空间特征引入到了采样过程中。
2. LiDAR 360 点云重采样方法 (LiDAR 360)
最小点间距(默认,默认为“0.0000”):用户需要设置两点之间的最小点间距,这样采样后的点云任意两个点之间空间三维的最小距离不会小于该值。设置的值越大,保留的点越少。
采样率(默认为“99.99%”):需要用户设置保留点数的百分比,在此模式下,LiDAR360会随机的保留指定的点数。保留的点数 = 总点数 * 采样率。该参数的取值范围为0 - 100%,设置的值越小,保留的点越少。
八叉树(Octree)(默认为21):该模式允许用户选择一种“八叉树”的细分级别,在这个级别上,对于每个八叉树的细胞中,将会保留最接近于八叉树细胞中心的位置点。范围:1~21。设置的值越小,保留的点越少。
3. MATLAB 点云降采样方法 (pcdownsample)
ptCloudOut = pcdownsample(ptCloudIn,‘random’,percentage) random 返回具有随机采样且不替换的降采样点云。百分比输入指定要返回到输出的输入部分。 (可以动态调整采样率,输出特定点数N的采样数据)
ptCloudOut = pcdownsample(ptCloudIn,‘gridAverage’,gridStep) gridAverage 使用网格过滤器返回下采样点云。 gridStep 输入指定一个3D box的大小。(迭代采样间隔,可以获得>=最小采样点数N的采样结果)
ptCloudOut = pcdownsample(ptCloudIn,‘nonuniformGridSample’,maxNumPoints) nonuniformGridSample 使用非均匀盒网格滤波器返回下采样点云。必须在网格框中设置最大点数,maxNumPoints最小设置为6. (参数maxNumPoints从最小值6开始累加,找到输出的采样点云数为目标个数N时停止迭代,导出结果)
4、通过其他聚类算法实现点云降采样 (scikit-learn)
K-Means (K-Means聚类算法(一):算法思路(zhihu))
用人话讲明白快速聚类kmeans (zhihu)
点云聚类 (Tophat 观察 / PointCloudTutorial)
回波强度约束下的无人机LiDAR点云K-means聚类滤波 (地球信息科学学报)
K-Means聚类算法原理与实现 (前端矿场)
-
DBSCAN algorithm identifies the dense region
DBSCAN algorithm identifies the dense region by grouping together data points that are closed to each other based on distance measurement.
DBSCAN Clustering Algorithm in Machine Learning (kdnuggets)
Clustering with DBSCAN, Clearly Explained!!! (youtube)
DBSCAN Algorithm Clustering in Python (section) -
Random Sample Consensus (RANSAC) algorithm
Parallel-RANSAC (github)
Parallel RANSAC: Speeding up plane extraction in RGBD image sequences using GPU -
GeometryHub
点云采样的方法有很多种,常见的有均匀采样,几何采样,随机采样,格点采样等。
1 概述
三维点云往往包含大量冗余数据,直接处理计算量大,消耗时间长,因此对其进行降采样是十分必要的。降采样同时也是点云预处理过程中的关键环节。
2 常用方法
2.1 体素网格下采样
2.1.1 原理
体素(Voxel):将三维空间划分成一个个立体的方格,每个方格就叫一个体素。
在每个体素中可能存在几个点,也可能没有点。降采样的思路为:检查每个体素中是否有点存在,若有,则用一个点代替体素内的点集,通常,这个采样点可以是体素中所有点坐标的平均值(质心),也可以是中心点或者离中心点最近的点。
2.1.2 流程
计算点云的包围盒,将包围盒离散成小体素。体素的长宽高尺寸可以通过用户设定,也可以指定三个方向上的体素个数;
获取落在每个体素中的点集,在每个体素中取一个采样点代替原来的点集。
2.1.3 特点
效率高
采样点分布比较均匀
可以通过控制体素大小间接控制采样点的距离(采样后点云的稀疏程度)
采集到的点云数量不可控
2.2 均匀下采样
2.2.1 原理
均匀下采样有多种不同的采样方式。
类似体素网格采样,同样需要将点云空间划分,不同的是,在均匀采样中是以半径为r的球体。选取距离球体中心最近的点作为采样点替代落在球体中的点集,此时,采样点的坐标为源点云中一点的坐标,不同于体素下采样中使用质心进行替代而产生新的点坐标。可以修改球体半径r,实现对采样点云稀疏程度的控制。
均匀采样是指每隔固定的点数采样一次。样本按点的顺序执行,始终选择从第 1 个点开始,而不是随机选择。显然点存储的顺序不同,得到的结果也会不一样。从这个角度来看,这种方法比较适合有序点云的降采样。这种方法适合均匀采集到的点云,如果点云本身不均匀,那么以固定点数采样很有可能造成某一部分的点云没被采样到。相比于体素的采样方法,点云均匀采样后的点数是固定可控的,而体素采样后的点云数量是不可控的。
最远点采样是较为简单的一种,首先需要选取一个种子点,并设置一个内点集合,每次从点云中不属于内点的集合找出一点距离内点最远的点,如下图,这里的距离计算方式为该点至内点所有点的最小距离。这种方式的下采样点云分布均匀,但是算法复杂度较高效率低。
2.2.2 特点
分布很均匀
不会移动源点云,准确度较高
时间复杂度偏高。可以采用分治的方法提高效率
2.3 几何采样(曲率下采样)
2.3.1 原理
以点云的几何特征作为采样依据,以曲率为例。在点云中任意一点都存在某曲面,曲率示意图如下所示(密切圆半径r的倒数为曲率),曲率越大,弧的弯曲程度越大,表示该地方的特征点越多,故在点云曲率越大的地方,采样点数越多。
2.3.2 流程
1)首先计算每个点的K领域,然后计算点到领域点的法线夹角值,以此来近似达到曲率的效果并提高计算效率,因为曲率越大的地方,夹角值越大。
2)设置一个角度阈值,当点的领域夹角值大于阈值时被认为是特征明显的区域,其余区域为不明显区域。
3)对明显和不明显区域进行均匀采样,采样数分别为U ( 1 − V ) 和 U V UV UV, U U U是目标采样数,V是均匀采样性。
2.3.3特点
几何特征越明显的区域,采样个数分布越多(曲率越大,采样点数越多)
效率高
采样点局部均匀分布
稳定性高:通过几何特征区域的划分,使得采样结果抗噪性更强
2.4 随机下采样
原理简单,顾名思义,指定采样点个数,进行随机点去除进行采样操作。
2.4.1 特点
能控制采样点个数
随机性太强
3 PCL 下采样实践
3.1 体素网格下采样
在PCL库中,我们可以使用pcl::VoxelGridpcl::PointXYZ将空间点云体素化,得到对应的体素网格,再进行体素下采样。
关键代码:
*pcl::VoxelGridpcl::PointXYZ sor; // 体素化网格
sor.setInputCloud(cloud);
sor.setLeafSize(0.01f, 0.01f, 0.01f); // 设置体素栅格的大小为 1x1x1cm
sor.filter(cloud_filtered);
测试代码如下:
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/visualization/pcl_visualizer.h>
int main(int argc, char** argv)
{
//pcl::PCLPointCloud2::Ptr cloud(new pcl::PCLPointCloud2());
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
//pcl::PCLPointCloud2::Ptr cloud_filtered(new pcl::PCLPointCloud2());
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
// Fill in the cloud data
pcl::PCDReader reader;
// Replace the path below with the path where you saved your file
reader.read("table_scene_lms400.pcd", *cloud); // Remember to download the file first!
std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height
<< " data points (" << pcl::getFieldsList(*cloud) << ").";
// Create the filtering object
pcl::VoxelGrid<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setLeafSize(0.01f, 0.01f, 0.01f);
sor.filter(*cloud_filtered);
std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height
<< " data points (" << pcl::getFieldsList(*cloud_filtered) << ").";
//pcl::PCDWriter writer;
//writer.write("table_scene_lms400_downsampled.pcd", *cloud_filtered,
//Eigen::Vector4f::Zero(), Eigen::Quaternionf::Identity(), false);
pcl::visualization::PCLVisualizer viewer("demo");
int v1(0);
int v2(1);
viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1);
viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2);
// The color we will be using
float bckgr_gray_level = 0.0; // Black
float txt_gray_lvl = 1.0 - bckgr_gray_level;
// Original point cloud is white
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_in_color_h(cloud, (int)255 * txt_gray_lvl, (int)255 * txt_gray_lvl, (int)255 * txt_gray_lvl);
viewer.addPointCloud(cloud, cloud_in_color_h, "cloud_in_v1", v1); //viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v2", v2);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_out_green(cloud_filtered, 20, 180, 20);
viewer.addPointCloud(cloud_filtered, cloud_out_green, "cloud_out", v2);
viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v2);
viewer.setSize(1280, 1024); // Visualiser window size
//viewer.showCloud(cloud_out);
while (!viewer.wasStopped())
{
viewer.spinOnce();
}
return (0);
}
3.2 均匀下采样
对点云数据创建一个三维体素栅格,然后取每个体素中最接近体素中心的点,代替体素中所有点。
// Uniform sampling object
pcl::UniformSampling<pcl::PointXYZ> filter; // 创建均匀采样对象
filter.setInputCloud(cloud); // 设置待采样点云
filter.setRadiusSearch(0.01f); // 设置采样半径
filter.filter(*cloud_filtered); // 执行均匀采样,结果保存在cloud_filtered中