欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > 2条件欧几里得聚类

2条件欧几里得聚类

2024/10/25 3:26:53 来源:https://blog.csdn.net/m0_71087087/article/details/141950426  浏览:    关键词:2条件欧几里得聚类

条件欧几里得聚类

本教程介绍如何使用类: 一种分割算法,根据欧几里得距离和需要保持的用户可自定义条件对点进行聚类。pcl::ConditionalEuclideanClustering

此类使用与 Euclidean Cluster Extraction、Region growing segmentation 和 Color-based regional growing segmentation 中使用的相同 greedy-like /region-growing /flood-fill 方法。 与其他类相比,使用此类的优势在于,聚类的约束(纯欧几里得、平滑度、RGB)现在可以由用户自定义。 一些缺点包括:没有初始种子系统,没有过度和不足分段控制,以及从主计算循环内部调用条件函数的时间效率较低。

理论入门

Euclidean Cluster Extraction 和 Region growing segmentation 教程已经非常准确地解释了区域增长算法。 对这些解释的唯一补充是,现在可以完全自定义将邻居合并到当前集群中所需的条件。

随着聚类的增长,它将评估聚类内已有的点与附近的候选点之间的用户定义条件。 候选点(最近邻点)使用围绕聚类中每个点的欧氏半径搜索来查找。 对于生成的聚类中的每个点,条件需要至少与一个相邻要素保持,而不是与其所有相邻要素保持。

Conditional Euclidean Clustering 类还可以根据大小约束自动筛选聚类。 分类为“太小”或“太大”的集群仍可在之后检索。

代码

首先,下载数据集 Statues_4.pcd 并从存档中提取 PCD 文件。 这是一个非常大的室外环境数据集,我们的目标是将单独的对象聚集在一起,并且还希望将建筑物与地平面分开,即使它是以欧几里得意义连接的。

现在,在您最喜欢的编辑器中创建一个文件,并将以下内容放入其中:conditional_euclidean_clustering.cpp

  1#include <pcl/point_types.h>
  2#include <pcl/io/pcd_io.h>
  3#include <pcl/console/time.h>
  4
  5#include <pcl/filters/voxel_grid.h>
  6#include <pcl/features/normal_3d.h>
  7#include <pcl/segmentation/conditional_euclidean_clustering.h>
  8
  9typedef pcl::PointXYZI PointTypeIO;
 10typedef pcl::PointXYZINormal PointTypeFull;
 11
 12bool
 13enforceIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float /*squared_distance*/)
 14{
 15  if (std::abs (point_a.intensity - point_b.intensity) < 5.0f)
 16    return (true);
 17  else
 18    return (false);
 19}
 20
 21bool
 22enforceNormalOrIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float /*squared_distance*/)
 23{
 24  Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
 25  if (std::abs (point_a.intensity - point_b.intensity) < 5.0f)
 26    return (true);
 27  if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))
 28    return (true);
 29  return (false);
 30}
 31
 32bool
 33customRegionGrowing (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance)
 34{
 35  Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
 36  if (squared_distance < 10000)
 37  {
 38    if (std::abs (point_a.intensity - point_b.intensity) < 8.0f)
 39      return (true);
 40    if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))
 41      return (true);
 42  }
 43  else
 44  {
 45    if (std::abs (point_a.intensity - point_b.intensity) < 3.0f)
 46      return (true);
 47  }
 48  return (false);
 49}
 50
 51int
 52main ()
 53{
 54  // Data containers used
 55  pcl::PointCloud<PointTypeIO>::Ptr cloud_in (new pcl::PointCloud<PointTypeIO>), cloud_out (new pcl::PointCloud<PointTypeIO>);
 56  pcl::PointCloud<PointTypeFull>::Ptr cloud_with_normals (new pcl::PointCloud<PointTypeFull>);
 57  pcl::IndicesClustersPtr clusters (new pcl::IndicesClusters), small_clusters (new pcl::IndicesClusters), large_clusters (new pcl::IndicesClusters);
 58  pcl::search::KdTree<PointTypeIO>::Ptr search_tree (new pcl::search::KdTree<PointTypeIO>);
 59  pcl::console::TicToc tt;
 60
 61  // Load the input point cloud
 62  std::cerr << "Loading...\n", tt.tic ();
 63  pcl::io::loadPCDFile ("Statues_4.pcd", *cloud_in);
 64  std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_in->size () << " points\n";
 65
 66  // Downsample the cloud using a Voxel Grid class
 67  std::cerr << "Downsampling...\n", tt.tic ();
 68  pcl::VoxelGrid<PointTypeIO> vg;
 69  vg.setInputCloud (cloud_in);
 70  vg.setLeafSize (80.0, 80.0, 80.0);
 71  vg.setDownsampleAllData (true);
 72  vg.filter (*cloud_out);
 73  std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_out->size () << " points\n";
 74
 75  // Set up a Normal Estimation class and merge data in cloud_with_normals
 76  std::cerr << "Computing normals...\n", tt.tic ();
 77  pcl::copyPointCloud (*cloud_out, *cloud_with_normals);
 78  pcl::NormalEstimation<PointTypeIO, PointTypeFull> ne;
 79  ne.setInputCloud (cloud_out);
 80  ne.setSearchMethod (search_tree);
 81  ne.setRadiusSearch (300.0);
 82  ne.compute (*cloud_with_normals);
 83  std::cerr << ">> Done: " << tt.toc () << " ms\n";
 84
 85  // Set up a Conditional Euclidean Clustering class
 86  std::cerr << "Segmenting to clusters...\n", tt.tic ();
 87  pcl::ConditionalEuclideanClustering<PointTypeFull> cec (true);
 88  cec.setInputCloud (cloud_with_normals);
 89  cec.setConditionFunction (&customRegionGrowing);
 90  cec.setClusterTolerance (500.0);
 91  cec.setMinClusterSize (cloud_with_normals->size () / 1000);
 92  cec.setMaxClusterSize (cloud_with_normals->size () / 5);
 93  cec.segment (*clusters);
 94  cec.getRemovedClusters (small_clusters, large_clusters);
 95  std::cerr << ">> Done: " << tt.toc () << " ms\n";
 96
 97  // Using the intensity channel for lazy visualization of the output
 98  for (const auto& small_cluster : (*small_clusters))
 99    for (const auto& j : small_cluster.indices)
100      (*cloud_out)[j].intensity = -2.0;
101  for (const auto& large_cluster : (*large_clusters))
102    for (const auto& j : large_cluster.indices)
103      (*cloud_out)[j].intensity = +10.0;
104  for (const auto& cluster : (*clusters))
105  {
106    int label = rand () % 8;
107    for (const auto& j : cluster.indices)
108      (*cloud_out)[j].intensity = label;
109  }
110
111  // Save the output point cloud
112  std::cerr << "Saving...\n", tt.tic ();
113  pcl::io::savePCDFile ("output.pcd", *cloud_out);
114  std::cerr << ">> Done: " << tt.toc () << " ms\n";
115
116  return (0);
117}

解释

由于 Conditional Euclidean Clustering 类适用于更高级的用户,因此我将跳过对代码中更明显的部分的解释:

  • pcl::io::loadPCDFile,用于加载和保存点云数据。pcl::io::savePCDFile

  • pcl::console::TicToc用于轻松输出计时结果。

  • 使用 VoxelGrid 过滤器对 PointCloud 进行下采样(第 66-73 行)来对云进行下采样并提供更均衡的点密度。

  • 在 PointCloud 中估计表面法线(第 75-83 行)用于估计将附加到点信息的法线; 条件欧几里得聚类类将使用 模板化,其中包含要在条件函数中使用的 x、y、z、强度、法线和曲率信息。pcl::PointXYZINormal

第 85-95 行设置了要使用的 Conditional Euclidean Clustering 类:

  // Set up a Conditional Euclidean Clustering classstd::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";

对不同代码行的更详细描述:

  • 该类使用 TRUE 进行初始化。 这将允许提取太小或太大的集群。 如果在没有 this 的情况下初始化类,它可以节省一些计算时间和内存。

  • 可以使用从类派生的方法指定类的输入数据,即: 和 .PCLBasesetInputCloudsetIndices

  • 随着集群的增长,它将评估集群内已有的点与附近的候选点之间的用户定义条件。 有关 condition 函数的更多信息可以在下面进一步阅读。

  • 拓扑容差是 k-NN 搜索的半径,用于查找候选点。

  • 占云总点数 0.1% 的集群被认为太小。

  • 占云总点数 20% 以上的集群被视为太大。

  • 生成的集群以索引数组的格式存储,该格式是索引数组,即输入点云的索引点。pcl::IndicesClusters

  • 太小或太大的集群不会传递到主输出,而是可以在单独的数据容器中检索,但前提是类是使用 TRUE 初始化的。pcl::IndicesClusters

第 12-49 行显示了条件函数的一些示例:

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);elsereturn (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);if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))return (true);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){if (std::abs (point_a.intensity - point_b.intensity) < 8.0f)return (true);if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))return (true);}else{if (std::abs (point_a.intensity - point_b.intensity) < 3.0f)return (true);}return (false);
}

条件函数的格式是固定的:

  • 前两个输入参数的类型必须与 Conditional Euclidean Clustering 类中使用的模板类型相同。 这些参数将传递当前种子点(第一个参数)和当前候选点(第二个参数)的点信息。

  • 第三个 input 参数需要是 float。 此参数将传递种子点和候选点之间的平方距离。 虽然这个信息也可以使用前两个参数进行计算,但它已经由底层的最近邻搜索提供,并且可以很容易地用于制作一个与距离相关的条件函数。

  • output 参数需要是布尔值。 返回 TRUE 会将候选点合并到种子点的集群中。 返回 FALSE 不会通过此特定点对合并候选点,但是,这两个点仍有可能通过不同的点对关系最终位于同一集群中。

这些示例条件函数只是为了指示如何使用它们。 例如,只要集群在表面法线方向上相似或强度值相似,第二个条件函数就会增加集群。 这应该希望将纹理相似的建筑物群集为一个群集,但不会将它们与相邻对象合并到同一群集中。 这将计算出强度是否与附近的对象足够不同,并且附近的对象没有共享具有相同法线的附近表面。 第三个条件函数与第二个条件函数类似,但根据点之间的距离具有不同的约束。

第 97-109 行包含一段代码,该代码是一个快速而肮脏的修复,用于可视化结果:

  // Using the intensity channel for lazy visualization of the outputfor (const auto& small_cluster : (*small_clusters))for (const auto& j : small_cluster.indices)(*cloud_out)[j].intensity = -2.0;for (const auto& large_cluster : (*large_clusters))for (const auto& j : large_cluster.indices)(*cloud_out)[j].intensity = +10.0;for (const auto& cluster : (*clusters)){int label = rand () % 8;for (const auto& j : cluster.indices)(*cloud_out)[j].intensity = label;}

当使用 PCL 的标准 PCD 查看器打开输出点云时,按“5”将切换到强度通道可视化。 太小的群集将变为红色,太大的群集将变为蓝色,实际感兴趣的群集/对象将在黄色和青色之间随机着色。

编译和运行程序

将以下行添加到您的CMakeLists.txt

 1cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
 2
 3project(conditional_euclidean_clustering)
 4
 5find_package(PCL 1.7 REQUIRED)
 6
 7include_directories(${PCL_INCLUDE_DIRS})
 8link_directories(${PCL_LIBRARY_DIRS})
 9add_definitions(${PCL_DEFINITIONS})
10
11add_executable (conditional_euclidean_clustering conditional_euclidean_clustering.cpp)
12target_link_libraries (conditional_euclidean_clustering ${PCL_LIBRARIES})

创建可执行文件后,您可以运行它。只需:

$ ./conditional_euclidean_clustering

生成的输出点云可以按如下方式打开:

$ ./pcl_viewer output.pcd

您应该会看到类似于以下内容的内容:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com