PCL 移除点云边缘不连续的点

news2025/1/11 18:44:04

目录

一、概述

1.1原理

1.2实现步骤

1.3应用场景

二、代码实现

2.1关键函数

2.1.1 法向量计算

2.1.2 边界检测和移除

2.1.3 边界检测和移除

2.2完整代码

三、实现效果


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

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


一、概述

        在点云处理中,移除边缘不连续的点可以帮助提升数据的准确性,尤其是在3D物体的表面重建、特征提取等场景中。通过计算每个点的邻域信息,分析其与邻近点的距离或法向量变化,来识别并去除不连续的点。本文介绍了使用 PCL 中的BoundaryEstimation 类来估计点云中的边界点,并移除那些边缘不连续的点。

1.1原理

边缘不连续点的检测通过计算点与其邻域点的法向量变化或距离差异来判断。具体原理如下:

  1. 法向量估计:计算每个点的法向量及其邻域的法向量。
  2. 边界检测:根据法向量的变化或邻域点的距离,判断点是否为边界点。
  3. 滤波移除:通过设定边界点检测的阈值,移除那些不连续的边缘点。

1.2实现步骤

  1. 读取点云数据。
  2. 使用 NormalEstimation 计算每个点的法向量。
  3. 使用 BoundaryEstimation 估计边界点,判断哪些点是不连续的边缘点。
  4. 移除不连续的边缘点。
  5. 可视化原始点云和移除不连续边缘点后的点云。

1.3应用场景

  1. 表面重建:通过去除不连续的边缘点提升表面重建的精度。
  2. 特征提取:去除不连续边缘点,提取更稳定的几何特征。
  3. 噪声去除:移除位于边缘的离群点,增强数据的质量。

二、代码实现

2.1关键函数

2.1.1 法向量计算

首先通过 NormalEstimation 计算点云中每个点的法向量。

#include <pcl/features/normal_3d.h>

// 计算点云法向量
pcl::PointCloud<pcl::Normal>::Ptr computeNormals(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)  // 输入点云
{
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);

    ne.setInputCloud(cloud);
    ne.setSearchMethod(tree);
    ne.setKSearch(50);  // 设置搜索邻域的点数量
    ne.compute(*normals);  // 计算法向量

    return normals;
}

2.1.2 边界检测和移除

使用 BoundaryEstimation 检测点云中的边缘点,并移除那些不连续的点。

#include <pcl/features/boundary.h>

// 检测点云中的边缘点
pcl::PointCloud<pcl::Boundary>::Ptr detectBoundaries(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::Normal>::Ptr normals)  // 输入点云和法向量
{
    pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_est;
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>);

    boundary_est.setInputCloud(cloud);
    boundary_est.setInputNormals(normals);
    boundary_est.setSearchMethod(tree);
    boundary_est.setKSearch(50);  // 邻域点数
    boundary_est.setAngleThreshold(M_PI / 2);  // 设置角度阈值
    boundary_est.compute(*boundaries);  // 计算边缘点

    return boundaries;
}

// 移除不连续的边缘点
pcl::PointCloud<pcl::PointXYZ>::Ptr removeEdgePoints(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries)  // 输入点云和边界点
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // 遍历每个点,如果该点不是边缘点(boundary_point == 0),则保留
    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        if (boundaries->points[i].boundary_point == 0)  // 边缘点的 boundary_point 属性为 1
        {
            filtered_cloud->points.push_back(cloud->points[i]);
        }
    }
    return filtered_cloud;
}

2.1.3 边界检测和移除

使用 BoundaryEstimation 检测点云中的边缘点,并移除那些不连续的点。

#include <pcl/features/boundary.h>

// 检测点云中的边缘点
pcl::PointCloud<pcl::Boundary>::Ptr detectBoundaries(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::Normal>::Ptr normals)  // 输入点云和法向量
{
    pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_est;
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>);

    boundary_est.setInputCloud(cloud);
    boundary_est.setInputNormals(normals);
    boundary_est.setSearchMethod(tree);
    boundary_est.setKSearch(50);  // 邻域点数
    boundary_est.setAngleThreshold(M_PI / 2);  // 设置角度阈值
    boundary_est.compute(*boundaries);  // 计算边缘点

    return boundaries;
}

// 移除不连续的边缘点
pcl::PointCloud<pcl::PointXYZ>::Ptr removeEdgePoints(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries)  // 输入点云和边界点
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // 遍历每个点,如果该点不是边缘点(boundary_point == 0),则保留
    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        if (boundaries->points[i].boundary_point == 0)  // 边缘点的 boundary_point 属性为 1
        {
            filtered_cloud->points.push_back(cloud->points[i]);
        }
    }
    return filtered_cloud;
}

2.2完整代码

#include <iostream>
#include <pcl/io/pcd_io.h>  // 用于加载和保存PCD文件
#include <pcl/point_types.h>  // 定义点云数据类型
#include <pcl/kdtree/kdtree_flann.h>  // Kd树搜索方法
#include <pcl/features/normal_3d.h>  // 法向量计算
#include <pcl/features/boundary.h>  // 边缘点检测
#include <pcl/visualization/pcl_visualizer.h>  // 可视化库
#include <boost/thread/thread.hpp>  // 线程库

// 计算法向量
pcl::PointCloud<pcl::Normal>::Ptr computeNormals(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);

    // 设置输入点云
    ne.setInputCloud(cloud);
    // 使用 Kd 树进行邻域搜索
    ne.setSearchMethod(tree);
    // 设置邻域中的点数
    ne.setKSearch(50);
    // 计算法向量和曲率
    ne.compute(*normals);

    return normals;
}

// 检测边缘点
pcl::PointCloud<pcl::Boundary>::Ptr detectBoundaries(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::Normal>::Ptr normals)
{
    pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_est;
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>);

    // 设置输入点云和法向量
    boundary_est.setInputCloud(cloud);
    boundary_est.setInputNormals(normals);
    // 设置 Kd 树搜索方法
    boundary_est.setSearchMethod(tree);
    // 设置邻域点数
    boundary_est.setKSearch(50);
    // 设置角度阈值,用于边界检测
    boundary_est.setAngleThreshold(M_PI /8);
    // 计算边缘点
    boundary_est.compute(*boundaries);

    return boundaries;
}

// 移除边缘不连续的点
pcl::PointCloud<pcl::PointXYZ>::Ptr removeEdgePoints(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // 遍历每个点,如果该点不是边缘点(boundary_point == 0),则保留
    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        if (boundaries->points[i].boundary_point == 0)  // 边缘点的 boundary_point 属性为 1
        {
            filtered_cloud->points.push_back(cloud->points[i]);
        }
    }

    return filtered_cloud;
}

// 可视化函数
void visualizePointClouds(
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud)
{
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("Edge Removal Viewer"));

    // 创建视口1,显示原始点云
    int vp_1;
    viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp_1);
    viewer->setBackgroundColor(1.0, 1.0, 1.0, vp_1);  // 白色背景
    viewer->addText("Original PointCloud", 10, 10, "vp1_text", vp_1);
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color(cloud, 255, 0, 0);  // 原始点云红色
    viewer->addPointCloud(cloud, cloud_color, "original_cloud", vp_1);

    // 创建视口2,显示去除边缘点后的点云
    int vp_2;
    viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp_2);
    viewer->setBackgroundColor(0.98, 0.98, 0.98, vp_2);  // 浅灰色背景
    viewer->addText("Filtered PointCloud", 10, 10, "vp2_text", vp_2);
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> filtered_color(filtered_cloud, 0, 255, 0);  // 绿色
    viewer->addPointCloud(filtered_cloud, filtered_color, "filtered_cloud", vp_2);

    // 设置点的大小
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud", vp_1);
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "filtered_cloud", vp_2);

    while (!viewer->wasStopped())
    {
        viewer->spinOnce(100);
    }
}

int main(int argc, char** argv)
{
    // 读取点云数据
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("nosiy_bunny.pcd", *cloud) != 0)
    {
        return -1;
    }

    std::cout << "读取点的个数为: " << cloud->size() << " points" << std::endl;

    // 计算法向量
    pcl::PointCloud<pcl::Normal>::Ptr normals = computeNormals(cloud);

    // 检测边缘点
    pcl::PointCloud<pcl::Boundary>::Ptr boundaries = detectBoundaries(cloud, normals);

    // 移除边缘不连续的点
    pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud = removeEdgePoints(cloud, boundaries);

    // 可视化原始点云和移除边缘不连续点后的点云
    visualizePointClouds(cloud, filtered_cloud);

    return 0;
}

三、实现效果

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

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

相关文章

React学习笔记(4.0)

json-server实现数据Mock 1.项目中安装json-server npm i -D json-server 2.准备一个json文件 3.添加启动命令【package.json中配置】 "server":"json-server ./server/data.json --port 8888" 该命令中&#xff0c;路径就是自己创建的json文件路径&…

【C++】BitSet和Bloom_Filter

前言&#xff1a; 在计算机图形学中&#xff0c;位图&#xff08;Bitmap&#xff09;也称为光栅图&#xff0c;是由像素点组成的图像表示方式。在 C 编程中&#xff0c;位图可以通过特定的函数和数据结构来进行处理和操作。 BitMap 位图&#xff08;BitMap&#xff09;是一种数…

uniapp中uni.request的统一封装 (ts版)

文章目录 前言一、我们为什么要去封装&#xff1f;二、具体实现1.创建一个请求封装文件&#xff1a;2.封装 uni.request&#xff1a;3.如何去使用&#xff1f; 总结 前言 在uniapp中如何去更简洁高效的发送我们的请求&#xff0c;下面就介绍了uni.request()二次封装。 一、我们…

超强大的 Nginx 可视化管理工具

今天给大家介绍一款 Nginx 可视化管理界面&#xff0c;非常好用&#xff0c;小白也能立马上手。 nginx-proxy-manager 是一个反向代理管理系统&#xff0c;它基于 NGINX&#xff0c;具有漂亮干净的 Web UI。还可以获得受信任的 SSL 证书&#xff0c;并通过单独的配置、自定义和…

【计算机视觉】ch1-Introduction

相机模型与成像 1. 世界坐标系 (World Coordinate System) 世界坐标系是指物体在真实世界中的位置和方向的表示方式。在计算机视觉和图像处理领域&#xff0c;世界坐标系通常是一个全局坐标系统&#xff0c;描述了摄像机拍摄到的物体在实际三维空间中的位置。它是所有其他坐标…

【嵌入式开发】可编程4k蓝牙摄像头点击器

今天讲解的是一款可编程摄像头蓝牙点击器&#xff0c;支持离线文字识别 外观如下 B站有使用视频 链接: B站使用 下面是具体使用步骤&#xff08;主要根据B站视频使用流程总结&#xff09;&#xff0c;文末附源码 全是干货 执行main代码 代码运行后&#xff0c;检查摄像头…

【电机-概述及分类】

文章目录 第1章1-1 电机的定义1-2 电机的构成要素1-3 电机的分类1-3-1 直流电机1-3-1-1 永磁励磁型直流电机1-3-1-2 电磁铁励磁型直流电机 第1章 重新认识电机的体系 电机包括许多种类。换个角度来看&#xff0c;并没有完美的电机&#xff0c;某种电机具有所谓A的优点&#xf…

docker 搭建minimalist-web-notepad

亲测可用 服务器 ubuntu sudo mkdir -p /root/docker/note# 切换到rootsu -chmod -R 755 /root/docker/notedocker pull carlosa/minimalist-web-notepad# 容器端口80 映射到 服务器7001端口&#xff0c;记得开防火墙 docker run -d --name notepad --restart always -p 7001…

u盘格式化后数据能恢复吗?2024年Top4恢复神器来帮忙

在这个电脑和手机满天飞的时代&#xff0c;U盘是我们用来存东西和传文件的得力助手&#xff0c;特别重要。但是&#xff0c;有时候U盘可能会不小心被格式化了&#xff0c;里面的重要文件就不见了。那么&#xff0c;U盘格式化后的数据还能恢复吗&#xff1f;当然可以。今天会告诉…

揭秘一下平时我们下载的python库跑到哪里了呢???

&#xff08;阅读之前&#xff0c;祝福大家国庆假期快乐&#xff0c;以及真诚的祝福我们的祖国越来越强大&#xff09;在那天的课上&#xff0c;老师问了我们这样一个问题&#xff1a;你们知道你们平时pip install下载库&#xff0c;下载好了&#xff0c;你们的库是下载到哪里了…

智邦国际ERP系统存在SQL注入漏洞

漏洞描述 智邦国际ERP系统是一款全面的企业资源规划工具&#xff0c;其主要目标是协助组织实现业务流程自动化和提升效率。此系统具备多种功能模块&#xff0c;包括但不限于财务管理、采办管理、销售操作、库存控制和生产过程管理&#xff0c;都能够满足企业多元化的业务需求。…

Ubuntu Server 20.04 64bit定时备份MySQL8.0.36数据库数据

一、编写sh脚本 常见备份命令介绍 我选用的是mysqldump命令&#xff0c;命令使用简介 [root]> mysqldump -helpUsage: mysqldump [OPTIONS] database_name [tables] OR mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...] OR mysqldump [OPTIONS] --all…

朝天椒USB Server为赛力斯集团解决网银U盾难题

近期&#xff0c;赛力斯集团的财务部门引进了朝天椒USB Server&#xff0c;用几台USB服务器解决了集团困扰多年的银行U盾管理难题。 一、背景 赛力斯集团是一家以新能源汽车为核心业务的科技制造企业&#xff0c;A股上市公司&#xff0c;中国企业500强。赛力斯集团作为一家大型…

基于JAVA+SpringBoot+Vue的电商平台的设计与实现

基于JAVASpringBootVue的电商平台的设计与实现 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345…

如何利用 StarRocks 加速 Iceberg 数据湖的查询效率

数据湖作为一种存储各种类型数据的集中式存储系统&#xff0c;以其灵活性、可扩展性和低成本的优势受到越来越多企业的青睐。然而&#xff0c;数据湖虽然降低了数据存储成本&#xff0c;但在数据分析尤其是实时数据分析场景下&#xff0c;其性能仍存在一定瓶颈。 本文将探讨如何…

Linux shell编程学习笔记84:tee命令——显示保存两不误

0 引言 在前面的学习笔记中&#xff0c;我们经常使用echo命令和输出重定向来生成脚本文件或演示文件&#xff0c;其实Linux提供了一个可以从标准输入读取数据&#xff0c;并输出成文件的命令——tee。 1 tee命令 的帮助信息、功能、命令格式、选项和参数说明 1.1 tee命令 的…

攻防世界---->crazy

做题笔记&#xff1a; 草&#xff0c;不要怀疑自己。 下载 查壳。 64ida打开。 先运行一下程序&#xff1a; 先解决第一个问题 ——是不是输入的长度为32。 tips&#xff1a;这题加载了混淆函数等 ida 反正我跳不过去&#xff0c;pwndbg我用的不是很熟悉。 不过没关系 直接跑…

数据库基础03

文章目录 一、DCL语句1.1 管理用户1.2 权限控制 二、函数2.1 字符串函数2.2 数值函数2.3 日期函数3.4 流程函数 三、约束3.1 概述3.2 约束演示3.3 外键约束3.3.1 外键的基本语法3.3.2 删除/更新行为 一、DCL语句 DCL英文全称是Data Control Language(数据控制语言)&#xff0c;…

Wooey:将 Python 脚本转化为 Web 应用的简易解决方案

Wooey 是一个开源的 Python 项目&#xff0c;旨在帮助开发者快速将 Python 脚本转化为用户友好的 Web 应用程序。通过 Wooey&#xff0c;开发者无需编写复杂的前端代码即可轻松为 Python 脚本创建基于网页的图形用户界面&#xff08;GUI&#xff09;。这使得 Wooey 成为处理数据…

小阿轩yx-案例:项目发布基础

小阿轩yx-案例&#xff1a;项目发布基础 前言 随着软件开发需求及复杂度的不断提高&#xff0c;团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作&#xff0c;工具集…