• [点云分割] 条件欧氏聚类分割


    介绍

    条件欧氏聚类分割是一种基于欧氏距离和条件限制的点云分割方法。它通过计算点云中点与点之间的欧氏距离,并结合一定的条件限制来将点云分割成不同的区域或聚类。

    在条件欧氏聚类分割中,通常会定义以下两个条件来判断两个点是否属于同一个聚类:

    1. 距离条件:两个点之间的欧氏距离是否小于设定的阈值。如果两个点之间的距离小于阈值,则认为它们是相邻的,属于同一个聚类。

    2. 条件限制:除了距离条件外,还可以根据其他的条件来限制聚类的形成。例如,可以限制点的法线方向、颜色、强度等属性的相似性,只有当这些属性满足一定的条件时,两个点才被认为是相邻的,属于同一个聚类。

    条件欧氏聚类分割的步骤通常包括以下几个步骤:

    1. 初始化:设置距离阈值和其他条件限制的参数。

    2. 遍历点云:对于点云中的每个点,依次进行以下操作:

      • 计算当前点与其周围点之间的欧氏距离。

      • 根据距离条件和其他条件限制,判断当前点是否与周围点属于同一个聚类。如果是,则将它们标记为同一个聚类。

      • 继续遍历其他未被标记的点,重复上述操作,直到所有点都被遍历完。

    3. 输出聚类结果:将同一个聚类的点标记为一组,形成不同的聚类簇。

    效果

    代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. typedef pcl::PointXYZI PointTypeIO;
    8. typedef pcl::PointXYZINormal PointTypeFull;
    9. bool enforceIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float /*squared_distance*/)
    10. {
    11. if (std::abs (point_a.intensity - point_b.intensity) < 5.0f)
    12. return (true);
    13. else
    14. return (false);
    15. }
    16. bool enforceNormalOrIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float /*squared_distance*/)
    17. {
    18. // 将点云的法线信息转换未Eigen库的Eigen:vector3f类型
    19. Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
    20. // 判断点云A的点云B的强度差是否小于5.0
    21. if (std::abs (point_a.intensity - point_b.intensity) < 5.0f)
    22. return (true);
    23. // 判断点云A和点云B的法线夹角的余弦值是否大于30°对应的余弦值,即判断法线相似性
    24. if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))
    25. return (true);
    26. return (false);
    27. }
    28. bool customRegionGrowing (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance)
    29. {
    30. Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
    31. // 根据平方距离的大小,判断生长条件
    32. if (squared_distance < 10000)
    33. {
    34. if (std::abs (point_a.intensity - point_b.intensity) < 8.0f)
    35. return (true);
    36. if (std::abs (point_a_normal.dot (point_b_normal)) > std::cos (30.0f / 180.0f * static_cast<float> (M_PI)))
    37. return (true);
    38. }
    39. else
    40. {
    41. if (std::abs (point_a.intensity - point_b.intensity) < 3.0f)
    42. return (true);
    43. }
    44. return (false);
    45. }
    46. int main ()
    47. {
    48. // Data containers used
    49. pcl::PointCloud::Ptr cloud_in (new pcl::PointCloud), cloud_out (new pcl::PointCloud);
    50. pcl::PointCloud::Ptr cloud_with_normals (new pcl::PointCloud);
    51. pcl::IndicesClustersPtr clusters (new pcl::IndicesClusters), small_clusters (new pcl::IndicesClusters), large_clusters (new pcl::IndicesClusters);
    52. pcl::search::KdTree::Ptr search_tree (new pcl::search::KdTree);
    53. pcl::console::TicToc tt;
    54. // Load the input point cloud
    55. std::cerr << "Loading...\n", tt.tic ();
    56. pcl::io::loadPCDFile ("Statues_4.pcd", *cloud_in);
    57. std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_in->size () << " points\n";
    58. // Downsample the cloud using a Voxel Grid class
    59. std::cerr << "Downsampling...\n", tt.tic ();
    60. pcl::VoxelGrid vg;
    61. vg.setInputCloud (cloud_in);
    62. vg.setLeafSize (80.0, 80.0, 80.0);
    63. vg.setDownsampleAllData (true);
    64. vg.filter (*cloud_out);
    65. std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_out->size () << " points\n";
    66. // Set up a Normal Estimation class and merge data in cloud_with_normals
    67. std::cerr << "Computing normals...\n", tt.tic ();
    68. pcl::copyPointCloud (*cloud_out, *cloud_with_normals);
    69. pcl::NormalEstimation ne;
    70. ne.setInputCloud (cloud_out);
    71. ne.setSearchMethod (search_tree);
    72. ne.setRadiusSearch (300.0);
    73. ne.compute (*cloud_with_normals);
    74. std::cerr << ">> Done: " << tt.toc () << " ms\n";
    75. // Set up a Conditional Euclidean Clustering class
    76. std::cerr << "Segmenting to clusters...\n", tt.tic ();
    77. pcl::ConditionalEuclideanClustering cec (true);
    78. cec.setInputCloud (cloud_with_normals);
    79. cec.setConditionFunction (&customRegionGrowing);
    80. cec.setClusterTolerance (500.0);
    81. cec.setMinClusterSize (cloud_with_normals->size () / 1000);
    82. cec.setMaxClusterSize (cloud_with_normals->size () / 5);
    83. cec.segment (*clusters);
    84. cec.getRemovedClusters (small_clusters, large_clusters);
    85. std::cerr << ">> Done: " << tt.toc () << " ms\n";
    86. // Using the intensity channel for lazy visualization of the output
    87. for (const auto& small_cluster : (*small_clusters))
    88. for (const auto& j : small_cluster.indices)
    89. (*cloud_out)[j].intensity = -2.0;
    90. for (const auto& large_cluster : (*large_clusters))
    91. for (const auto& j : large_cluster.indices)
    92. (*cloud_out)[j].intensity = +10.0;
    93. for (const auto& cluster : (*clusters))
    94. {
    95. int label = rand () % 8;
    96. for (const auto& j : cluster.indices)
    97. (*cloud_out)[j].intensity = label;
    98. }
    99. // Save the output point cloud
    100. std::cerr << "Saving...\n", tt.tic ();
    101. pcl::io::savePCDFile ("output.pcd", *cloud_out);
    102. std::cerr << ">> Done: " << tt.toc () << " ms\n";
    103. return (0);
    104. }

  • 相关阅读:
    扩展函数和运算符重载
    java---卡特兰数---满足条件的01序列(每日一道算法2022.9.29)
    【华为OD机试真题 python】勾股数元组 【2022 Q4 | 100分】
    CTFHub | 布尔盲注
    FFmpeg入门详解之53:Qt Qss FFplay SDL播放器实战项目
    vim中粘贴代码片段出现每行新增缩进的解决方法-set paste
    安利几个好用的图片转文字识别软件
    《回炉重造 Java 基础》——集合(容器)
    Python并发编程之进程间通信
    TinyRenderer学习笔记--Lesson 5
  • 原文地址:https://blog.csdn.net/weixin_45824067/article/details/134562868