PCL的点云滤波

文章目录

    • 使用直通滤波器对点云进行滤波处理
    • 使用VoxelGrid滤波器对点云进行下采样
    • 使用StatisticalOutlierRemoval滤波器移除离群点
    • 使用参数化模型投影点云
    • 从一个点云集中提取一个子集
    • 使用ConditionalRemoval或RadiusOutlierRemoval移除离群点
    • CropHull任意多边形内部点云提取

    点云数据中会存在噪声点,离群点这些我们在后续操作中不需要的点,所以需要滤波删除这些点。这个过程也可以称为点云预处理。同时预处理还包括孔洞修复,数据压缩等,这里我们暂时只讨论噪声点和离群点,其他后续如有需要再学习。通过预处理后,才能更好的进行配准、特征提取、曲面重建、可视化等后续应用处理。pcl/filters模块提供了一些滤波处理算法,接下来对其中的一部分进行介绍。

使用直通滤波器对点云进行滤波处理

    直通滤波器通常是对指定的某一维度进行滤波,也就是去掉用户指定范围内部或者外部的点 ,例如高程筛选。下面是一个高程筛选的例子,将点云Z方向的范围设置为(0.0,1.0),并保留内部点,删除外部点。
官方tutorials: http://pointclouds.org/documentation/tutorials/passthrough.php#passthrough

#include 
#include 
#include             //点云格式
#include     //PCL滤波算法中的passthrough

int            //PCL函数定义编码规范,返回值类型单独放一行,函数名及参数列表放一行。
main (int argc, char** argv)
{ 
    srand(time(0));
    //pcl是命名空间,PointCloud是类模板,是模板实例化的类型,Ptr是智能指针
    //cloud是指针变量 指向PointCloud类对象的指针
    //简单来说,就是创建了一份名cloud的存储XYZ类型点云数据的指针。
    pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
    pcl::PointCloud::Ptr cloud_filtered (new pcl::PointCloud);
  
    //填入点云数据
    cloud->width  = 5;                   //设置点云宽度/数量
    cloud->height = 1;                   //设置点云高度/无序点云
    cloud->points.resize (cloud->width * cloud->height);      //点云总数

    //size_t 其实就是unsinged int
    for (size_t i = 0; i < cloud->points.size (); ++i)      //为点云填充数据,这里为随机数
    {
        cloud->points[i].x = rand () / (RAND_MAX + 1.0f)-0.5;
        cloud->points[i].y = rand () / (RAND_MAX + 1.0f)-0.5;
        cloud->points[i].z = rand () / (RAND_MAX + 1.0f)-0.5;
    }

    //std::err 标准出错输出对象,默认信息输出到屏幕。
    //打印所有点到标准错误输出
    std::cerr << "Cloud before filtering: " << std::endl;
    for (size_t i = 0; i < cloud->points.size (); ++i)
    {
        std::cerr << "    " << cloud->points[i].x << " " 
                        << cloud->points[i].y << " " 
                        << cloud->points[i].z << std::endl;
     }


/*************************************************************************
 创建直通滤波器的对象,设立参数,滤波字段名被设置为Z轴方向,可接受的范围为(0.0,1.0)
 即将点云中所有点的Z轴坐标不在该范围内的点过滤掉或保留,这里是过滤掉,由函数
 setFilterLimitsNegative设定。即是按高程信息筛选。
*************************************************************************/
      // 创建滤波器对象
      pcl::PassThrough pass;    //设置滤波器对象
      pass.setInputCloud (cloud);              //设置输入点云
      pass.setFilterFieldName ("z");           //设置过滤时所需要点云类型的z字段
      pass.setFilterLimits (0.0, 1.0);         //设置在过滤的z字段上的范围
      //pass.setFilterLimitsNegative (true);   //设置保留范围内的还是范围外
      pass.filter (*cloud_filtered);           //执行滤波,把滤波结果保存在cloud_filtered

      std::cerr << "Cloud after filtering: " << std::endl;
      for (size_t i = 0; i < cloud_filtered->points.size (); ++i)
     {  
         std::cerr << "    " << cloud_filtered->points[i].x << " " 
                        << cloud_filtered->points[i].y << " " 
                        << cloud_filtered->points[i].z << std::endl;
      }
  return (0);
}

结果为:
PCL的点云滤波_第1张图片
    可以看到在滤波之前共有5个点云,其中z方向为负的有3个,经过滤波之后所有z方向为负的均被删除,只留下两个点云。

PS:由于本人在接触PCL以及点云知识之前对计算机知识完全属于零基础,连C++都是从头开始学,对于C++代码在ubuntu下的编译和运行并没有充分理解,导致在编译以上代码时走了很多弯路。下面给出我自己用的方法,不知是否正确,还希望读到这的朋友能够不吝指教,感谢!

首先在代码文件和CMakeLists.txt的文件夹中打开终端

mkdir src
mkdir build
cd build
cmake ..
make

在build文件中会生成个xxxx.out文件

./xxxx.out

便可运行该文件,得到上述图中结果。

使用VoxelGrid滤波器对点云进行下采样

    VoxelGrid滤波也叫体素化网格法,是PCL中VoxelGrid类通过输入的点云数据创建一个三维体素栅格(简单点来说就是特别小的空间三维立方体的集合),然后在每个体素(三维立方体)内,用体素中所有点云点的重心(不是中心)近似的表示该体素内的所有点,对于所有体素处理后得到过滤后的点云。这种方法比用体素中心来逼近的方法更慢,但是对于采样点对应曲面的表示更为准确。
官方tutorials: http://pointclouds.org/documentation/tutorials/voxel_grid.php#voxelgrid

#include 
#include 
#include 
#include 
int
main (int argc, char** argv)
{
      pcl::PCLPointCloud2::Ptr cloud (new pcl::PCLPointCloud2 ());
      pcl::PCLPointCloud2::Ptr cloud_filtered (new pcl::PCLPointCloud2 ());
      // 读取点云数据
      pcl::PCDReader reader;
      // 把路径改为自己存放文件的路径
      reader.read ("table_scene_lms400.pcd", *cloud);    //读取点云到cloud中
      std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height 
                << " data points (" << pcl::getFieldsList (*cloud) << ").";
    
      // pcl::getFieldsList (*cloud)得到cloud的field信息(x y z intensity distance sid)
  
      // 创建滤波器对象
      pcl::VoxelGrid sor;      // 创建滤波对象
      sor.setInputCloud (cloud);                    //给滤波对象设置需要过滤的点云
      sor.setLeafSize (0.01f, 0.01f, 0.01f);        //设置滤波时创建的体素大小1cm立方体
      sor.filter (*cloud_filtered);                 //执行滤波处理,存储输出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);
    return (0);
}

过滤前后对比:过滤前PCL的点云滤波_第2张图片
过滤后:
PCL的点云滤波_第3张图片
可以明显看到点云数量减少,变得稀疏了很多。实际情况也是如此,下面是过滤前的点云数量以及过滤后的点云数量。可以看到滤波前是460400个点云,滤波后变成了41049个,效果很明显。

PCL的点云滤波_第4张图片

使用StatisticalOutlierRemoval滤波器移除离群点

    激光扫描通常会产生密度不均匀的点云数据集,另外,测量中的误差会产生稀疏的离群点,使效果更糟。可以对每个点的邻域进行一个统计分析,并修减掉那些不符合一定“标准”的点。StatisticalOutlierRemoval滤波器基于对输入数据中对点到临近点的距离分布的计算。对每个点,计算它到它的所有临近点的平均距离。假设得到的结果是一个高斯分布,其形状由均值和标准差决定,平均距离在标准范围(由全局距离平均值和方差定义)之外的点,可被定义为离群点并可从数据集中去除掉。
官方tutorials: http://pointclouds.org/documentation/tutorials/statistical_outlier.php#statistical-outlier-removal

#include 
#include 
#include 
#include 

int
main (int argc, char** argv)
{
      pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
      pcl::PointCloud::Ptr cloud_filtered (new pcl::PointCloud);
    
      // 填入点云数据
      pcl::PCDReader reader;        //定义读取对象。
      
      // 把路径改为自己存放文件的路径
      reader.read ("table_scene_lms400.pcd", *cloud);    //读取点云文件
      std::cerr << "Cloud before filtering: " << std::endl;
      std::cerr << *cloud << std::endl;
 
      // 创建pcl::StatisticalOutlierRemoval滤波器对象
      pcl::StatisticalOutlierRemoval sor;
      sor.setInputCloud (cloud);
      sor.setStddevMulThresh (1.0);
      //将标准差倍数设为1,意味着吐过一点的距离超出平均距离一个标准差以上,则该点被标记为离群点,将被移除。
     
      sor.filter (*cloud_filtered);        //计算结果储存在cloud_filter中
      std::cerr << "Cloud after filtering: " << std::endl;
      std::cerr << *cloud_filtered << std::endl;
    
      //滤波后的数据(内部点)写入磁盘中,以便下次使用,例如可视化
      pcl::PCDWriter writer;
      writer.write ("table_scene_lms400_inliers.pcd", *cloud_filtered, false);
     
      //滤波后的数据(外部点)
      sor.setNegative (true);    //设置为输出取外部点,以获得离群点数据。(就是滤掉的点)
      sor.filter (*cloud_filtered);
      writer.write ("table_scene_lms400_outliers.pcd", *cloud_filtered, false);
      return (0);
}

过滤之前:
PCL的点云滤波_第5张图片
过滤后的内部点:
PCL的点云滤波_第6张图片
过滤后的外部点:
PCL的点云滤波_第7张图片

使用参数化模型投影点云

    将点投影到一个参数化模型上(例如平面或球等),这个参数化模型是通过一组参数来设定的。在本例中采用的是平面,使用其等式形式ax+by+cz+d=0。这里具体就是X-Y面,令a=b=d=0,c=1。也可以使用其他不同的参数化模型,在PCL中有特意储存常见模型系数的数据结构。
官方tutorials: http://pointclouds.org/documentation/tutorials/project_inliers.php#project-inliers

#include 
#include 
#include 
#include            //模型系数定义头文件
#include      //投影滤波类头文件

int
main (int argc, char** argv)
{
      //创建点云结构
      pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
      pcl::PointCloud::Ptr cloud_projected (new pcl::PointCloud);
     
      // 填入点云数据
      cloud->width  = 5;
      cloud->height = 1;
      cloud->points.resize (cloud->width * cloud->height);    //resize重新指定容器大小,减少内存损耗
      for (size_t i = 0; i < cloud->points.size (); ++i)
      {
         cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
         cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
         cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
       }
 
      //将填入的点云数据打印出来
      std::cerr << "Cloud before projection: " << std::endl;
      for (size_t i = 0; i < cloud->points.size (); ++i)
      std::cerr << "    " << cloud->points[i].x << " " 
                        << cloud->points[i].y << " " 
                        << cloud->points[i].z << std::endl;
  
      //填充ModelCoefficients的值,本例中使用ax+by+cz+d=0的平面模型,即X=Y=0,Z=1的X-Y平面
      pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());//定义模型系数对象coefficients
      coefficients->values.resize (4);    //设置values大小为4,a,b,c,d
      coefficients->values[0] = coefficients->values[1] = 0;     //设置a,b=0
      coefficients->values[2] = 1.0;      //设置c=1
      coefficients->values[3] = 0;        //设置d=0

      // 创建滤波器对象
      pcl::ProjectInliers proj;
      proj.setModelType (pcl::SACMODEL_PLANE);    //设置对象对应的投影模型,pcl::SACMODEL_PLANE指三维平面
      proj.setInputCloud (cloud);
      proj.setModelCoefficients (coefficients);    //设置模型对应的系数,把上面创建的参数模型对象作为参数传入
      proj.filter (*cloud_projected);
    
      //打印出投影后的点云
      std::cerr << "Cloud after projection: " << std::endl;
      for (size_t i = 0; i < cloud_projected->points.size (); ++i)
      std::cerr << "    " << cloud_projected->points[i].x << " " 
                        << cloud_projected->points[i].y << " " 
                        << cloud_projected->points[i].z << std::endl;

      return (0);
}

结果如图:
PCL的点云滤波_第8张图片
    可以看到参数化模型投影之前五个点云数据的Z值都有自己的值,因为投影到X-Y面上,Z方向上的值就都变为了0 。

从一个点云集中提取一个子集

    这里使用ExtractIndices滤波器基于某以分割算法提取点云中的一个子集。这里先给出提取子集的代码,其中包含的分割算法会在之后给出并解释。
官方ExtractIndices滤波器tutorials: http://pointclouds.org/documentation/tutorials/extract_indices.php#extract-indices
官方分割算法tutorials: http://pointclouds.org/documentation/tutorials/planar_segmentation.php#planar-segmentation

#include 
#include               //模型系数定义头文件
#include 
#include 
#include   //随机样本一致性算法 方法类型
#include    //随机样本一致性算法 模型类型
#include   //随机样本一致性算法 分割方法
#include              //voxelgrid降采样滤波器
#include         //extract_indices滤波器

//pcl::PCLPointCloud2::Ptr和pcl::PointCloud两中数据结构的区别:
//https://www.cnblogs.com/li-yao7758258/p/6659451.html
int
main (int argc, char** argv)
{
      /**********************************************************************************************************
       从输入的.PCD 文件载入数据后,创建一个VOxelGrid滤波器对数据进行下采样,在这里进行下才样是为了加速处理过程,
       越少的点意味着“分割循环中处理起来越快”
       **********************************************************************************************************/

      //申明滤波前后的点云
      pcl::PCLPointCloud2::Ptr cloud_blob (new pcl::PCLPointCloud2), cloud_filtered_blob (new pcl::PCLPointCloud2);
  
      pcl::PointCloud::Ptr cloud_filtered (new pcl::PointCloud), cloud_p (new      pcl::PointCloud), cloud_f (new pcl::PointCloud);

      // 读取PCD文件
      pcl::PCDReader reader;
      reader.read ("table_scene_lms400.pcd", *cloud_blob);
      //统计滤波前的点云个数
      std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl;

      // 创建体素栅格下采样: 下采样的大小为1cm
      pcl::VoxelGrid sor;    //创建voxelgrid对象
      sor.setInputCloud (cloud_blob);             //输入原始点云
      sor.setLeafSize (0.01f, 0.01f, 0.01f);      //设置采样体素大小1cm
      sor.filter (*cloud_filtered_blob);          //把降采样后的点云输出到cloud_filtered_blob

      // 转换为模板点云
      pcl::fromPCLPointCloud2 (*cloud_filtered_blob, *cloud_filtered);
      //输出滤波后的点云个数
      std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl;

      //保存降采样后的点云
      pcl::PCDWriter writer;
      writer.write ("table_scene_lms400_downsampled.pcd", *cloud_filtered, false);

      pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());   //存储输出的模型的系数
      pcl::PointIndices::Ptr inliers (new pcl::PointIndices ());                  //存储内点,使用的点
 
     //创建分割对象
      pcl::SACSegmentation seg;
      //可选设置
      seg.setOptimizeCoefficients (true);//设置对估计模型参数进行优化处理
      //必须设置
      seg.setModelType (pcl::SACMODEL_PLANE);    //设置分割模型类别,pcl::SACMODEL_PLANE代表三维平面,检测平面
      seg.setMethodType (pcl::SAC_RANSAC);       //设置用哪个随机参数估计方法【聚类或随机样本一致性】
      seg.setMaxIterations (1000);               //设置最大迭代次数
      seg.setDistanceThreshold (0.01);           //判断是否为模型内点的距离阀值

      //设置ExtractIndices的实际参数
      pcl::ExtractIndices extract;                  //创建点云提取对象

      int i = 0, nr_points = (int) cloud_filtered->points.size (); //nr_points表示点云的总数量
 
      // 留下30%的原始点云
      while (cloud_filtered->points.size () > 0.3 * nr_points)
      {
          // 为了处理点云包含的多个模型,在一个循环中执行该过程并在每次模型被提取后,保存剩余的点进行迭代
          seg.setInputCloud (cloud_filtered);
          seg.segment (*inliers, *coefficients);   //模型内点通过分割过程获取
    
          if (inliers->indices.size () == 0)
          {
             std::cerr << "Could not estimate a planar model for the given dataset." << std::endl;
             break;
          }

          // 提取内点
          extract.setInputCloud (cloud_filtered);     //设置输入点云
          extract.setIndices (inliers);               //设置分割后的内点为需要提取的点集
          extract.setNegative (false);                //设置提取内点而非外点
          extract.filter (*cloud_p);                  //提取输出存储到cloud_p
          std::cerr << "PointCloud representing the planar component: " << cloud_p->width * cloud_p->height << " data points." << std::endl;

          std::stringstream ss;
          ss << "table_scene_lms400_plane_" << i << ".pcd";
          writer.write (ss.str (), *cloud_p, false);

          //提取外点
          extract.setNegative (true);     //设置提取外点而非内点
          extract.filter (*cloud_f);      //提取输出存储到cloud_f
          cloud_filtered.swap (cloud_f);  //把外点cloud_f赋给cloud_filtered,再迭代循环处理,直到满足条件cloud_filtered->points.size () > 0.3 * nr_points)
          i++;
  }

  return (0);
}

结果为:
PCL的点云滤波_第9张图片
第一步降采样的可视化:
PCL的点云滤波_第10张图片
第二步第一个平面子集可视化:
PCL的点云滤波_第11张图片
第二步第二个平面子集可视化:
PCL的点云滤波_第12张图片

使用ConditionalRemoval或RadiusOutlierRemoval移除离群点

    这里讨论在滤波模块使用几种不同的方法移除离群点。对于ConditionalRemoval滤波器,可以一次删除满足对输入的点云设定的一个或多个条件指标的所有的数据点。RadiusOutlinerRemoval滤波器,它可以删除在输入点云一定范围内没有至少达到足够多近邻的所有数据点。
    对于RadiusOutlinerRemoval滤波器,用户指定每个点的一定范围内周围至少要有足够多近邻点。比如下图中,如果指定至少要有1个邻点,只有黄色的点会被删除。如果指定至少要有2个邻点,则黄色和绿色的点都将被删除。
PCL的点云滤波_第13张图片
官方tutorials: http://pointclouds.org/documentation/tutorials/remove_outliers.php#remove-outliers

#include 
#include 
#include 
#include 

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::Ptr cloud (new pcl::PointCloud);
      pcl::PointCloud::Ptr cloud_filtered (new pcl::PointCloud);
 
      //填入点云数据
      cloud->width  = 10;
      cloud->height = 1;
      cloud->points.resize (cloud->width * cloud->height);
      for (size_t i = 0; i < cloud->points.size (); ++i)
      {
         cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
         cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
         cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
       }
  
      //选择使用哪个滤波器,先是RadiusOutlierRemoval滤波器
      if (strcmp(argv[1], "-r") == 0){
          pcl::RadiusOutlierRemoval outrem;
          //创建滤波器
          outrem.setInputCloud(cloud);
          outrem.setRadiusSearch(0.8);         //设置在0.8半径的范围内找临近点
          outrem.setMinNeighborsInRadius (2);  //设置查询点的临近点集数小于2的删除
          //应用滤波器
          outrem.filter (*cloud_filtered);
       }
  
      //选择使用conditionalremoval滤波器,这里我们使用Z字段上大于0.0和小于0.8两个条件建立条件滤波器
      //只有满足条件的数据才能保留在点云结构中。
      else if (strcmp(argv[1], "-c") == 0){  
          //创建条件定义对象
          pcl::ConditionAnd::Ptr range_cond (new
          pcl::ConditionAnd ()); 
          //为条件定义对象添加在z字段上大于0.0的比较算子 
          range_cond->addComparison (pcl::FieldComparison::ConstPtr (new
              pcl::FieldComparison ("z", pcl::ComparisonOps::GT, 0.0)));
          //为条件定义对象添加在z字段上小于0.8的比较算子
          range_cond->addComparison (pcl::FieldComparison::ConstPtr (new
              pcl::FieldComparison ("z", pcl::ComparisonOps::LT, 0.8)));
    
          //创建滤波器并用条件定义对象初始化
          pcl::ConditionalRemoval condrem (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 (size_t i = 0; i < cloud->points.size (); ++i)
          std::cerr << "    " << cloud->points[i].x << " "
                              << cloud->points[i].y << " "
                              << cloud->points[i].z << std::endl;
       //显示滤波后的点云
       std::cerr << "Cloud after filtering: " << std::endl;
       for (size_t i = 0; i < cloud_filtered->points.size (); ++i)
       std::cerr << "    " << cloud_filtered->points[i].x << " "
                           << cloud_filtered->points[i].y << " "
                           << cloud_filtered->points[i].z << std::endl;
       return (0);
}

    上述代码实现了在一个模块中如何选择使用两种不同的滤波器对点云数据进行滤波。在运行时如果想使用RadiusOutlierRemoval滤波器:

./remove_outliers -r

想使用ConditionalRemoval滤波器:

./remove_outliers -c

结果:
PCL的点云滤波_第14张图片
从中可以看出ConditionalRemoval 或RadiusOutlinerRemoval的区别
RadiusOutlinerRemoval比较适合去除单个的离群点 ConditionalRemoval 比较灵活,可以根据用户设置的条件灵活过滤

CropHull任意多边形内部点云提取

    这里利用CropHull滤波器得到2D封闭多边形内部或者外部的点云。首先设置几个2D平面点云构成封闭多边形,再利用ConvexHull类构造成凸包,再将需要滤波的点云输入,得到在这个2D封闭凸包范围内的点云。
官方tutorials: 没有

#include    //可视化头文件
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char** argv)
{
	  pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
	  pcl::PCDReader reader;
	  reader.read("pig.pcd",*cloud);  //输入滤波对象点云
        //输入2D平面点云(Z方向都是0,即在X-Y这个2D平面上),五个点构成封闭多边形
      pcl::PointCloud::Ptr boundingbox_ptr (new pcl::PointCloud);
	  boundingbox_ptr->push_back(pcl::PointXYZ(0.1, 0.1, 0));
	  boundingbox_ptr->push_back(pcl::PointXYZ(0.1, -0.1,0 ));
	  boundingbox_ptr->push_back(pcl::PointXYZ(-0.1, 0.1,0 ));
	  boundingbox_ptr->push_back(pcl::PointXYZ(-0.1, -0.1,0 ));
	  boundingbox_ptr->push_back(pcl::PointXYZ(0.15, 0.1,0 ));
       
      //对上述输入的2D平面点构造2D凸包,凸包的构造需要使用concave_hull类
	  pcl::ConvexHull hull;  //创建凸包对象
	  hull.setInputCloud(boundingbox_ptr);  //输入2D平面点云
	  hull.setDimension(2);                 //设置凸包维度
	  std::vector polygons;  //设置pcl::Vertices类型的向量,用于保存凸包
   
      //该点云用于描述凸包形状surface_hull
	  pcl::PointCloud::Ptr surface_hull (new pcl::PointCloud);
      //顶点polygons
	  hull.reconstruct(*surface_hull, polygons);//计算2D凸包结果
  
        //创建CropHull滤波器
	  pcl::PointCloud::Ptr objects (new pcl::PointCloud);
	  pcl::CropHull bb_filter;     //创建CropHull滤波器
	  bb_filter.setDim(2);                        //设置维度(维度要与输入的凸包维度一致)
	  bb_filter.setInputCloud(cloud);             //设置需要过滤的点云
	  bb_filter.setHullIndices(polygons);         //输入封闭多边形的顶点
	  bb_filter.setHullCloud(surface_hull);       //输入封闭多边形的形状
   	  bb_filter.filter(*objects);                 //执行CropHull滤波,存储结果到objects
	  std::cout <<“滤波后的点云数量:” <size() << std::endl;  //输出滤波之后的点云数量

	  //可视化
	  boost::shared_ptr for_visualizer_v (new pcl::visualization::PCLVisualizer ("crophull display"));
	  for_visualizer_v->setBackgroundColor(255,255,255);
      //分别R/G/B,三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色。 255表示设置的值,值可为0-255

	  int v1(0);
	  for_visualizer_v->createViewPort (0.0, 0.0, 0.33, 1, v1);
	  for_visualizer_v->setBackgroundColor (0, 255, 255, v1);
	  for_visualizer_v->addPointCloud (cloud,"cloud",v1);
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"cloud");
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,3,"cloud");
	  for_visualizer_v->addPolygon(surface_hull,0,.069*255,0.2*255,"backview_hull_polyline1",v1);

	  int v2(0);
	  for_visualizer_v->createViewPort (0.33, 0.0, 0.66, 1, v2);	
	  for_visualizer_v->setBackgroundColor (255, 255, 255, v2);
	  for_visualizer_v->addPointCloud (surface_hull,"surface_hull",v2);
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"surface_hull");
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,8,"surface_hull");
	  for_visualizer_v->addPolygon(surface_hull,0,.069*255,0.2*255,"backview_hull_polyline",v2);

	  int v3(0);
	  for_visualizer_v->createViewPort (0.66, 0.0, 1, 1, v3);
	  for_visualizer_v->setBackgroundColor (255, 0, 255, v3);
	  for_visualizer_v->addPointCloud (objects,"objects",v3);
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"objects");
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,3,"objects");

	  while (!for_visualizer_v->wasStopped())
	  {

		  for_visualizer_v->spinOnce(1000);
	  }
	  system("pause");
}

终端结果为:
PCL的点云滤波_第15张图片
可视化结果为:
PCL的点云滤波_第16张图片

    运行之后,三个视口显示的分别是原始输入点云、封闭2D多边形凸包结果和CropHull滤波结果。我们通过修改2D平面点云输入部分代码自定义2D多边形的形状,以输出不同的滤波结果。

PS:由于直接看的点云滤波这部分,对于可视化部分代码并不了解,这里对于可视化多视口代码进行解释。准备以后也像这样,在学习处理点云的过程中遇到不懂不会的再查找之前的知识,即用到再学。

    以第一个视口为例,首先创建可视化窗口for_visualizer_v,命名为"crophull display",并设置背景颜色。分别R/G/B,三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色。 255表示设置的值,值可为0-255。

      boost::shared_ptr for_visualizer_v (new pcl::visualization::PCLVisualizer ("crophull display"));
	  for_visualizer_v->setBackgroundColor(255,255,255);

    创建新的视口,所需的四个参数是视口在X轴的最小值、最大值。Y轴的最小值、最大值,取值在0~1之间。最后一个字符串参数用来唯一标识该视口,在其他改变该视口内容的调用中,需要以该唯一标识为参数。还设置了该视口的背景颜色,并添加点云。

	  int v1(0);
	  for_visualizer_v->createViewPort (0.0, 0.0, 0.33, 1, v1);
	  for_visualizer_v->setBackgroundColor (0, 255, 255, v1);
	  for_visualizer_v->addPointCloud (cloud,"cloud",v1);

    如果想添加标签以区分其他视口,还可以添加以下代码:

for_visualizer_v -> addText("The first viewer",10,10,"v1 text",v1);

    还可以利用setPointCloudRenderingProperties设置点云属性,其中PCL_VISUALIZER_POINT_SIZE表示设置cloud中点云的大小为3,PCL_VISUALIZER_COLOR表示设置点云颜色。

	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"cloud");
	  for_visualizer_v->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,3,"cloud");

    具体可视化的其他内容可参考:https://blog.csdn.net/qq_43407445/article/details/91369685

你可能感兴趣的:(PCL的点云滤波)