【PCL】教程conditional_euclidean_clustering 对输入的点云数据进行条件欧式聚类分析...

4ccccfc0f1340a58f60cb5daa0910890.png

[done, 3349.09 ms : 19553780 points]
Available dimensions: x y z intensity

源点云 Statues_4.pcd

becdbc15ee6f90f59ee3d380a39ba4fd.png

6afaafb5d309c2c0ce8965a184a6e045.png

d71f85639ee5bbf01816308d978fc890.png

a6983c7961f920447409c866299c9d76.png

不同条件函数output.pcd 【按5切换到强度通道可视化】

终端输出:

Loading...
>> Done: 1200.46 ms, 19553780 points
Downsampling...
>> Done: 411.366 ms, 202529 points
Computing normals...
>> Done: 717.815 ms
Segmenting to clusters...
>> Done: 1616.51 ms
Saving...
>> Done: 590.647 ms

点云下载:

https://sourceforge.net/projects/pointclouds/files/PCD%20datasets/Trimble/Outdoor1/

源码解析:

6bb52c40003fdd5c081be3262e0ec3ce.png

#include <pcl/point_types.h> // 包含PCL库中点云类型的头文件
#include <pcl/io/pcd_io.h> // 包含PCL库中用于点云读写的头文件
#include <pcl/console/time.h> // 包含PCL库中用于计时的头文件


#include <pcl/filters/voxel_grid.h> // 包含PCL库中体素栅格滤波器的头文件
#include <pcl/features/normal_3d.h> // 包含PCL库中用于计算点云中点的法向量的头文件
#include <pcl/segmentation/conditional_euclidean_clustering.h> // 包含PCL库中条件欧几里得聚类算法的头文件


typedef pcl::PointXYZI PointTypeIO; // 定义带有强度信息的3D点类型
typedef pcl::PointXYZINormal PointTypeFull; // 定义带有强度和法线信息的3D点类型


bool
enforceIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float /*squared_distance*/)
{
  if (std::abs (point_a.intensity - point_b.intensity) < 5.0f)
    return (true); // 如果两个点的强度相差小于5,认为它们相似
  else
    return (false);
}


bool
enforceNormalOrIntensitySimilarity2(const PointTypeFull& point_a,
                                      const PointTypeFull& point_b,
                                      float squared_distance) // 曲率强度相似性
{
  Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap(),
                                    point_b_normal = point_b.getNormalVector3fMap();
  if (fabs(point_a.intensity - point_b.intensity) < 5.0f) // 如果a点的密度-b点的密度<5
    return (true);
  if (fabs(point_a_normal.dot(point_b_normal)) < 0.05) // 如果a点法线估计
    return (true);
  return (false);
}


bool
enforceNormalOrIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float /*squared_distance*/)
{
  Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
  if (std::abs (point_a.intensity - point_b.intensity) < 5.0f)
    return (true); // 如果强度相差小于5,认为相似
  if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))
    return (true); // 如果法线方向的夹角小于30度,也认为相似
  return (false);
}


bool
customRegionGrowing (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance)
{
  Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
  if (squared_distance < 10000) // 距离小于10000
  {
    if (std::abs (point_a.intensity - point_b.intensity) < 8.0f) // 强度相差小于8
      return (true);
    if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI))) // 法线夹角小于30度
      return (true);
  }
  else
  {
    if (std::abs (point_a.intensity - point_b.intensity) < 3.0f) // 距离大于10000时,强度相差小于3
      return (true);
  }
  return (false);
}


int
main ()
{
  // 用于存储点云的数据容器
  pcl::PointCloud<PointTypeIO>::Ptr cloud_in (new pcl::PointCloud<PointTypeIO>), cloud_out (new pcl::PointCloud<PointTypeIO>);
  pcl::PointCloud<PointTypeFull>::Ptr cloud_with_normals (new pcl::PointCloud<PointTypeFull>);
  // 用于存储聚类结果的数据容器
  pcl::IndicesClustersPtr clusters (new pcl::IndicesClusters), small_clusters (new pcl::IndicesClusters), large_clusters (new pcl::IndicesClusters);
  pcl::search::KdTree<PointTypeIO>::Ptr search_tree (new pcl::search::KdTree<PointTypeIO>); // Kd树搜索方法
  pcl::console::TicToc tt; // 用于计时


  // 加载输入的点云
  std::cerr << "Loading...\n", tt.tic ();
  pcl::io::loadPCDFile ("Statues_4.pcd", *cloud_in);
  std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_in->size () << " points\n";


  // 使用体素栅格类下采样点云
  std::cerr << "Downsampling...\n", tt.tic ();
  pcl::VoxelGrid<PointTypeIO> vg;
  vg.setInputCloud (cloud_in);
  vg.setLeafSize (80.0, 80.0, 80.0); // 设置体素大小
  vg.setDownsampleAllData (true); // 设置下采样时是否处理所有数据
  vg.filter (*cloud_out);
  std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_out->size () << " points\n";


  // 设置法线估计类并合并数据到包含法线的点云中
  std::cerr << "Computing normals...\n", tt.tic ();
  pcl::copyPointCloud (*cloud_out, *cloud_with_normals); // 将经过下采样的点云数据复制到带有法线信息的点云容器中
  pcl::NormalEstimation<PointTypeIO, PointTypeFull> ne; // 创建一个法线估计对象
  ne.setInputCloud (cloud_out); // 设置该对象的输入点云为下采样之后的点云
  ne.setSearchMethod (search_tree); // 设置搜索方法为Kd树
  ne.setRadiusSearch (300.0); // 设置法线估计时的搜索半径为300.0
  ne.compute (*cloud_with_normals); // 计算输入点云的每个点的法线,并将结果存储到带有法线信息的点云容器中
    std::cerr << ">> Done: " << tt.toc () << " ms\n";


  // 设置条件欧几里得聚类类
  std::cerr << "Segmenting to clusters...\n", tt.tic ();
  pcl::ConditionalEuclideanClustering<PointTypeFull> cec (true);
  cec.setInputCloud (cloud_with_normals);
  cec.setConditionFunction (&customRegionGrowing); // 设置条件函数
  cec.setClusterTolerance (500.0); // 设置聚类容忍度
  cec.setMinClusterSize (cloud_with_normals->size () / 1000); // 设置最小聚类大小
  cec.setMaxClusterSize (cloud_with_normals->size () / 5); // 设置最大聚类大小
  cec.segment (*clusters); // 执行聚类
  cec.getRemovedClusters (small_clusters, large_clusters); // 获取移除的聚类结果
  std::cerr << ">> Done: " << tt.toc () << " ms\n";


  // 使用强度通道进行简单的可视化输出
  for (const auto& small_cluster : (*small_clusters))
    for (const auto& j : small_cluster.indices)
      (*cloud_out)[j].intensity = -2.0; // 较小的聚类强度设置为-2
  for (const auto& large_cluster : (*large_clusters))
    for (const auto& j : large_cluster.indices)
      (*cloud_out)[j].intensity = +10.0; // 较大的聚类强度设置为+10
  for (const auto& cluster : (*clusters))
  {
    int label = rand () % 8; // 为每个聚类随机分配一个标签
    for (const auto& j : cluster.indices)
      (*cloud_out)[j].intensity = label; // 设置对应的强度值
  }


  // 保存输出的点云
  std::cerr << "Saving...\n", tt.tic ();
  pcl::io::savePCDFile ("output.pcd", *cloud_out);
  std::cerr << ">> Done: " << tt.toc () << " ms\n";


  return (0);
}

这段代码使用了PCL(Point Cloud Library)库,其主要的功能是对输入的点云数据进行条件欧式聚类分析

主要步骤如下:

  1. 使用pcl::VoxelGrid类进行下采样:将输入点云的密度降低,用于提高后续操作的效率。这通过指定立方体体素的边长(80.0),将点云中处于同一立方体体素内的所有点替换为他们的质心。

  2. 计算点云中每一个点的法线:使用pcl::NormalEstimation类计算每一个点的法线,这将作为后续操作的一部分使用。setRadiusSearch用于指定查找近邻的半径。

  3. 使用pcl::ConditionalEuclideanClustering类进行条件欧式聚类:在这个过程中,每一个点都会被放入一个特定的群组(也就是聚类)。算法会根据定义的距离容忍度(在本代码中为500)和自定义的函数customRegionGrowing来确定点是否应属于当前的聚类。所定义的函数中,需要注意的是,据点之间的强度差异(小于8)和点法线之间的角度(小于或等于30度),或者强度差异小于3的情况下,两点可以被视为同一聚类。聚类的最大和最小大小也被定义为输入点的数量的五分之一和千分之一。

  4. 对每个生成的聚类(条件欧式聚类)进行可视化处理:通过将不同聚类的点置为不同强度来区分它们。此处,较小的聚类将被设置为强度-2,较大的聚类将被设置为强度+10

  5. 保存处理后的点云数据,以便于后续的分析和处理。

在这段代码中,定义了四个自定义函数(enforceIntensitySimilarityenforceNormalOrIntensitySimilaritycustomRegionGrowing),这些函数被用作聚类过程中的条件函数,以便在决定如何聚类点时,可以根据强度和法线等属性进行更精细控制。

4e090a6f08554fc5cb78620f18af7f4c.png

踩坑笔记

6c9a75c1b628ea12e1833e148164c97d.png

dfaf11a3e73dacf2d3697d30df22cf72.png

预处理器添加:PCL_NO_PRECOMPILE

31d5211f137d5098c3c9ef898fbe5632.png

添加flann.lib

pcl::console::TicToc

0d6ab198a78d1dd0b671878185ce77d4.png

pcl::NormalEstimation<PointTypeIO, PointTypeFull> ne;

693a3a70100ba8531a2f39f5796ee293.png

#include <pcl/point_types.h>
#include <pcl/features/normal_estimation.h>
#include <pcl/io/pcd_io.h>
#include <pcl/kdtree/kdtree_flann.h>


int main() {
    // 加载点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile("inputCloud.pcd", *cloud);


    // 创建法线估计对象
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    ne.setInputCloud(cloud);


    // 创建一个空的kdtree表示,并将其设置为法线估计对象的搜索方法
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
    ne.setSearchMethod(tree);


    // 设置邻居搜索的参数
    ne.setKSearch(20);


    // 计算法线
    pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
    ne.compute(*cloud_normals);


    // cloud_normals 现在包含输入点云的法线
}
计算结果存储在之前复制的带有法线信息的点云数据结构中时,不会覆盖点坐标吗?

8c5e001a13d49d6dc137dd2e0216fa86.png

pcl::ConditionalEuclideanClustering<PointTypeFull>

214049c17df997361757038f0e493685.png

cec.getRemovedClusters (small_clusters, large_clusters);

28a568abf941fa4263ed7fda5c94757b.png

点云类型

c796b1c15f506c8be937630527c1b6ad.png

5eeca695efe9106c90ea257a3d846353.png

a1fce39f71222aa9dc0a0a3bf09dd8d3.png

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值