目录
一、kd-tree 简介
pcl_kdtree库使用FLANN提供了kd-tree数据结构,它允许快速的最近邻搜索。
k- tree (k维树)是一种空间分区数据结构,它在树结构中存储一组k维点,支持有效的范围搜索和最近邻搜索。最近邻搜索是处理点云数据时的核心操作,可用于查找点组或特征描述符之间的对应关系,或定义一个或多个点周围的局部邻域。
通过雷达,激光扫描,立体摄像机等三维测量设备获取的点云数据,具有数据量大,分布不均匀等特点,作为三维领域中一个重要的数据来源,点云主要是表征目标表面的海量点的集合,并不具备传统网格数据的几何拓扑信息,所以点云数据处理中最为核心的问题就是建立离散点间的拓扑关系,实现基于邻域关系的快速查找。
k-d树 (k-dimensional树的简称),是一种分割k维数据空间的数据结构。主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)。K-D树是二进制空间分割树的特殊的情况。用来组织表示K维空间中点的几何,是一种带有其他约束的二分查找树,为了达到目的,通常只在三个维度中进行处理因此所有的kd_tree都将是三维的kd_tree,kd_tree的每一维在指定维度上分开所有的字节点,在树 的根部所有子节点是以第一个指定的维度上被分开。
k-d树算法可以分为两大部分,一部分是有关k-d树本身这种数据结构建立的算法,另一部分是在建立的k-d树上如何进行最邻近查找的算法。
二、kd-tree的构建
k-d树是一个二叉树,每个节点表示一个空间范围。表1给出的是k-d树每个节点中主要包含的数据结构。
域名 | 数据类型 | 描述 |
Node-data | 数据矢量 | 数据集中某个数据点,是n维矢量(这里也就是k维) |
Range | 空间矢量 | 该节点所代表的空间范围 |
split | 整数 | 垂直于分割超平面的方向轴序号 |
Left | k-d树 | 由位于该节点分割超平面左子空间内所有数据点所构成的k-d树 |
Right | k-d树 | 由位于该节点分割超平面右子空间内所有数据点所构成的k-d树 |
parent | k-d树 | 父节点 |
以一个简单直观的实例来介绍k-d树算法。
假设有6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},数据点 位于二维空间内(如图1中黑点所示)。k-d树算法就是要确定图1中这些分割空间的分割线(多维空间即为分割平面,一般为超平面)。下面就要通过一步步展 示k-d树是如何确定这些分割线的。
由于此例简单,数据维度只有2维,所以可以简单地给x,y两个方向轴编号为0,1,也即split={0,1}。
(1)确定split域的首先该取的值。分别计算x,y方向上数据的方差得知x方向上的方差最大,所以split域值首先取0,也就是x轴方向;
(2)确定Node-data的域值。根据x轴方向的值2,5,9,4,8,7排序选出中值为7,所以Node-data = (7,2)。这样,该节点的分割超平面就是通过(7,2)并垂直于split = 0(x轴)的直线x = 7;
(3)确定左子空间和右子空间。分割超平面x = 7将整个空间分为两部分,如图2所示。x < = 7的部分为左子空间,包含3个节点{(2,3),(5,4),(4,7)};另一部分为右子空间,包含2个节点{(9,6),(8,1)}。
(4)k-d树的构建是一个递归的过程。然后对左子空间和右子空间内的数据重复根节点的过程就可以得到下一级子节点(5,4)和(9,6)(也就是 左右子空间的'根'节点),同时将空间和数据集进一步细分。如此反复直到空间中只包含一个数据点,如图1所示。
最后生成的k-d树如图3所示。
三、kd-tree 查找算法
在k-d树中进行数据的查找也是特征匹配的重要环节,其目的是检索在k-d树中与查询点距离最近的数据点。这里先以一个简单的实例来描述最邻近查找的基本思路。
星号表示要查询的点(2.1,3.1)。通过二叉搜索,顺着搜索路径很快 就能找到最邻近的近似点,也就是叶子节点(2,3)。
而找到的叶子节点并不一定就是最邻近的,最邻近肯定距离查询点更近,应该位于以查询点为圆心且通过叶 子节点的圆域内。
为了找到真正的最近邻,还需要进行'回溯'操作:
算法沿搜索路径反向查找是否有距离查询点更近的数据点。此例中先从(7,2)点开始进行 二叉查找,然后到达(5,4),最后到达(2,3),此时搜索路径中的节点为<(7,2),(5,4),(2,3)>,首先以(2,3)作为 当前最近邻点,计算其到查询点(2.1,3.1)的距离为0.1414,然后回溯到其父节点(5,4),并判断在该父节点的其他子节点空间中是否有距离查 询点更近的数据点。以(2.1,3.1)为圆心,以0.1414为半径画圆,如图4所示。发现该圆并不和超平面y = 4交割,因此不用进入(5,4)节点右子空间中去搜索。
四、接口文档查看
这个模块用定义了两个类
pcl::KdTree
和 pcl::KdTreeFLANN
是 PCL(Point Cloud Library)中用于最近邻搜索的两种不同的 KD 树实现。它们有一些区别,主要体现在实现细节和使用场景上。
pcl::KdTree
是 PCL 中 KD 树的一个抽象基类。它定义了 KD 树的一些基本接口,但没有具体实现。这意味着你不能直接实例化 pcl::KdTree
类,而是需要使用它的派生类。
继承关系如下:
五、demo演示
官方demo演示了,如何使用KdTree来查找特定点或位置的K个最近的邻居,以及指定的某个半径内找到所有相邻点云。
链接:How to use a KdTree to search — Point Cloud Library 0.0 documentation
- 示例一
#include <pcl/point_cloud.h> //点类型定义头文件
#include <pcl/kdtree/kdtree_flann.h> //kdtree类定义头文件
#include <iostream>
#include <vector>
#include <ctime>
int
main (int argc, char** argv)
{
srand (time (NULL)); //用系统时间初始化随机种子
//创建一个PointCloud<pcl::PointXYZ>
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
// 随机点云生成
cloud->width = 1000; //此处点云数量
cloud->height = 1; //表示点云为无序点云
cloud->points.resize (cloud->width * cloud->height);
for (size_t i = 0; i < cloud->points.size (); ++i) //循环填充点云数据
{
cloud->points[i].x = 1024.0f * rand () / (RAND_MAX + 1.0f);
cloud->points[i].y = 1024.0f * rand () / (RAND_MAX + 1.0f);
cloud->points[i].z = 1024.0f * rand () / (RAND_MAX + 1.0f);
}
//创建KdTreeFLANN对象,并把创建的点云设置为输入,创建一个searchPoint变量作为查询点
pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
//设置搜索空间
kdtree.setInputCloud (cloud);
//设置查询点并赋随机值
pcl::PointXYZ searchPoint;
searchPoint.x = 1024.0f * rand () / (RAND_MAX + 1.0f);
searchPoint.y = 1024.0f * rand () / (RAND_MAX + 1.0f);
searchPoint.z = 1024.0f * rand () / (RAND_MAX + 1.0f);
// K 临近搜索
//创建一个整数(设置为10)和两个向量来存储搜索到的K近邻,两个向量中,一个存储搜索到查询点近邻的索引,另一个存储对应近邻的距离平方
int K = 10;
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;
if ( kdtree.nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) > 0 ) //执行K近邻搜索
{
//打印所有近邻坐标
for (size_t i = 0; i < pointIdxNKNSearch.size (); ++i)
std::cout << " " << cloud->points[ pointIdxNKNSearch[i] ].x
<< " " << cloud->points[ pointIdxNKNSearch[i] ].y
<< " " << cloud->points[ pointIdxNKNSearch[i] ].z
<< " (squared distance: " << pointNKNSquaredDistance[i] << ")" << std::endl;
}
/**********************************************************************************
下面的代码展示查找到给定的searchPoint的某一半径(随机产生)内所有近邻,重新定义两个向量
pointIdxRadiusSearch pointRadiusSquaredDistance来存储关于近邻的信息
********************************************************************************/
// 半径 R内近邻搜索方法
std::vector<int> pointIdxRadiusSearch; //存储近邻索引
std::vector<float> pointRadiusSquaredDistance; //存储近邻对应距离的平方
float radius = 256.0f * rand () / (RAND_MAX + 1.0f); //随机的生成某一半径
//打印输出
std::cout << "Neighbors within radius search at (" << searchPoint.x
<< " " << searchPoint.y
<< " " << searchPoint.z
<< ") with radius=" << radius << std::endl;
if ( kdtree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0 ) //执行半径R内近邻搜索方法
{
for (size_t i = 0; i < pointIdxRadiusSearch.size (); ++i)
std::cout << " " << cloud->points[ pointIdxRadiusSearch[i] ].x
<< " " << cloud->points[ pointIdxRadiusSearch[i] ].y
<< " " << cloud->points[ pointIdxRadiusSearch[i] ].z
<< " (squared distance: " << pointRadiusSquaredDistance[i] << ")" << std::endl;
}
return 0;
}
CMakeList如下
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(kdtree_search)
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable (kdtree_search kdtree_search.cpp)
target_link_libraries (kdtree_search ${PCL_LIBRARIES})
输出如下:
K nearest neighbor search at (455.807 417.256 406.502) with K=10
494.728 371.875 351.687 (squared distance: 6578.99)
506.066 420.079 478.278 (squared distance: 7685.67)
368.546 427.623 416.388 (squared distance: 7819.75)
474.832 383.041 323.293 (squared distance: 8456.34)
470.992 334.084 468.459 (squared distance: 10986.9)
560.884 417.637 364.518 (squared distance: 12803.8)
466.703 475.716 306.269 (squared distance: 13582.9)
456.907 336.035 304.529 (squared distance: 16996.7)
452.288 387.943 279.481 (squared distance: 17005.9)
476.642 410.422 268.057 (squared distance: 19647.9)
Neighbors within radius search at (455.807 417.256 406.502) with radius=225.932
494.728 371.875 351.687 (squared distance: 6578.99)
506.066 420.079 478.278 (squared distance: 7685.67)
368.546 427.623 416.388 (squared distance: 7819.75)
474.832 383.041 323.293 (squared distance: 8456.34)
470.992 334.084 468.459 (squared distance: 10986.9)
560.884 417.637 364.518 (squared distance: 12803.8)
466.703 475.716 306.269 (squared distance: 13582.9)
456.907 336.035 304.529 (squared distance: 16996.7)
452.288 387.943 279.481 (squared distance: 17005.9)
476.642 410.422 268.057 (squared distance: 19647.9)
499.429 541.532 351.35 (squared distance: 20389)
574.418 452.961 334.7 (squared distance: 20498.9)
336.785 391.057 488.71 (squared distance: 21611)
319.765 406.187 350.955 (squared distance: 21715.6)
528.89 289.583 378.979 (squared distance: 22399.1)
504.509 459.609 541.732 (squared distance: 22452.8)
539.854 349.333 300.395 (squared distance: 22936.3)
548.51 458.035 292.812 (squared distance: 23182.1)
546.284 426.67 535.989 (squared distance: 25041.6)
577.058 390.276 508.597 (squared distance: 25853.1)
543.16 458.727 276.859 (squared distance: 26157.5)
613.997 387.397 443.207 (squared distance: 27262.7)
608.235 467.363 327.264 (squared distance: 32023.6)
506.842 591.736 391.923 (squared distance: 33260.3)
529.842 475.715 241.532 (squared distance: 36113.7)
485.822 322.623 244.347 (squared distance: 36150.5)
362.036 318.014 269.201 (squared distance: 37493.6)
493.806 600.083 462.742 (squared distance: 38032.3)
392.315 368.085 585.37 (squared distance: 38442.9)
303.826 428.659 533.642 (squared distance: 39392.8)
616.492 424.551 289.524 (squared distance: 39556.8)
320.563 333.216 278.242 (squared distance: 41804.5)
646.599 502.256 424.46 (squared distance: 43948.8)
556.202 325.013 568.252 (squared distance: 44751)
291.27 497.352 515.938 (squared distance: 45463.9)
286.483 322.401 495.377 (squared distance: 45567.2)
367.288 550.421 550.551 (squared distance: 46318.6)
595.122 582.77 394.894 (squared distance: 46938.1)
256.784 499.401 379.931 (squared distance: 47064.1)
430.782 230.854 293.829 (squared distance: 48067.2)
261.051 486.593 329.854 (squared distance: 48612.7)
602.061 327.892 545.269 (squared distance: 48632.4)
347.074 610.994 395.622 (squared distance: 49475.6)
482.876 284.894 583.888 (squared distance: 49718.6)
356.962 247.285 514.959 (squared distance: 50423.7)
282.065 509.488 516.216 (squared distance: 50730.4)
- 示例二
使用 pcl::KdTreeFLANN
的示例,展示了如何进行最近邻搜索。
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <iostream>
#include <vector>
int main() {
typedef pcl::PointXYZ PointT;
// 创建点云
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
// 添加点到点云
cloud->push_back(PointT(0.0, 0.0, 0.0));
cloud->push_back(PointT(1.0, 0.0, 0.0));
cloud->push_back(PointT(0.0, 1.0, 0.0));
cloud->push_back(PointT(0.0, 0.0, 1.0));
// 创建KD树
pcl::KdTreeFLANN<PointT> kdtree;
kdtree.setInputCloud(cloud);
// 查询点
PointT searchPoint(0.5, 0.5, 0.5);
// 搜索最近邻
int K = 2;
std::vector<int> pointIdxNKNSearch(K);
std::vector<float> pointNKNSquaredDistance(K);
if (kdtree.nearestKSearch(searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) > 0) {
for (size_t i = 0; i < pointIdxNKNSearch.size(); ++i)
std::cout << "Index: " << pointIdxNKNSearch[i] << " ("
<< cloud->points[pointIdxNKNSearch[i]].x << " "
<< cloud->points[pointIdxNKNSearch[i]].y << " "
<< cloud->points[pointIdxNKNSearch[i]].z << ") "
<< "distance: " << pointNKNSquaredDistance[i] << std::endl;
}
return 0;
}