【PCL】教程 stick_segmentation点云的滤波、降采样、颜色阈值过滤、欧式聚类提取以及线段分割等过程...

6e57a5b9bd641bf8259638ddca3c55d1.png

[done, 653.59 ms : 2073600 points]
Available dimensions: x y z rgb

源点云1189_kinect_v2_scene_1.pcd

1cef583b207fc2bfc05d9bd1606ebf15.png

79.2252 ms
Found 911 inliers.

结果

代码包含了许多 PCL (Point Cloud Library) 相关的文件和类的使用,涉及到点云的读取、处理和可视化。代码的核心部分定义了如何过滤特殊颜色的点云数据,以及如何提取最大的欧几里得距离聚类。

下面是代码的主要组件和流程:

  1. 定义了 ConditionThresholdHSV 类,用于颜色阈值处理。它基于HSV色彩空间定义了一个条件,用于确定一个点是否符合用户定义的颜色阈值。该类内有一个函数 evaluate,用于评估输入点的颜色是否位于定义的HSV值范围内

  2. filterRed 和 filterGreen 函数使用 pcl::ConditionalRemoval 类和 ConditionThresholdHSV 类来过滤出红色和绿色成分的点云。它们读取输入的点云,应用条件删除滤波器,并输出符合条件的点云。

  3. downsample 函数通过 pcl::VoxelGrid 类对输入点云进行体素化下采样,从而减少数据点的数量。

  4. extractLargestCluster 函数利用 pcl::EuclideanClusterExtraction 类从点云中提取最大的欧几里得距离聚类。

  5. compute 函数结合上述几个函数,先对输入点云进行下采样和颜色过滤,然后通过 pcl::SACSegmentation 类的随机抽样一致性 (RANSAC) 算法提取直线模型,找到支持直线模型的点集(内点)。

  6. main 函数则设置了程序的主要逻辑流程。首先,它创建了可视化对象 pcl::visualization::PCLVisualizer 并进行初始化,然后它加载点云文件,对读入的点云应用 compute 函数进行处理,并通过可视化工具显示结果。根据内点和模型系数绘制直线,并使用不同的参数展示不同的点云部分。

总的来说,该代码的主要目的是:

  • 通过HSV色彩空间过滤点云中的特定颜色(例如红色和绿色)。

  • 降采样处理点云数据。

  • 提取颜色过滤后点云的最大距离聚类。

  • 使用RANSAC算法提取直线并可视化结果。

方法上,该代码结合了多种PCL库提供的工具,如条件删除、聚类提取、模型分割和可视化,并通过封装成函数,实现了点云数据处理和特征提取的自动化。

#include <pcl/common/distances.h> // 导入PCL库中处理点云距离计算的头文件
#include <pcl/console/parse.h> // 导入PCL库中解析控制台参数的头文件
#include <pcl/console/time.h> // 导入PCL库中处理时间的头文件
#include <pcl/point_types.h> // 导入PCL库中定义点云类型的头文件
#include <pcl/memory.h> // 导入PCL库中处理内存分配的头文件
#include <pcl/point_cloud.h> // 导入PointCloud类的头文件,用于处理点云数据
#include <pcl/io/pcd_io.h> // 导入PCL库中读写PCD文件的头文件
#include <pcl/filters/voxel_grid.h> // 导入PCL库中体素滤波器的头文件,用于降采样点云
#include <pcl/sample_consensus/method_types.h> // 导入PCL库中抽样一致性方法类型的头文件
#include <pcl/segmentation/sac_segmentation.h> // 导入PCL库中基于抽样一致性的分割方法的头文件
#include <pcl/visualization/pcl_visualizer.h> // 导入PCL库中点云可视化的头文件
#include <pcl/filters/conditional_removal.h> // 导入PCL库中基于条件移除的滤波器的头文件
#include <pcl/segmentation/extract_clusters.h> // 导入PCL库中基于欧式距离的聚类提取的头文件


#define MIN_NR_INLIERS_LINE 40 // 定义直线模型最小内点数量的宏


// 定义一个基于HSV颜色阈值的条件类,用于过滤点云中的点
template <typename PointT>
class ConditionThresholdHSV : public pcl::ConditionBase<PointT>
{
  public:
    typedef pcl::shared_ptr<ConditionThresholdHSV<PointT> > Ptr; // 定义智能指针类型
    
    // 构造函数,初始化HSV颜色阈值
    ConditionThresholdHSV (float min_h, float max_h, float min_s, float max_s, float min_v, float max_v) :
      min_h_(min_h), max_h_(max_h), min_s_(min_s), max_s_(max_s), min_v_(min_v), max_v_(max_v)
    {
      // 确保min_h和max_h在[0, 360)范围内
      assert (!std::isnan(min_h) && !std::isnan(max_h));
      while (min_h_ < 0) min_h_ += 360;
      while (min_h_ >= 360) min_h_ -= 360;
      while (max_h_ < 0) max_h_ += 360;
      while (max_h_ >= 360) max_h_ -= 360;
    }
    
    // 评估函数,判断点的颜色是否在指定的HSV阈值范围内
    virtual bool evaluate(const PointT & p) const
{
      float h, s, v;
      rgb2hsv (p.r, p.g, p.b, h, s, v); // 将RGB颜色转换为HSV颜色
      return (!std::isnan(h) && !std::isnan(s) && !std::isnan(v) && 
              ((min_h_ < max_h_) ? ((min_h_ <= h) && (h <= max_h_)) : ((min_h_ <= h) || (h <= max_h_))) &&
              (min_s_ <= s) && (s <= max_s_) &&
              (min_v_ <= v) && (v <= max_v_));
    }
    
    // RGB到HSV颜色转换函数
    void rgb2hsv (std::uint8_t r, std::uint8_t g, std::uint8_t b, float & h, float & s, float & v) const
{
      float maxval = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b);
      float minval = (r < g) ? ((r < b) ? r : b) : ((g < b) ? g : b);
      float minmaxdiff = maxval - minval;
      
      if (maxval == minval)
      {
        h = 0;
        s = 0;
        v = maxval;
        return;
      }   
      else if (maxval == r)
      {
        h = 60.0*((g - b)/minmaxdiff);
        if (h < 0) h += 360.0;
      }
      else if (maxval == g)
      {
        h = 60.0*((b - r)/minmaxdiff + 2.0);
      }
      else // (maxval == b)
      {
        h = 60.0*((r - g)/minmaxdiff + 4.0);
      }
      s = 100.0 * minmaxdiff / maxval;
      v = maxval;
    }


  protected:
    float min_h_, max_h_, min_s_, max_s_, min_v_, max_v_; // 定义HSV颜色阈值变量
};


// 定义过滤红色点云的函数
void 
filterRed (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr &input, pcl::PointCloud<pcl::PointXYZRGB>::Ptr &output)
{
  pcl::ConditionalRemoval<pcl::PointXYZRGB> removal_filter; // 创建条件移除滤波器
  removal_filter.setKeepOrganized (false); // 设置是否保持点云的组织结构
  ConditionThresholdHSV<pcl::PointXYZRGB>::Ptr condition (new ConditionThresholdHSV<pcl::PointXYZRGB> (-20,20, 75,100, 25,255)); // 创建HSV颜色阈值条件
  removal_filter.setCondition (condition); // 设置滤波条件


  removal_filter.setInputCloud (input); // 设置输入点云
  removal_filter.filter (*output); // 过滤点云
}


// 定义过滤绿色点云的函数
void 
filterGreen (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr &input, pcl::PointCloud<pcl::PointXYZRGB>::Ptr &output)
{
  pcl::ConditionalRemoval<pcl::PointXYZRGB> removal_filter; // 创建条件移除滤波器
  removal_filter.setKeepOrganized (false); // 设置是否保持点云的组织结构
  ConditionThresholdHSV<pcl::PointXYZRGB>::Ptr condition (new ConditionThresholdHSV<pcl::PointXYZRGB> (90,150, 15,100, 25,255)); // 创建HSV颜色阈值条件
  removal_filter.setCondition (condition); // 设置滤波条件


  removal_filter.setInputCloud (input); // 设置输入点云
  removal_filter.filter (*output); // 过滤点云
}


// 定义降采样点云的函数
void
downsample (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr &input, 
            pcl::PointCloud<pcl::PointXYZRGB>::Ptr &output)
{
  pcl::VoxelGrid<pcl::PointXYZRGB> pass; // 创建体素栅格滤波器
  pass.setInputCloud (input); // 设置输入点云
  pass.setLeafSize (0.005, 0.005, 0.005); // 设置体素大小
  pass.setFilterFieldName ("z"); // 设置过滤字段为z轴
  pass.setFilterLimits (0.0, 2.0); // 设置过滤范围
  pass.filter (*output); // 过滤点云
}


// 定义提取最大聚类的函数
void
extractLargestCluster (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr &input, 
                       const pcl::PointIndices::Ptr &inliers_all,
                       pcl::PointIndices &inliers)
{
  pcl::EuclideanClusterExtraction<pcl::PointXYZRGB> ece; // 创建欧式聚类提取器
  ece.setInputCloud (input); // 设置输入点云
  ece.setIndices (inliers_all); // 设置内点索引
  ece.setClusterTolerance (0.3);   // 设置聚类容忍度为30cm
  std::vector<pcl::PointIndices> clusters; // 创建聚类索引容器
  ece.extract (clusters); // 提取聚类
  inliers = clusters[0]; // 获取最大聚类的索引
}


// 定义计算点云处理的主函数
void
compute (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr &input, 
         pcl::PointCloud<pcl::PointXYZRGB>::Ptr &output,
         pcl::ModelCoefficients &coefficients,
         pcl::PointIndices &inliers)
{
  // 过滤器
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr output_down (new pcl::PointCloud<pcl::PointXYZRGB>); // 创建输出点云
  downsample (input, output_down); // 降采样点云


  if (output_down->points.empty ())
  {
    inliers.indices.clear ();
    coefficients.values.clear ();
    return;
  }
  filterGreen (output_down, output); // 过滤绿色点云


  if (output->points.empty ())
  {
    inliers.indices.clear ();
    coefficients.values.clear ();
    return;
  }
  pcl::SACSegmentation<pcl::PointXYZRGB> seg; // 创建基于抽样一致性的分割对象
  seg.setInputCloud (output); // 设置输入点云
  seg.setOptimizeCoefficients (false); // 设置是否优化模型系数
  seg.setProbability (0.99); // 设置抽样一致性概率
  seg.setMaxIterations (10000); // 设置最大迭代次数
  seg.setModelType (pcl::SACMODEL_STICK); // 设置模型类型为直线模型
  seg.setMethodType (pcl::SAC_RANSAC); // 设置采用RANSAC方法
  seg.setDistanceThreshold (0.02); // 设置距离阈值
  //seg.setRadiusLimits (0.02, 0.08);
  pcl::PointIndices::Ptr inliers_all (new pcl::PointIndices); // 创建内点索引指针
  seg.segment (*inliers_all, coefficients); // 执行分割,获取内点和模型系数
  if (inliers_all->indices.size () < MIN_NR_INLIERS_LINE) // 判断内点数量是否满足最小要求
  {
    inliers.indices.clear ();
    coefficients.values.clear ();
    return;
  }


  extractLargestCluster (output, inliers_all, inliers); // 提取最大聚类
}


// 程序主入口函数
int
main (int argc, char** argv)
{
  srand (time (0)); // 初始化随机数生成器


  pcl::visualization::PCLVisualizer p (argc, argv, "Line segmentation"); // 创建一个可视化对象,用于线段分割的展示


  pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>); // 创建输入点云指针
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_d (new pcl::PointCloud<pcl::PointXYZRGB>); // 创建降采样指针
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_f (new pcl::PointCloud<pcl::PointXYZRGB>); // 创建过滤后的点云指针
  pcl::ModelCoefficients coefficients; // 创建模型系数对象
  pcl::PointIndices inliers; // 创建内点索引对象


  std::vector<int> p_file_indices = pcl::console::parse_file_extension_argument (argc, argv, ".pcd"); // 分析命令行参数,获取PCD文件索引


  for (std::size_t i = 0; i < p_file_indices.size (); ++i)
  {
    pcl::io::loadPCDFile (argv[p_file_indices[i]], *cloud); // 加载PCD文件到点云对象
    
    // 计算
    pcl::console::TicToc tt; // 创建计时对象
    tt.tic (); // 开始计时
    compute (cloud, cloud_f, coefficients, inliers); // 进行点云处理
    tt.toc_print (); // 打印时间消耗


    if (inliers.indices.empty ()) // 判断是否找到内点
    {
      p.removeShape ("line"); // 若未找到则移除之前的线形状
      continue;
    }


    // 展示结果
    PCL_INFO ("Found %lu inliers.\n", inliers.indices.size ()); // 打印找到的内点数量


    pcl::PointCloud<pcl::PointXYZ>::Ptr line (new pcl::PointCloud<pcl::PointXYZ>); // 创建用于展示的线点云指针
    pcl::copyPointCloud (*cloud_f, inliers, *line); // 复制内点到线点云


    if (!p.updatePointCloud (cloud, "all")) // 若无法更新,则新增点云
    {
      p.addPointCloud (cloud, "all"); // 添加输入点云
      p.resetCameraViewpoint ("all"); // 重置相机视角
    }


    if (!p.updatePointCloud (cloud_f, "filter")) // 若无法更新,则新增过滤后的点云
      p.addPointCloud (cloud_f, "filter"); // 添加过滤后的点云
    p.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10.0, "filter"); // 设置点大小
    p.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_OPACITY, 0.2, "filter"); // 设置透明度


    if (!p.updatePointCloud (line, "line inliers"))  // 若无法更新,则新增线内点的点云
      p.addPointCloud (line, "line inliers"); // 添加线内点的点云


    pcl::PointXYZRGB pmin, pmax; // 创建最大最小点对象
    if (pcl::getMaxSegment (*cloud_f, inliers.indices, pmin, pmax) != std::numeric_limits<double>::min ()) // 获取最大段落
      p.addLine<pcl::PointXYZRGB> (pmin, pmax); // 添加线段
    else
    {
      pmin.x = coefficients.values[0]; pmin.y = coefficients.values[1]; pmin.z = coefficients.values[2]; // 设置直线模型的起点
      pmax.x = coefficients.values[3]; pmax.y = coefficients.values[4]; pmax.z = coefficients.values[5]; // 设置直线模型的终点
      PCL_ERROR ("Couldn't compute the maximum segment!\n");
      p.addLine<pcl::PointXYZRGB> (pmin, pmax); // 添加直线模型
      //p.addLine (coefficients);
    }
    p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 50.0, "line"); // 设置线宽
    p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 1.0, 0.0, "line"); // 设置线颜色


    if (p_file_indices.size () == 1) // 若只有一个文件,进行全屏显示
      p.spin (); // 全屏显示
    p.spinOnce (); // 进行一次显示更新
    p.removeShape ("line"); // 移除线形状
  }


  if (p_file_indices.size () != 1) // 若有多个文件,进行显示
    p.spin (); // 显示


  return (0); // 返回0,程序结束
}

此段代码实现了一个基于点云库(PCL)的具体应用,主要包括点云的滤波、降采样、颜色阈值过滤、欧式聚类提取以及线段分割等过程。通过定义了滤红、滤绿函数对点云中特定颜色的点进行过滤,利用条件移除滤波器结合HSV色彩空间实现。然后,使用体素栅格方法对点云进行降采样,提高处理速度。接着,通过欧式聚类提取方法提取出点云中最大的聚类,最后采用抽样一致性(SAC)模型对过滤后的点云进行线段分割,识别并显示出线段。整个处理流程展示了PCL库在机器视觉中处理点云数据的强大能力,特别是在颜色过滤、点云降采样、聚类提取和特征识别等方面有着广泛的应用场景。

ConditionThresholdHSV<pcl::PointXYZRGB>::Ptr condition(
      new ConditionThresholdHSV<pcl::PointXYZRGB>(
          -20, 20, 75, 100, 25, 255));

7f913a548f627737b0af4fdb4a621602.png

pcl::EuclideanClusterExtraction<pcl::PointXYZRGB> ece

f615a5e991f63e4b4ea2ea14a87996d6.png

pcl::getMaxSegment(*cloud_f, inliers.indices, pmin, pmax)

44a219ada1e31fb21e223ad97b0ad33d.png

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS(机器人操作系统)是一个基于开源的软件平台,广泛应用于机器人控制、导航、感知和执行等方面。其中,ROS的激光点云处理模块是非常重要的部分。激光点云处理主要是针对激光测距仪或LIDAR等传感器采集到的数据进行处理,实现对环境的建模、地图构建以及目标检测等功能。本文主要介绍基于ROS的激光点云处理中的三个重要方面:点云降采样欧式聚类分割的目标检测和地面拟合分割。 首先是点云降采样。对于一些大规模的三维点云数据,通常需要对其进行降采样减少数据量以及提高点云处理效率。ROS中,PointCloud2节点提供了一个非常灵活的点云降采样模块,可以通过ROS消息类型进行订阅、降采样处理和发布。同时,ROS中也提供了一些基本的降采样算法,如均匀采样、随机采样、体素滤波等。 其次是基于欧式聚类分割的目标检测。在激光点云传感器中,目标物体通常是一些密集的点云簇,通过欧式聚类算法可以将属于同一目标的点云簇进行分割,从而实现目标的检测。ROS中,可以通过PCL库实现欧式聚类分割算法,并结合提取出的目标点云簇进行目标检测、跟踪和分类等任务。 最后是地面拟合分割。很多情况下,机器人需要整体地分析环境,而非只是分析某些目标。因此,在处理激光数据时,需要将地面和非地面点云进行分别处理,以便更好地进行环境建模和点云分类等任务。ROS中,可以通过PCL库实现RANSAC算法对垂直平面进行拟合,从而实现对地面点云拟合和分割。 综上所述,基于ROS的激光点云处理可以实现多种功能,包括点云降采样欧式聚类分割的目标检测和地面拟合分割。这些功能可以为机器人的环境感知和控制提供优秀的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值