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

embedded/2024/10/18 9:18:48/

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,认为它们相似elsereturn (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点的密度<5return (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) // 强度相差小于8return (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时,强度相差小于3return (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.0ne.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; // 较小的聚类强度设置为-2for (const auto& large_cluster : (*large_clusters))for (const auto& j : large_cluster.indices)(*cloud_out)[j].intensity = +10.0; // 较大的聚类强度设置为+10for (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


http://www.ppmy.cn/embedded/6051.html

相关文章

数据结构 -- 二叉树二叉搜索树

二叉树 二叉树是这么一种树状结构&#xff1a;每个节点最多有两个孩子&#xff0c;左孩子和右孩子 重要的二叉树结构 完全二叉树&#xff08;complete binary tree&#xff09;是一种二叉树结构&#xff0c;除最后一层以外&#xff0c;每一层都必须填满&#xff0c;填充时要遵…

机器学习预测汽车油耗效率 MPG

流程 数据获取导入需要的包引入文件,查看内容划分训练集和测试集调用模型查看准确率 数据获取 链接&#xff1a;https://pan.baidu.com/s/1KeIJykbcVpsfEk0xjhiICA?pwd30oe 提取码&#xff1a;30oe --来自百度网盘超级会员V1的分享导入需要的包 import pandas as pd imp…

CSS中的圆角和阴影

目录 盒子圆角 圆角基础使用 圆角常见使用 通过设置盒子圆角得到一个圆形 通过设置盒子圆角&#xff0c;得到一个“操场”的样式 盒子阴影 文字阴影 盒子圆角 圆角基础使用 在 CSS3 中&#xff0c;新增了圆角边框样式&#xff0c;这样我们的盒子就可以变圆角了。 使用…

分类算法——朴素贝叶斯(四)

概率基础 1概率定义 概率定义为一件事情发生的可能性 扔出一个硬币&#xff0c;结果头像朝上 P(X)&#xff1a;取值在[0&#xff0c;1] 2女神是否喜欢计算案例 在讲这两个概率之前我们通过一个例子&#xff0c;来计算一些结果&#xff1a; 问题如下&#xff1a; 1、女神喜欢…

《设计模式之美》第二章 总结

《设计模式之美》总结 第二章 面向对象编程范式 2.1 当我们在谈论面向对象时&#xff0c;我们在谈什么 2.1.1 面向对象编程和面向对象编程语言 面向对象编程语言&#xff1a; 1. 以类或对象作为组织代码的基本单元&#xff0c;并将封装、继承、抽象、多态4个特性作为代码的…

设计模式之创建型模式详解

设计模式 创建型模式 创建型模式(Creational Pattern)对类的实例化过程进行了抽象&#xff0c;能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰&#xff0c;外界对于这些对象只需要知道它们共同的接口&#xff0c;而不清楚其具体的实现细节&#xff0…

2024年阿里云4核8G配置云服务器价格低性能高!

阿里云4核8G服务器租用优惠价格700元1年&#xff0c;配置为ECS通用算力型u1实例&#xff08;ecs.u1-c1m2.xlarge&#xff09;4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选&#xff0c;CPU采用Intel(R) Xeon(R) Platinum处理器&#xff0c;阿里云优惠 aliyunfuwuqi…

kafka实验部署

一、前期准备 二、kafka实验 在zookeeper后继续进行操作 2.1 为ndoe1、node2、node3作出部署 2.1.1 解压kafka压缩包&#xff08;node1举例&#xff09; 2.1.2 操作 将解压后的kafka移动到kafka&#xff0c;进入到kafka下的config中&#xff0c;复制文件 2.1.2.1 编辑server.pr…