【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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/560978.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JavaWeb】Day50.Mybatis的XML配置文件

XML配置文件规范 使用Mybatis的注解方式&#xff0c;主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能&#xff0c;建议使用XML来配置映射语句&#xff0c;也就是将SQL语句写在XML配置文件中。 在Mybatis中使用XML映射文件方式开发&#xff0c;需要符合一定的规…

Nginx解决跨域访问难题:轻松实现跨域资源共享!

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 跨域资源共享&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;是一种网络浏览器的安全功能&#xff0c;它限制了一个源&#xff08;域、协议和端口…

学习空间转换-3D转换

1.什么是空间转换&#xff1f; 使用的是transform属性实现元素在空间内的位移&#xff0c;旋转&#xff0c;缩放等效果。 空间&#xff1a;是从坐标轴角度定义的。x,y,z三条坐标轴构成的一个立体空间&#xff0c;Z轴位置与视线方向相同。 所以空间转换也被叫做3D转换 语法&a…

docker+awk=无敌?!

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 dockerawk无敌&#xff1f;&#xff01; 前言需求分析容器间通过容器名称访问脚本实现一键部署命令解释 前言 当今软件开发的世界充满了数据&#xff0c;而 Docker 则是许多开发者首选的容器化解决方…

嵌入式学习55-ARM4(ADC和I²C)

1、什么是ADC,模拟量和数字量有什么特点&#xff1f; ADC&#xff1a; …

GhostNetV3:探索紧凑型模型的训练策略学习笔记

代码地址&#xff08;coming soon&#xff09;&#xff1a;Efficient-AI-Backbones/ghostnetv3_pytorch at master huawei-noah/Efficient-AI-Backbones GitHub 论文地址&#xff1a;2404.11202v1.pdf (arxiv.org) 紧凑型神经网络是专门为边缘设备上的应用而设计的&#xff0…

linux离线安装mysql

一、下载mysql 地址&#xff1a;MySQL 这里选择64为还是32为要根据操作系统来 uname -m 二、上传解压配置mysql 使用root账户登录linux服务器&#xff0c;在opt文件下创建mysql文件夹 cd /opt sudo mkdir mysql 使用Xftp上传mysql压缩包到此文件夹下(自行决定路径) cd mysql/…

Unity Editor编辑器扩展之创建脚本

前言 既然你看到这篇文章了&#xff0c;你是否也有需要使用代码创建脚本的需求&#xff1f;使用编辑器扩展工具根据不同的表格或者新增的内容去创建你想要的脚本。如果不使用工具&#xff0c;那么你只能不断去修改某个脚本&#xff0c;这项工作既繁琐也浪费时间。这个时候作为程…

蓝桥杯第十五界软件测试线下省赛题目分析及解决

PS 需要第十五界蓝桥杯被测系统或者功能测试模板、单元测试被测代码、自动化测试被测代码请加&#x1f427;:1940787338 备注&#xff1a;15界蓝桥杯省赛软件测试 题目1&#xff1a;功能测试 题目描述 ​ 某物流公司的货运收费标准根据重量、距离和节假日三个因素来确定。如…

【介绍下LeetCode的使用方法】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

1.为什么选择Vue框架

参考&#xff1a;百战程序员 为什么选择Vue框架 Vue是什么&#xff1f; 渐进式 JavaScript 框架&#xff0c;易学易用&#xff0c;性能出色&#xff0c;适用场景丰富的 Web 前端框架 为什么要学习Vue Vue是目前前端最火的框架之一Vue是目前企业技术栈中要求的知识点Vue可以…

Lesson4--栈和队列

【本节目标】 1.栈 2.队列 3.栈和队列面试题 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO &…

vue快速入门(三十五)组件通信-父传子

注释很详细&#xff0c;直接上代码 上一篇 新增内容 父组件传值子组件接收父组件传来的数据 源码 App.vue <template><div id"app"><!-- :item"item"为将item的值传递给MyTest组件 --><MyTest v-for"item in roles" :key&q…

【双曲几何】圆盘上的三角形概念

目录 一、说明二、对偶三角形概念2.1 反演关系2.2 对偶关系2.3 找出三角形的对偶三角形 三、正交三角形概念3.1 通过对偶三角形&#xff0c;找到垂心3.2 正交三角形的概念3.3 中心射影点的概念 四、后记 一、说明 本文对双曲空间的三角形进行分析&#xff0c;本篇首先给出&am…

(vue)el-select选择框加全选/清空/反选

(vue)el-select选择框加全选/清空/反选 <el-form-item label"批次"><el-selectv-model"formInline.processBatch"multiplecollapse-tagsfilterableplaceholder"请选择"style"width: 250px"no-data-text"请先选择企业、日…

基于docker的开发者集成环境

docker-compose一键部署开发者环境。 常见的中间件&#xff1a;nginx, mysql, redis, mongo, rabbitmq, nacos, rocketmq, zookeeper等。 GIthub项目地址 1. 下载项目&#xff1a;git clone https://github.com/xhga/docker-develop-env.git 2. 进入文件夹&#xff1a;cd d…

实例分割——苹果数据集

一、重要性及意义 重要性&#xff1a; 提升农业生产效率&#xff1a;通过自动化检测和分割技术&#xff0c;可以快速准确地识别出图像中的苹果&#xff0c;进而实现自动化的采摘、计数和品质评估。这极大地提高了农业生产的效率&#xff0c;减少了人工劳动成本。 优化资源配置…

【网站项目】高校毕业论文管理系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

力扣:LCR 022. 环形链表 II

力扣&#xff1a;LCR 022. 环形链表 II 给定一个链表&#xff0c;返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;我们使用整数 pos 来表示链…

为了机器学习量化策略,我标注了两万条数据

题图&#xff1a;芝加哥大学海德公园。芝大是经济学重镇&#xff0c;其学者开创了著名的芝加哥经济学派&#xff0c;共产生了 100 位诺奖、10 位菲尔兹奖、4 位图灵奖。今天量化人追逐的 Alpha&#xff0c; 最早就来自于 Michael Jessen 在芝大时的博士论文。 很多人对基于机器…