PCL KD树的使用

news2024/11/15 10:49:20

目录

一、概述

1.1原理

1.1.1 数据拆分过程

1.1.2 树的构建示例

1.2实现步骤

1.3应用场景

二、代码实现

2.1关键函数

2.1.1KD树构建与查询:

2.1.2 k近邻搜索

 2.1.3半径搜索

2.2完整代码

三、实现效果

3.1处理后点云

3.2数据显示


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

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


一、概述

        KD树(K-D Tree)是一种用于组织k维空间中的点的数据结构,常用于高效的最近邻搜索、范围查询等操作。KD树通过递归地将空间划分成k维的超矩形,使得在高维空间中的搜索变得更加高效。本文将详细介绍KD树的构建原理及其在PCL中的应用。

1.1原理

        KD树的全称是k维树(k-Dimensional Tree),它是一种二叉树,用于对k维空间中的点进行组织,以便于快速地进行点的查询操作(如最近邻搜索)。KD树的基本思想是通过递归地将数据空间划分为两个部分,从而形成一棵二叉树。每个节点代表一个k维超矩形,划分过程直到节点包含的数据点数目低于某个阈值或不能再划分为止。

1.1.1 数据拆分过程

1.选择维度:
    - 在构建KD树时,选择一个维度来划分数据空间。通常,使用循环选择的方法,即在每一层递归中,依次选择数据的不同维度。例如,在三维空间(k=3)中,第一层选择x维度,第二层选择y维度,第三层选择z维度,第四层再回到x维度,以此类推。
2.选择分割点:
    - 在选定的维度上,通过找到数据在该维度上的中位数,将数据空间划分为两个部分。中位数的选择保证了数据点被尽量均匀地分割开。
3.递归构建:
    - 将数据空间划分后,左子树包含小于或等于中位数的数据点,右子树包含大于中位数的数据点。递归地对每个子空间构建KD树,直到满足终止条件(如节点数据点数目小于某个阈值)。

1.1.2 树的构建示例

假设我们有一组二维点 (x, y),要将其构建为一棵KD树:

  • 第一层(根节点):选择x维度进行划分,找到x维度的中位数,作为根节点的分割点。左子树包含所有x值小于或等于中位数的点,右子树包含所有x值大于中位数的点。
  • 第二层:对左子树和右子树,选择y维度进行划分,找到y维度的中位数,分别作为左右子树的分割点。继续递归划分。
  • 第三层:再回到x维度进行划分,依次类推,直到不能再划分为止。

1.2实现步骤

        KD树的搜索过程通常是以递归的方式进行的,主要分为最近邻搜索和范围搜索两种常见的操作。
1.最近邻搜索:
    - 从根节点开始,沿树向下递归搜索,比较查询点与节点的分割点,决定搜索左子树还是右子树。
    - 在找到一个叶节点后,记录下该叶节点的点,并计算它与查询点的距离。
    - 回溯检查其他可能包含更近点的子树。
2.范围搜索:
    - 同样从根节点开始,根据分割维度的值,判断是否需要搜索左右子树。
    - 判断当前节点的分割点是否在查询范围内,如果是,则将其纳入结果集。
    - 继续递归地检查子树,直到所有符合条件的节点都被搜索到。
通过这种分割和搜索方法,KD树能够有效地减少高维空间中的搜索范围,从而加快查询速度。

1.3应用场景

KD树广泛应用于以下场景:

  1. 最近邻搜索:用于查找点云中距离某点最近的邻居点,常用于点云配准。
  2. k近邻搜索:用于查找点云中某点的k个最近邻点,适用于点云平滑、特征计算等。
  3. 范围搜索:用于查找在一定范围内的所有邻居点,适用于聚类、边界检测等。

二、代码实现

2.1关键函数

2.1.1KD树构建与查询:

  • pcl::KdTreeFLANN:用于创建KD树对象,支持最近邻、k近邻、范围搜索。
  • setInputCloud:将点云数据输入到KD树中进行构建。
  • nearestKSearch:执行k近邻搜索,找到指定点的k个最近邻点。
  • radiusSearch:执行范围搜索,找到指定点在给定半径内的所有邻居点。

2.1.2 k近邻搜索

int pcl::KdTreeFLANN< PointT, Dist >::nearestKSearch  ( const PointT &  point,  
  unsigned int  k,  
  Indices &  k_indices,  
  std::vector< float > &  k_sqr_distances  
 )  const 

 2.1.3半径搜索

int pcl::KdTreeFLANN< PointT, Dist >::radiusSearch  ( const PointT &  point,  
  double  radius,  
  Indices &  k_indices,  
  std::vector< float > &  k_sqr_distances,  
  unsigned int  max_nn = 0  
 )  const 

2.2完整代码

#include <pcl/io/pcd_io.h>  // 用于加载PCD文件
#include <pcl/point_types.h>  // PCL点类型定义
#include <pcl/kdtree/kdtree_flann.h>  // PCL中的KD树实现
#include <pcl/visualization/pcl_visualizer.h>  // 用于可视化点云
#include <vector>
#include <iostream>

// 将搜索到的结果渲染为红色,并在可视化窗口中显示
void visualizeResult(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, const std::vector<int>& indices)
{
    // 创建一个带颜色信息的点云对象,用于存储带有颜色信息的点云
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr coloredCloud(new pcl::PointCloud<pcl::PointXYZRGB>);

    // 遍历原始点云中的每个点
    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        // 创建一个带有RGB颜色信息的点对象
        pcl::PointXYZRGB point;
        point.x = cloud->points[i].x;  // 复制点的X坐标
        point.y = cloud->points[i].y;  // 复制点的Y坐标
        point.z = cloud->points[i].z;  // 复制点的Z坐标

        // 判断当前点是否在搜索到的索引列表中
        if (std::find(indices.begin(), indices.end(), i) != indices.end())
        {
            // 如果是搜索到的点,将颜色设置为红色
            point.r = 255;
            point.g = 0;
            point.b = 0;
        }
        else
        {
            // 如果不是搜索到的点,将颜色设置为白色
            point.r = 255;
            point.g = 255;
            point.b = 255;
        }

        // 将带颜色的点添加到新的点云对象中
        coloredCloud->points.push_back(point);
    }

    // 设置点云的基本属性
    coloredCloud->width = coloredCloud->points.size();  // 设置点云宽度为点的数量
    coloredCloud->height = 1;  // 设置点云高度为1(无序点云)
    coloredCloud->is_dense = true;  // 设置点云为稠密点云

    // 创建一个PCLVisualizer对象,用于显示带颜色的点云
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    viewer->setBackgroundColor(0, 0, 0);  // 设置背景颜色为黑色
    viewer->addPointCloud<pcl::PointXYZRGB>(coloredCloud, "result");  // 将带颜色的点云添加到可视化窗口
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "result");  // 设置点的大小

    // 启动可视化主循环,保持窗口打开直到用户关闭
    while (!viewer->wasStopped())
    {
        viewer->spinOnce(100);  // 刷新窗口,间隔100ms
    }
}

int main(int argc, char** argv)
{
    // 1. 创建PointCloud对象,用于存储点云数据
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    // 2. 读取点云数据,加载PCD文件
    if (pcl::io::loadPCDFile("bunny.pcd", *cloud) == -1)
    {
        PCL_ERROR("Couldn't read file input.pcd \n");  // 如果文件加载失败,输出错误信息
        return -1;  // 返回错误代码并退出程序
    }

    // 3. 创建KD树对象,并将点云数据输入到KD树中进行构建
    pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;  // 创建KD树对象
    kdtree.setInputCloud(cloud);  // 将点云数据设置为KD树的输入

    // 4. 定义一个查询点,用于进行搜索
    pcl::PointXYZ searchPoint(-0.0208042, 0.125974, 0.0209606);  // 查询点的坐标

    // 5. k近邻搜索(K Nearest Neighbor Search)
    int k = 1000;  // 设置k值,表示要查找的最近邻点的数量
    std::vector<int> pointIdxNKNSearch(k);  // 用于存储最近邻点的索引
    std::vector<float> pointNKNSquaredDistance(k);  // 用于存储最近邻点的距离平方值

    std::cout << "K nearest neighbor search at (" << searchPoint.x
        << " " << searchPoint.y
        << " " << searchPoint.z << ") with K=" << k << std::endl;

    // 使用KD树进行k近邻搜索,找到距离查询点最近的k个点
    if (kdtree.nearestKSearch(searchPoint, k, pointIdxNKNSearch, pointNKNSquaredDistance) > 0)
    {
        // 如果搜索成功,将搜索结果进行可视化
        visualizeResult(cloud, pointIdxNKNSearch);
    }

    // 6. 半径搜索(Radius Search)
    float radius = 0.03;  // 设置搜索半径
    std::vector<int> pointIdxRadiusSearch;  // 用于存储在指定半径范围内的点的索引
    std::vector<float> pointRadiusSquaredDistance;  // 用于存储在指定半径范围内的点的距离平方值

    std::cout << "Neighbors within radius " << radius << " around point ("
        << searchPoint.x << " " << searchPoint.y << " " << searchPoint.z << ")" << std::endl;

    // 使用KD树进行半径搜索,找到查询点在给定半径范围内的所有点
    if (kdtree.radiusSearch(searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0)
    {
        // 如果搜索成功,将搜索结果进行可视化
        visualizeResult(cloud, pointIdxRadiusSearch);
    }

    // 7. 混合搜索(k近邻搜索 + 半径搜索)
    std::vector<int> hybridSearchIndices;  // 存储混合搜索结果的索引
    for (size_t i = 0; i < pointIdxNKNSearch.size(); ++i)
    {
        // 判断k近邻搜索的点是否也在指定半径范围内
        if (pointNKNSquaredDistance[i] <= radius * radius)
        {
            hybridSearchIndices.push_back(pointIdxNKNSearch[i]);  // 如果满足条件,将索引添加到结果中
        }
    }

    std::cout << "Hybrid search results within K=" << k << " and radius " << radius << std::endl;

    // 如果混合搜索结果不为空,将结果进行可视化
    if (!hybridSearchIndices.empty())
    {
        visualizeResult(cloud, hybridSearchIndices);
    }

    return 0;
}

三、实现效果

3.1处理后点云

3.2数据显示

K nearest neighbor search at (-0.0208042 0.125974 0.0209606) with K=1000
Neighbors within radius 0.03 around point (-0.0208042 0.125974 0.0209606)
Hybrid search results within K=1000 and radius 0.03

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

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

相关文章

Jboss Administration Console弱⼝令

漏洞描述 Administration Console管理⻚⾯存在弱⼝令&#xff0c;admin:admin&#xff0c;登陆后台上传war包 , getshell 影响版本 全版本 环境搭建 因为这⾥⽤的环境是CVE-2017-12149的靶机 cd vulhub-master/jboss/CVE-2017-12149 docker-compose up -d 密码⽂件 /j…

【SA8155P】AIS Camera相关内容的简单介绍

高通车载相机模块(AIS,Automotive lmage System)是专门针对车载系统特性而设计的一套车载视觉架构,可用于AVM、RVC、DMS等常见车载视频应用开发。车载Camera系统的图像大部分是给自动驾驶等使用,更多考虑的是远距离传输、多摄像头图像处理等场景。 本文仅对AIS Camera相关…

MySQL的登陆错误:ERROR 1049 (42000): Unknown database ‘root‘

MySQL的登陆错误&#xff1a;ERROR 1049 (42000): Unknown database ‘root’ 安装MySQL的时候&#xff0c;到网上查的命令行登陆MySQL的方法都是mysql -u root -p password mysql -r root -p 123456但是奇怪的是这条命令我输进去死活都不对&#xff0c;它都会要求再输入一遍…

21、Tomato

难度 低(个人认为中) 目标 root权限 一个flag 使用VMware启动 kali 192.168.152.56 靶机 192.168.152.66 信息收集 端口信息收集 可以看到有个ftp服务&#xff0c;2211实际是ssh协议端口&#xff0c;80、8888是一个web服务 web测试 80端口显示一个tomato 查看源码给了一些…

从0开始学ARM

1. ARM模式和寄存器 1.1 ARM处理器工作模式 Cortex系列之前的ARM处理器工作模式一共有7种。 1.1.1 工作模式 Cortex系列的ARM处理器工作模式有8种&#xff0c;多了1个monitor模式&#xff0c;如下图所示&#xff1a; ARM之所以设计出这么多种模式出来&#xff0c;就是为了…

三菱变频器RS-485 端子的接线和构成

RS-485 端子排列 RS-485 端子接线方法 RS-485 的计算机1台、变频器1台时 RS-485 的计算机1台、变频器n台(多台)时 通讯运行的初始设定 1、为使变频器和计算机进行 RS-485 通讯&#xff0c;进行必要的设定。 2、通讯分为使用变频器的PU接口的通讯和使用RS-485端子的通讯。 3、…

搜索引擎onesearch3实现解释和升级到Elasticsearch v8系列(四)-搜索

搜索 搜索内容比较多&#xff0c;onesearch分成两部分&#xff0c;第一部分&#xff0c;Query构建&#xff0c;其中包括搜索词设置&#xff0c;设置返回字段&#xff0c;filter&#xff0c;高亮&#xff1b;第二部分分页和排序。第一部分是映射引擎负责&#xff0c;映射通用表…

SAP B1 流程实操 - 营销单据销售部分(下)

背景 在 SAP B1 中&#xff0c;最重要的模块就是【销售】&#xff0c;企业可能不涉及生产和库存&#xff08;贸易公司&#xff09;&#xff0c;甚至不涉及采购&#xff08;服务业&#xff09;&#xff0c;但是一定会有基本的 销售。本文中我们讲解 销售 模块的基本核心&#x…

2024年最新苹果cms升级插件【泛目录专用】

苹果CMS是一款专为视频内容管理而设计的系统&#xff0c;近年来在视频站点搭建中逐渐成为热门选择。其直观的用户界面和灵活的管理功能&#xff0c;使得无论是新手还是专业开发者都能轻松上手。 苹果CMS提供了多种主题和模板&#xff0c;用户可以根据自身需求进行定制&#xf…

Python画笔案例-054 绘制流光溢彩动画

1、绘制流光溢彩动画 通过 python 的turtle 库绘制 流光溢彩动画&#xff0c;如下图&#xff1a; 2、实现代码 绘制流光溢彩动画&#xff0c;以下为实现代码&#xff1a; """本程序实现流光溢彩的动画效果 """ from turtle import * from color…

from tqdm.auto import tqdm用法详细介绍

tqdm 是一个 Python 库&#xff0c;用于在长时间运行的任务中显示进度条。tqdm.auto 是 tqdm 的一个版本&#xff0c;能够自动适配输出环境&#xff08;如 Jupyter Notebook、命令行等&#xff09;&#xff0c;以确保进度条在各种环境下显示正确。下面是 tqdm.auto 的详细用法介…

【刷题】杨辉三角

目录 杨辉三角题目描述解题思路解题代码 相同的树题目描述解题思路 二叉树的层序遍历题目描述解题思路解题代码从底层层序遍历 二叉树的最近公共祖先题目描述解题思路 从前序与中序遍历序列构建二叉树题目描述解题思路 从后序与中序遍历序列构建二叉树题目描述解题思路 根据二叉…

SAP-MM-变式的设置

1、报表变式 业务需求: 业务人员查询报表时有些值是需要经常输入的,能不能设置成默认值?能不能设置成每次进入报表不选择变式直接是默认值? 解决措施: 1、事物码:MB51 以MB51物料凭证查询为例,其他报表自行举一反三 2、设置变式 首先进入MB51入下图 上图是没有选…

任天堂发言人:不会透露起诉《幻兽帕鲁》开发商细节

任天堂在昨天突然宣布将起诉《幻兽帕鲁》开发商 Pocketpair&#xff0c;除了引起许多玩家不满外&#xff0c;更多的是所有人的疑惑&#xff1a;因为实际上大家内心里觉得任天堂出手是理所当然的&#xff0c;但是为什么是在游戏发布数月后才选择起诉&#xff1f;此次诉讼是“专利…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)2.1-2.2

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第二周 深度卷积网络&#xff1a;实例探究&#xff08;Deep convolutional models: case studies&#xff09;2.1 为什么要进行实例探究&#xff1f;&#xff08;Why look at case studies?&…

Python编码系列—Python外观模式:简化复杂系统的快捷方式

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

生信初学者教程(八):数据收集

文章目录 数据分布表达谱数据最终数据分布自动下载GSE14520下载GSE149614下载其它数据在确定研究疾病为肝细胞癌**(Liver Hepatocellular Carcinoma: HCC)**后,系统地进行了文献回顾,专注于搜索与HCC相关的荟萃分析文章,以获取该领域的研究动态和已有成果。为了支持的研究…

卡车配置一键启动无钥匙进入手机控车

‌ 卡车智能一键启动无钥匙进入手机控车&#xff0c;通过手机应用程序与汽车内置硬件、软件的无线通信&#xff0c;实现对汽车的远程控制‌。 卡车改装一键启动的步骤包括安装门把手的感应装置、拆卸仪表台和门板&#xff0c;取出内部的待接线束&#xff0c;并将一键启动…

MySQL高阶1875-将工资相同的雇员分组

目录 题目 准备数据 分析数据 题目 编写一个解决方案来获取每一个被分配到组中的雇员的 team_id 。 返回的结果表按照 team_id 升序排列。如果相同&#xff0c;则按照 employee_id 升序排列。 这家公司想要将 工资相同 的雇员划分到同一个组中。每个组需要满足如下要求&a…

springboot结合p6spy进行SQL监控

1.学习p6spy的相关链接 英文文档&#xff1a;Integrating P6Spy — p6spy 3.9.2-SNAPSHOT documentationhttps://p6spy.readthedocs.io/en/latest/integration.html github链接&#xff1a;GitHub - p6spy/p6spy: P6Spy is a framework that enables database data to be sea…