PCL 提取点云边界

目录

一、概述

1.1原理

1.2实现步骤

1.3应用场景

二、代码实现

2.1关键函数

2.1.1 计算法向量

2.1.2 提取边界点

2.1.3 可视化边界点

2.2完整代码

三、实现效果


PCL点云算法汇总及实战案例汇总的目录地址链接:

PCL点云算法与项目实战案例汇总(长期更新)


一、概述

        点云边界提取是计算点云中具有显著几何变化点的一种方法,常用于点云的形状分析和特征提取。在点云数据中,边界点通常位于物体轮廓处,它们的几何信息可以帮助理解点云的轮廓结构。通过提取这些边界点,可以实现物体的轮廓特征增强和更准确的表面重建。

1.1原理

        点云边界提取的原理基于局部几何信息的变化,通过分析点云中某一点邻域内点的分布,可以判断该点是否位于边界。具体方法包括:

  1. 通过KD树找到每个点的局部邻域点。
  2. 基于这些邻域点计算法向量的变化或曲率变化。
  3. 若该点周围的法向量变化较大,则认为该点是边界点。

提取边界的公式如下:

1.2实现步骤

  1. 加载点云数据。
  2. 使用KD树查找点云的局部邻域。
  3. 基于邻域内法向量的变化提取边界点。
  4. 可视化原始点云和提取的边界点。

1.3应用场景

  1. 边界特征增强:在物体重建或识别过程中,提取边界点有助于增强物体的轮廓特征。
  2. 形状分析:边界点提取可以用于点云的轮廓形状分析,尤其是在复杂场景中。
  3. 物体识别:在3D物体识别中,边界点作为特征点可以帮助提高识别的准确性。

二、代码实现

2.1关键函数

2.1.1 计算法向量

该函数使用 pcl::NormalEstimation 来计算每个点的法向量,并将计算结果存储在 normals 点云中。

void computeNormals(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals)
{// 创建法向量估计对象pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;ne.setInputCloud(cloud);  // 输入原始点云// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());ne.setSearchMethod(tree);// 设置 K 近邻搜索中的邻域点数量ne.setKSearch(50);  // 计算法向量ne.compute(*normals);
}

2.1.2 提取边界点

该函数使用 pcl::BoundaryEstimation 来提取点云中的边界点。

void extractBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建边界估计对象pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> be;// 设置输入的原始点云和法向量be.setInputCloud(cloud);be.setInputNormals(normals);// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());be.setSearchMethod(tree);// 设置邻域搜索的半径be.setRadiusSearch(0.02);// 设置法向量角度阈值,大于此角度则认为是边界点be.setAngleThreshold(M_PI / 2);// 执行边界点提取操作be.compute(*boundaries);
}

2.1.3 可视化边界点

该函数负责将原始点云和提取的边界点进行可视化展示,原始点云显示为红色,边界点显示为绿色。

void visualizeBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建一个新的点云用于存储边界点pcl::PointCloud<pcl::PointXYZ>::Ptr boundary_points(new pcl::PointCloud<pcl::PointXYZ>);// 遍历边界估计结果,将边界点存储到新的点云中for (size_t i = 0; i < boundaries->points.size(); ++i){if (boundaries->points[i].boundary_point)  // 如果是边界点,则存储该点{boundary_points->points.push_back(cloud->points[i]);}}// 创建可视化窗口boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("Boundary Viewer"));int vp1, vp2;// 创建视口 1,显示原始点云viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp1);viewer->setBackgroundColor(1.0, 1.0, 1.0, vp1);  // 设置背景为白色viewer->addText("Original Point Cloud", 10, 10, "vp1_text", vp1);// 原始点云设置为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler(cloud, 255, 0, 0);viewer->addPointCloud(cloud, cloud_color_handler, "original_cloud", vp1);// 创建视口 2,显示边界点viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp2);viewer->setBackgroundColor(0.98, 0.98, 0.98, vp2);  // 设置背景为灰色viewer->addText("Boundary Points", 10, 10, "vp2_text", vp2);// 边界点设置为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> boundary_color_handler(boundary_points, 0, 255, 0);viewer->addPointCloud(boundary_points, boundary_color_handler, "boundary_cloud", vp2);// 开始可视化显示viewer->spin();
}

2.2完整代码

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/boundary.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>// 计算法向量
void computeNormals(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals)
{// 创建法向量估计对象pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;ne.setInputCloud(cloud);  // 输入原始点云// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());ne.setSearchMethod(tree);// 设置 K 近邻搜索中的邻域点数量ne.setKSearch(50);// 计算法向量ne.compute(*normals);
}// 提取边界点
void extractBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建边界估计对象pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> be;// 设置输入的原始点云和法向量be.setInputCloud(cloud);be.setInputNormals(normals);// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());be.setSearchMethod(tree);// 设置邻域搜索的半径be.setRadiusSearch(0.01);// 设置法向量角度阈值,大于此角度则认为是边界点be.setAngleThreshold(M_PI / 15);// 执行边界点提取操作be.compute(*boundaries);
}// 可视化边界点
void visualizeBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建一个新的点云用于存储边界点pcl::PointCloud<pcl::PointXYZ>::Ptr boundary_points(new pcl::PointCloud<pcl::PointXYZ>);// 遍历边界估计结果,将边界点存储到新的点云中for (size_t i = 0; i < boundaries->points.size(); ++i){if (boundaries->points[i].boundary_point)  // 如果是边界点,则存储该点{boundary_points->points.push_back(cloud->points[i]);}}// 创建可视化窗口boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("Boundary Viewer"));int vp1, vp2;// 创建视口 1,显示原始点云viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp1);viewer->setBackgroundColor(1.0, 1.0, 1.0, vp1);  // 设置背景为白色viewer->addText("Original Point Cloud", 10, 10, "vp1_text", vp1);// 原始点云设置为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler(cloud, 255, 0, 0);viewer->addPointCloud(cloud, cloud_color_handler, "original_cloud", vp1);// 创建视口 2,显示边界点viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp2);viewer->setBackgroundColor(0.98, 0.98, 0.98, vp2);  // 设置背景为灰色viewer->addText("Boundary Points", 10, 10, "vp2_text", vp2);// 边界点设置为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> boundary_color_handler(boundary_points, 0, 255, 0);viewer->addPointCloud(boundary_points, boundary_color_handler, "boundary_cloud", vp2);// 开始可视化显示viewer->spin();
}// 主函数
int main(int argc, char** argv)
{// 读取点云数据pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());pcl::io::loadPCDFile("bunny.pcd", *cloud);// 计算法向量pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);computeNormals(cloud, normals);// 提取边界点pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>);extractBoundaries(cloud, normals, boundaries);// 可视化结果visualizeBoundaries(cloud, boundaries);return 0;
}

三、实现效果

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动手学深度学习(李沐)PyTorch 第 6 章 卷积神经网络

李宏毅-卷积神经网络CNN 如果使用全连接层&#xff1a;第一层的weight就有3*10^7个 观察 1&#xff1a;检测模式不需要整张图像 很多重要的pattern只要看小范围即可 简化1&#xff1a;感受野 根据观察1 可以做第1个简化&#xff0c;卷积神经网络会设定一个区域&#xff0c…

SolarWinds中如何添加华为交换机实现网络管理

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 下午好&#xff0c;我的网工朋友。 SolarWinds作为一款广受好评的网络管理软件&#xff0c;它提供了全面的网络配置、监控和管理解决方案&#x…

组织病理学图像中的再识别|文献速递--基于多模态-半监督深度学习的病理学诊断与病灶分割

Title 题目 Re-identification from histopathology images 组织病理学图像中的再识别 01 文献速递介绍 在光学显微镜下评估苏木精-伊红&#xff08;H&E&#xff09;染色切片是肿瘤病理诊断中的标准程序。随着全片扫描仪的出现&#xff0c;玻片切片可以被数字化为所谓…

【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象

文章目录 请求1. 传递单个参数注意事项1 . 正常传递参数2 . 不传递 age 参数3 . 传递参数类型不匹配 2. 传递多个参数3. 传递对象 请求 访问不同的路径&#xff0c;就是发送不同的请求。在发送请求时&#xff0c;可能会带一些参数&#xff0c;所以学习 Spring 的请求&#xff…

【093】基于SpringBoot+Vue实现的精品水果线上销售系统

系统介绍 视频演示 基于SpringBootVue实现的精品水果线上销售系统&#xff08;有文档&#xff09; 基于SpringBootVue实现的精品水果线上销售系统采用前后端分离的架构方式&#xff0c;系统设计了管理员、商家、用户三种角色&#xff0c;实现了公告类型管理、商家信誉类型管理…

自由学习记录

约束的泛型通配符? Java中的泛型 xiaomi和byd都继承了car&#xff0c;但是只是这两个类是car的子类而已&#xff0c;而arraylist<xiaomi> ,arraylist<byd> 两个没有半毛钱继承关系 所以传入的参数整体&#xff0c;是car的list变形&#xff0c;里面的确都能存car…

SDK4(note下)

以下代码涉及到了很多消息的处理&#xff0c;有些部分注释掉了&#xff0c;主要看代码 #include <windows.h> #include<tchar.h> #include <stdio.h> #include <strsafe.h> #include <string> #define IDM_OPEN 102 /*鼠标消息 * 键盘消息 * On…

数据湖数据仓库数据集市数据清理以及DataOps

一提到大数据我们就知道是海量数据&#xff0c;但是我们并不了解需要从哪些维度去考虑这些数据的存储。比如 数据湖、数据仓库、数据集市&#xff0c;以及数据自动化应用DataOps有哪些实现方式和实际应用&#xff0c;这篇文章将浅显的做一次介绍。 数据湖 数据湖是一种以自然…

SimpleFoc以及SVPWM学习补充记录

SimpleFoc SimpleFOC移植STM32&#xff08;一&#xff09;—— 简介 FOC控制的过程是这样的&#xff1a; 对电机三相电流进行采样得到 Ia,Ib,Ic。将 Ia,Ib,Ic 经过Clark变换得到 I_alpha I_beta。将 I_alpha I_beta 经过Park变换得到 Id,Iq。计算 Id,Iq 和其设定值 Id_ref 和…

网络知识点之—EVPN

EVPN&#xff08;Ethernet Virtual Private Network&#xff09;是下一代全业务承载的VPN解决方案。EVPN统一了各种VPN业务的控制面&#xff0c;利用BGP扩展协议来传递二层或三层的可达性信息&#xff0c;实现了转发面和控制面的分离。 EVPN解决传统L2VPN的无法实现负载分担、…

《神经网络》—— 长短期记忆网络(Long Short-Term Memory,LSTM)

文章目录 一、LSTM的简单介绍二、 LSTM的核心组件三、 LSTM的优势四、 应用场景 一、LSTM的简单介绍 传统RNN循环神经网络的局限&#xff1a; 示例&#xff1a;当出现“我的职业是程序员。。。。。。我最擅长的是电脑”。当需要预测最后的词“电脑”。当前的信息建议下一个词可…

[Python] 使用Python自定义生成二维码

文章目录 目录 安装 qrcode 库生成简单的二维码代码讲解 生成自定义样式的二维码代码讲解 生成带有链接的二维码代码讲解 Demo代码实现代码讲解 总结 收录专栏: [Python] 二维码是现在非常常用的一种信息存储和传递方式&#xff0c;我们可以通过扫描二维码来快速获取文本、链接…

如何在测试中模拟请求和响应?

在日常开发中&#xff0c;除了在服务器端进行单元测试之外&#xff0c;还经常需要做集成测试&#xff0c;为了能更好地做一些边界测试&#xff0c;我们常常需要mock一些HTTP请求或者响应&#xff0c;今天我们就来聊聊几种常见的方式。 服务器端设置 在开发中&#xff0c;我们…

车辆路径规划问题(VRP)优化方案

车辆路径规划问题&#xff08;VRP&#xff09;优化方案 车辆路径规划问题&#xff08;Vehicle Routing Problem, VRP&#xff09;是物流领域中一个经典的组合优化问题&#xff0c;目标是在满足客户需求的情况下&#xff0c;找到一组车辆的最优配送路径&#xff0c;以最小化总的…

C/C++复习(一)

1.sizeof 关于sizeof我们是经常使用的&#xff0c;所以使用方法就不需要提及了&#xff0c;这里我们需要注意的是&#xff0c;sizeof 后面如果是表达式可以不用括号&#xff0c;并且sizeof实际上不参与运算&#xff0c;返回的是内容的类型大小&#xff08;size_t类型&#xff0…

CDN绕过学习

1.什么是CDN&#xff1f; CDN就是分布在各个地区的服务器&#xff0c;这些服务器储存着数据的副本。 哪些服务器比较接近你&#xff0c;当你发起请求时&#xff0c;提前就会快速为你提供服务。 总结来说就是&#xff1a; 其实就是用来加速访问的&#xff0c;以及缓解压力&a…

提示工程、微调和 RAG

自众多大型语言模型&#xff08;LLM&#xff09;和高级对话模型发布以来&#xff0c;人们已经运用了各种技术来从这些 AI 系统中提取所需的输出。其中一些方法会改变模型的行为来更好地贴近我们的期望&#xff0c;而另一些方法则侧重于增强我们查询 LLM 的方式&#xff0c;以提…

1. Keepalived概念和作用

1.keepalived概念 (1)解决单点故障(组件免费) (2)可以实现高可用HA机制 (3)基于VRR协议(虚拟路由沉余协议) 2.keepalived双机主备原理

一入递归深似海,算法之美无止境

最近在刷leetcode hot100,在写二叉树中最大路径和的时候,看到了一个佬对递归的理解,深受启发,感觉自己对于递归的题又行了!!! 这里给大家分享一下(建立大家先去尝试一下这道题再来看 124. 二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每…

什么是PLM系统?PLM系统对制造业起到哪些作用?三品PLM系统对汽车制造业意义

在当今竞争激烈的制造业环境中&#xff0c;企业面临着来自市场、技术、客户需求等多方面的挑战。为了应对这些挑战&#xff0c;许多制造企业纷纷引入产品生命周期管理PLM系统&#xff0c;以实现更高效、更灵活的产品全生命周期管理。PLM系统以其独特的优势&#xff0c;在优化产…