PCL_filter模块中各类滤波器目录
一、直通滤波器(PassThrough):用于阈值滤除
1、直通滤波器介绍
直通滤波器(PassThrough),顾名思义就是使用某个阈值直接过滤掉不符合阈值的滤波器。例如有值范围在1到100的十万个数字,直通滤波器设置阈值为<50,则经过直通滤波器过滤后,这十万个数字里所有>=50的数字将被遗弃掉,仅保留满足阈值的所有数字。
2、示例代码
首先,在编辑器中创建一个文件,比如pass .cpp,并将以下内容放入其中,代码分析见注释。
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
int main ()
{
// 原始点云指针
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
// 过滤后的点云指针
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
// 设置点云为无序点云,共5个点
cloud->width = 5;
cloud->height = 1;
cloud->points.resize (cloud->width * cloud->height);
for (auto& point: *cloud) //随机生成点坐标
{
point.x = 1024 * rand () / (RAND_MAX + 1.0f);
point.y = 1024 * rand () / (RAND_MAX + 1.0f);
point.z = 1024 * rand () / (RAND_MAX + 1.0f);
}
// 输出创建的点
std::cerr << "Cloud before filtering: " << std::endl;
for (const auto& point: *cloud)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
/*
* 以下代码创建PassThrough筛选器对象,并设置其参数。
* 过滤器字段名设置为z坐标,
* 接受的间隔值设置为(0.0;1.0)。
*/
// 创建过滤器对象
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud (cloud); // 填入数据
pass.setFilterFieldName ("z"); // 设置要过滤的维度名,这里为z向维度
pass.setFilterLimits (580.0, 900); // 设置滤波阈值
//pass.setFilterLimitsNegative (true);
pass.filter (*cloud_filtered); // 开始滤波
// 输出滤波后的点
std::cerr << "Cloud after filtering: " << std::endl;
for (const auto& point: *cloud_filtered)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
return (0);
}
过滤效果:绿色为阈值内的满足阈值(580,900)的点,红色为被过滤的点。当setFilterLimitsNegative参数设为True时,则选取( Z<580 || Z >900 )内的点云。
二、体素滤波器(VoxelGrid filter):用于下采样
1、体素滤波器介绍
体素滤波器(VoxelGrid filter),常被用于点云数量的下降,即点云下采样。可类比于图像金字塔下采样。顾名思义,将点云按照空间三维划分体素(空间网格),并使用数学方法将每个体素内的点云替换成一个点,从而减少点云数量。
官方的解释:
体素下降法:将要展示的VoxelGrid类在输入点云数据上创建一个3D体素网格(将体素网格想象为空间中的一组微型3D盒子)。然后,在每个体素(即3D框)中,所有出现的点将被近似(即下采样)到它们的质心。这种方法比用体素中心逼近它们要慢一些,但它能更准确地表示底层表面。
2、示例代码
首先,下载数据集table_scene_lms400.pcd并将其保存到磁盘某处。我这里由于暂时连不上github,使用他人的效果图替代一下。
将如下代码粘贴到编辑器中,并进行编译。
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
int
main ()
{
pcl::PCLPointCloud2::Ptr cloud (new pcl::PCLPointCloud2 ());
pcl::PCLPointCloud2::Ptr cloud_filtered (new pcl::PCLPointCloud2 ());
// 创建点云读取器对象
pcl::PCDReader reader;
// 将下载的pcd文件路径替代以下路径
reader.read ("table_scene_lms400.pcd", *cloud);
std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height
<< " data points (" << pcl::getFieldsList (*cloud) << ")." << std::endl;
// 创建体素过滤器对象
pcl::VoxelGrid<pcl::PCLPointCloud2> sor;
sor.setInputCloud (cloud);
//LeafSize是体素栅格叶大小参数,每个元素分别表示体素在XYZ方向上的尺寸。单位是m,此处0.01代表一厘米。
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) << ")." << std::endl;
//创建点云写入器将滤波后的点云写入pcd文件中
pcl::PCDWriter writer;
writer.write ("table_scene_lms400_downsampled.pcd", *cloud_filtered,
Eigen::Vector4f::Zero (), Eigen::Quaternionf::Identity (), false);
return (0);
}
滤波效果如下:
体素滤波前:
体素滤波后效果:
三、统计离群滤波器(StatisticalOutlierRemoval filter):用于离群点滤除
1、统计离群滤波器介绍
统计离群滤波器(StatisticalOutlierRemoval filter)常被用于删除离群点(噪声和杂点),该滤波器使用统计分析技术从点云数据集中去除噪声测量,例如离群值。
1.1背景:
激光扫描通常生成不同点密度的点云数据集。此外,测量误差还会导致稀疏的异常值,从而进一步破坏结果。这使得局部点云特征(如表面法线或曲率变化)的估计变得复杂,从而导致错误的值,进而可能导致点云配准失败。其中一些不规则性可以通过对每个点的邻域进行统计分析来解决,并对那些不符合一定标准的点进行修剪。离群点去除方法是基于计算点到邻点的距离在输入数据集中的分布。对于每一个点,计算它到所有相邻点的平均距离。通过假设得到的分布是具有均值和标准差的高斯分布,所有的点,其均值距离在由全局距离均值和标准差定义的区间之外,都可以被认为是离群点,并从数据集中进行修剪。
下面的图片展示了离群点分析和去除的效果:左边是原始数据集,右边是生成的数据集。该图显示了滤波前后一个点附近的平均k近邻距离。
2、示例代码
依旧是先从github上下载如下数据集table_scene_lms400_inliers.pcd。然后将如下代码粘贴到编译器中。
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>
int
main ()
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
// 点云数据
pcl::PCDReader reader;
// 将下载的pcd文件路径替换以下路径
reader.read<pcl::PointXYZ> ("table_scene_lms400.pcd", *cloud);
std::cerr << "Cloud before filtering: " << std::endl;
std::cerr << *cloud << std::endl;
// 创建滤波器对象sor
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
/* 参数设置:
* 每个点要分析的邻居数设置为50,标准差乘数为1。
* 这意味着,所有到查询点的距离大于1个标准差的点都将被标记为离群值并被删除。
* 输出被计算并存储在cloud_filtered中。
*/
sor.setInputCloud (cloud);// 填入点云
sor.setMeanK (50); // 设置均值参数K
sor.setStddevMulThresh (1.0);// 设置
sor.filter (*cloud_filtered);
std::cerr << "Cloud after filtering: " << std::endl;
std::cerr << *cloud_filtered << std::endl;
// 将滤波后的点云输出成pcd格式文件
pcl::PCDWriter writer;
writer.write<pcl::PointXYZ> ("table_scene_lms400_inliers.pcd", *cloud_filtered, false);
sor.setNegative (true); // 设置成true,条件就会变成所有到查询点的距离小于1个标准差的点都将被标记为离群值并被删除,留下之前被过滤的那些点。
sor.filter (*cloud_filtered);
writer.write<pcl::PointXYZ> ("table_scene_lms400_outliers.pcd", *cloud_filtered, false);
return (0);
}
四、条件和半径滤波器(Conditional or RadiusOutlier ):用于离群点滤除
1、条件和半径滤波器介绍
[1]条件滤波器,顾名思义,就是给滤波器一些条件值,可以是一个也可以是多个,使用条件滤波器可以滤除所有不满足条件的点,常用于离群点滤除。
在示例代码中,我们使用在条件中添加两个比较:大于(GT) 0.0和小于(LT) 0.8。然后使用这个条件构建滤波器。
[2]半径滤波器,顾名思义,给定指定半径,和半径内最少点数进行滤波,例如满足半径0.8内,点数至少为2,即计算每个点半径范围内的点数,少于该点数则被过滤,常用于离群点滤波。
我们来直接看代码:
下图可以帮助我们理解RadiusOutlierRemoval过滤器对象的作用。用户指定了一些邻域点,每个索引必须在指定的半径参数内。例如,如果指定了1个邻点,那么只有黄色的点将从PointCloud中删除。如果指定了2个邻点,那么黄色和绿色的点都将从PointCloud中删除。
2、示例代码
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/conditional_removal.h>
int
main (int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
exit(0);
}
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
// 随机生成一些点云数据
cloud->width = 5;
cloud->height = 1;
cloud->resize (cloud->width * cloud->height);
for (auto& point: *cloud)
{
point.x = 1024 * rand () / (RAND_MAX + 1.0f);
point.y = 1024 * rand () / (RAND_MAX + 1.0f);
point.z = 1024 * rand () / (RAND_MAX + 1.0f);
}
// 若用户输入-r则使用半径滤波器滤除点云,设置滤除条件:搜索半径0.8,最少点数2,设置滤除后依旧保持点云的有组织性(保持顺序)
if (strcmp(argv[1], "-r") == 0){
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
// 设置滤波参数
outrem.setInputCloud(cloud);
outrem.setRadiusSearch(0.8);
outrem.setMinNeighborsInRadius (2);
outrem.setKeepOrganized(true);
// 开始滤波器
outrem.filter (*cloud_filtered);
}
// 若用户输入指令-c,则启用条件滤波器。
else if (strcmp(argv[1], "-c") == 0){
// 创建条件滤波器的“条件”,设置条件为两个“比较”滤波器,“GT”是大于的意思,本例中表示z>0的点;“LT”是小于的意思,本例中表示z<0.8的点。因此该滤波器保留(0<z<0.8)的点。
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond (new
pcl::ConditionAnd<pcl::PointXYZ> ());
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new
pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::GT, 0.0)));
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new
pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::LT, 0.8)));
// 创建条件滤波器,设置滤除后依旧保持点云的有组织性(保持顺序)
pcl::ConditionalRemoval<pcl::PointXYZ> condrem;
condrem.setCondition (range_cond);
condrem.setInputCloud (cloud);
condrem.setKeepOrganized(true);
// 开始滤波
condrem.filter (*cloud_filtered);
}
else{
std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
exit(0);
}
std::cerr << "Cloud before filtering: " << std::endl;
for (const auto& point: *cloud)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
// display pointcloud after filtering
std::cerr << "Cloud after filtering: " << std::endl;
for (const auto& point: *cloud_filtered)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
return (0);
}
总结:
本文章介绍了直通滤波器,体素滤波器,统计离群滤波器,半径滤波器和条件滤波器的概念和示例代码。但是PCL中还有很多滤波器没有介绍,例如任意多边形内部点云滤除CropHull(多用作编写点云后处理软件使用,用于用户界面交互)。这里的介绍都比较简单,详细的介绍和实验结果可以参考前辈的文章。后期所有内容学习完成后,准备自己写一个点云后处理软件,使用QT+PCL实现各种点云后处理功能,到时候会把编写过程写出一篇博文,敬请期待。
【博主简介】
斯坦福的兔子,男,天津大学机械工程工学硕士。毕业至今从事光学三维成像及点云处理相关工作。因工作中使用的三维处理库为公司内部库,不具有普遍适用性,遂自学开源PCL库及其相关数学知识以备使用。谨此将自学过程与君共享。
博主才疏学浅,尚不具有指导能力,如有问题还请各位在评论处留言供大家共同讨论。
若前辈们有工作机会介绍欢迎私信。