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

news2024/11/15 1:25:32

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm064农产品仓库管理系统系统+jsp

农产品仓库管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本农产品仓库管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者…

Llama 3“智商”测试:英文提示表现亮眼,中文不完美但差强人意!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

C++从入门到精通——C++动态内存管理

C动态内存管理 前言一、C/C内存分布分类1分类2题目选择题sizeof 和 strlen 区别示例sizeofstrlen 二、C语言中动态内存管理方式malloc/calloc/realloc/free示例例题malloc/calloc/realloc的区别malloc的实现原理 三、C内存管理方式new/delete操作内置类型new和delete操作自定义…

zabbix自定义监控、自动发现和注册以及代理设置

前言 监控项的定制和新设备的注册往往需要大量手动操作&#xff0c;这会导致维护成本的增加和监控效率的降低。本文将介绍如何利用 Zabbix 的自定义功能&#xff0c;实现监控项的动态发布和新设备的自动注册以及代理设置、从而简化运维工作并实现更高效的监控管理。 Zabbix 监…

LearnOpenGL(二)之三角形

一、重要概念 顶点数组对象&#xff1a;Vertex Array Object&#xff0c;VAO顶点缓冲对象&#xff1a;Vertex Buffer Object&#xff0c;VBO元素缓冲对象&#xff1a;Element Buffer Object&#xff0c;EBO 或 索引缓冲对象 Index Buffer Object&#xff0c;IBO 以数组的形式…

【简单介绍下R-Tree】

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

单链表的实现(单链表的增删查改)

在顺序表中实现数据的增删的操作时&#xff0c;都要把操作位置之后的数据全部移动一遍&#xff0c;操作效率低下。其次是容量固定&#xff08;静态顺序表&#xff09;&#xff0c;虽然在动态顺序表中容量可变&#xff0c;但也会造成空间上的浪费。 单链表就完美解决了上述缺点…

.net8系列-02图文并茂手把手教你编写增删改查接口

前情提要 接上篇文章&#xff0c;我们的应用已经创建完毕了&#xff0c;接下来我们编写几个自己的接口 快速开始 新增Controller 复制一份WeatherForecastController.cs,改名为CommonInfoController 设置Class名 将CommonInfoController中的复制过来的class名改成新名 …

对称二叉树 - LeetCode 热题 39

大家好&#xff01;我是曾续缘&#x1f90e; 今天是《LeetCode 热题 100》系列 发车第 39 天 二叉树第 4 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#…

vue3图片展示实战

首先得有一个vue3项目 教程&#xff1a; vue3项目搭建 我测试存在两种方式可以将本地图片进行展示到页面 public文件夹下可以直接导入src下的asset文件夹下得图片需要在 script 标签内声明再导入网页图片不可导入&#xff0c;应该是存在一种限制 1&#xff0c;创建文件demo1I…

ICLR 2024 | FTS-Diffusion: 用于合成具有不规则和尺度不变模式的金融时间序列的生成框架

ICLR 2024 | FTS-Diffusion: 用于合成具有不规则和尺度不变模式的金融时间序列的生成框架 原创 QuantML QuantML 2024-04-17 09:53 上海 Content 本文提出了一个名为FTS-Diffusion的新颖生成框架&#xff0c;用于模拟金融时间序列中的不规则和尺度不变模式。这些模式由于其独…

桌面软件使用到的开源库

想了解一下桌面软件开发中可能使用到的dll库 联想锁屏 libcef-常用概念-框架特点-CSDN博客 libcurl库使用详情、libcurl库的制作-CSDN博客 使用Cef和Qt做一个跨平台的多标签多窗口浏览器_cef3 多个标签-CSDN博客 cef 依赖的文件 libcef - Bigben - 博客园 (cnblogs.com) Q…

Cadence virtuoso 原理图创建边框

给原理图创建如下图的边框Creating a Sheet Border and Title edit-sheet size添加画布 选择画布尺寸&#xff0c;border size里面的A/B/C/D对应不同的画布尺寸。 添加好画布后&#xff0c;在edit-sheet tilte里面可以选择输入相关信息&#xff0c;如电路名称&#xff0c;日期&…

STM32 F103C8T6经验笔记15:国产芯片下载受限问题

今日使用DAP仿真器下载时的小问题&#xff1a; Not a genuine sT Device! Abort connection 错误&#xff1a;不是真正的ST设备&#xff01;中止连接。 问题解释&#xff1a; 这是因为下载时软件给你检测出这不是他们的正版芯片了&#xff0c;然后我查看了一下之前的购买记…

项目管理-项目范围管理

目录 一、概述 二、范围计划的编制 2.1 项目中包含的范围 2.1.1 产品范围 2.1.2 工作范围 2.1.3 总结 2.2 范围计划编制的成果 2.2.1 范围管理计划 2.2.1.1 概述 2.2.1.2 内容 三、创建工作分解结构 3.1 概述 3.2 WBS目的和用途 3.3 WBS分层结构 3.3.1 分层结构图…

python爬虫-----深入了解 requests 库下篇(第二十五天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

[卷积神经网络]YoloV8

一、YoloV8 1.网络详解 ①backbone部分&#xff1a;第一次卷积的卷积核缩小(由3变为6)&#xff1b;CSP模块的预处理卷积从3次变为2次&#xff1b;借鉴了YoloV7的多分支堆叠结构&#xff08;Multi_Concat_Block&#xff09;。 所小第一次卷积的卷积核尺寸会损失部分感受野&#…

1.7jdk安装rpm的安装ssh加固

jk安装 [rootlocalhost ~]# java -version openjdk version "1.8.0_161"[rootlocalhost ~]# rpm -qa | grep java tzdata-java-2018c-1.el7.noarch python-javapackages-3.4.1-11.el7.noarch java-1.7.0-openjdk-1.7.0.171-2.6.13.2.el7.x86_64 java-1.8.0-openjdk-1…

会议文字记录工具【钉钉闪记】

当开会时&#xff0c;需要文字记录会议内容&#xff0c;但是打字又慢&#xff0c;可以使用钉钉闪记。 钉钉工作台直接搜索-钉钉闪记

【Pytorch】PytorchCPU版或GPU报错异常处理(10X~4090D)

Pytorch为CPU版或GPU使用报错异常处理 文章目录 Pytorch为CPU版或GPU使用报错异常处理0.检查阶段1. 在conda虚拟环境中安装了torch2.卸载cpuonly3.从tsinghua清华源安装不完善误为cpu版本4.用tsinghua清华源安装成cpu错误版本5.conda中torch/vision/cudatoolkit版本与本机cuda版…