使用KdTree进行搜索

在本教程中,我们将介绍如何使用KdTree查找特定点或位置的K个最临近点,然后还将探讨如何找到用户指定半径内的所有邻居。

点云主要是表征目标表面的海量点的集合,并不具备传统网格数据的几何拓扑信息,所以点云数据处理中最为核心的问题就是建立离散点之间的拓扑关系,实现基于邻域关系的快速查找。
KdTree是CS中用于分割k维空间中的一些点的数据结构,主要应用于多维空间关键数据的搜索(如范围搜索和最近邻搜索)。K-D树是二进制空间分割树的特殊情况,用于组织表示K维空间中点的几何,是一种带有其他约束的二分查找树,为了达到目的,通常只在三个维度中进行处理因此所有的KdTree都是三维的KdTree,KdTree的每一维在指定维度上分开所有的子节点,在树的根部所有子节点是以第一个指定的维度被分开。
kdTree算法可以分为两大部分,一部分是有关KdTree本身这种数据结构建立的算法,另一部分是在建立的KdTree树上如何进行最临近查找的算法。
KdTree是一个二叉树,每个节点表示一个空间范围。KdTree每个节点中主要包含的数据结构有:
Node-data 数据矢量 数据集中某个数据点,为n维矢量(在KdTree中就是k维)
Range 空间矢量 该节点所代表的空间范围
Split 整数 垂直于分割超平面的方向轴序号
Left KdTree 由位于该节点分割超平面左子空间内所有数据点所构成的Kd树
Right KdTree 由位于该节点分割超平面右子空间内所有数据点所构成的Kd树
Parent KdTree 父节点
关于kDTree的构建就不再详述了

KdTree查找算法
在KdTree中进行数据的查找也是特征匹配的重要环节,其目的是检索在KdTree中与查询点距离最近的数据点,这里先以一个简单的实例来描述最近邻查找的基本思路。
在图中的星号点表示要查询的点(2.1,3.1).通过二叉搜索,顺着搜索路径很快就能找到最临近的近似点,也就是叶子节点(2.3)。而找到的叶子节点并不一定就是最邻近的,最临近点的肯定距离查询点更近,应该位于以查询点为圆心且通过叶子节点的圆域中内。为了找到真正的最近邻,还要进行回溯操作:算法沿搜索路径反向查找是否有距离查询点更近的数据点。此例中搜索路径为<(7,2),(5,4),(2,3)>,首先我们将(2,3)作为当前最近邻点,计算其到查询点的距离为0.141,然后回溯到其父节点(5,4),并判断在该父节点的其他子节点空间中是否有距离查询点更近的数据点。以(2.1,3.1)为圆心,以0.1414为半径画圆,如下图所示。发现该圆并不和超平面y=4交割,因此不用进入(5,4)节点右子空间中去搜索。

KdTree搜索
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <vector>
#include <ctime>

#include <pcl/point_cloud.h>
#include <pcl/kdtree/kdtree_flann.h>

int main(int argc,char** argv){
srand(time(NULL));//用系统时间初始化随机种子

pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

//随机点云生成
cloud->width=1000;
cloud->height=1;
cloud->resize(cloud->height*cloud->width);

for (size_t i = 0; i < cloud->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邻近搜索**********************************
设定一个值K,使用该搜索方法会搜索指定点的邻域,找出距离目标点最近的10个点
**************************************************************/
int K=10;//这里设定K为10

std::vector<int> pointIdxNKNSearch(K);//存储搜索到的点
std::vector<float> pointNKNSquareDistance(K);//存储搜索到的点与目标点距离的平方
std::cout<<"K nearset neighbor search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with K="<<K<<std::endl;

if(kdtree.nearestKSearch(searchPoint,K,pointIdxNKNSearch,pointNKNSquareDistance)){
for (int 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: " << pointNKNSquareDistance[i] << ")" << std::endl;
}
}
/********************KdTree半径搜索******************************
设定一个半径,使用KdTree的搜索方式来搜索指定半径内的点,将其信息存储在向量中
**************************************************************/
std::vector<int> pointIdxRadiusSearch;
std::vector<float> pointRadiusSquareDistance;

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,pointRadiusSquareDistance)){
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: " << pointRadiusSquareDistance[i] << ")" << std::endl;
}
}

输出结果如下:

输出结果

感谢您的阅读。 🙏 关于转载请看这里