【PCL自学:Filtering】PCL中的各类滤波器介绍与使用 (持续更新)

11 篇文章 17 订阅

一、直通滤波器(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库及其相关数学知识以备使用。谨此将自学过程与君共享。
博主才疏学浅,尚不具有指导能力,如有问题还请各位在评论处留言供大家共同讨论。
若前辈们有工作机会介绍欢迎私信。

pcl::PassThrough滤波器是一种基于范围的滤波器,它可以通过设置一个范围来过滤掉点云数据中超出这个范围的点。它主要用于去除点云数据中的离群点或者将点云数据截取在某一范围内。 使用pcl::PassThrough滤波器的步骤如下: 1. 从点云库中引入pcl::PassThrough类。 ``` #include <pcl/filters/passthrough.h> ``` 2. 创建一个pcl::PassThrough对象。 ``` pcl::PassThrough<pcl::PointXYZ> pass; ``` 3. 设置输入点云数据。 ``` pass.setInputCloud(cloud); ``` 4. 设置要过滤的维度和范围。 ``` pass.setFilterFieldName("z"); // 过滤z维度 pass.setFilterLimits(0.0, 1.0); // 过滤范围为[0.0,1.0] ``` 5. 执行过滤操作。 ``` pass.filter(*cloud_filtered); ``` 完整示例代码如下: ```cpp #include <pcl/filters/passthrough.h> int main(int argc, char** argv) { // 加载点云数据 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile<pcl::PointXYZ>("input.pcd", *cloud); // 创建过滤器对象 pcl::PassThrough<pcl::PointXYZ> pass; pass.setInputCloud(cloud); // 设置过滤维度和范围 pass.setFilterFieldName("z"); pass.setFilterLimits(0.0, 1.0); // 执行过滤操作 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>); pass.filter(*cloud_filtered); // 保存过滤后的点云数据 pcl::io::savePCDFile<pcl::PointXYZ>("output.pcd", *cloud_filtered); return 0; } ``` 上述代码将会从名为“input.pcd”的文件中加载点云数据,然后过滤掉z维度不在[0.0,1.0]范围内的点,最后将过滤后的点云数据保存到名为“output.pcd”的文件中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值