KD-tree文档
- 格式:docx
- 大小:79.74 KB
- 文档页数:10
基于Matlab的三维kdtree构建代码1. 介绍在计算机科学和几何学中,k维树(kd-tree)是一种用于存储k维空间数据的数据结构。
它是一种二叉树,其中每个结点都是k维空间中的一个点,而非叶子节点表示一个将k维空间划分为两个半空间的超平面。
通过构建kd-tree,可以高效地搜索最近邻点、范围搜索以及k最近邻搜索等问题。
本文将详细介绍如何基于Matlab编写一个三维kd-tree构建代码,包括三个主要部分:数据结构设计、插入操作和搜索操作。
2. 数据结构设计2.1 kd-tree节点kd-tree的节点包括坐标值、切分维度以及左右孩子节点指针。
在Matlab中,可以使用结构体来表示节点:struct Nodepointsplit_dimleft_childright_childend参数解释: - point:节点对应的坐标值,是一个1x3的向量。
- split_dim:切分维度,表示当前节点是按照哪一个维度进行切分的。
- left_child:左孩子节点。
- right_child:右孩子节点。
2.2 kd-tree树kd-tree是由节点构成的二叉树结构。
在Matlab中,我们可以使用一个结构体数组来表示:struct Treerootend参数解释: - root:树的根节点。
3. 插入操作kd-tree的插入操作是将一个新的点插入到已有的树中。
具体实现如下:1.如果树为空,则将新节点作为根节点。
2.否则,按照树的结构进行递归地查找插入位置:1.根据当前节点的切分维度,将新节点与当前节点比较。
2.如果新节点在当前维度的坐标值小于当前节点,则继续查找左子树。
3.否则,继续查找右子树。
4.递归地执行上述步骤,直到找到一个空节点位置。
3.在找到的空节点位置插入新节点。
具体的Matlab代码如下:function tree = insert(tree, new_point)if isempty(tree.root)tree.root = Node(new_point, 1, [], []);elsetree.root = insertNode(tree.root, new_point, 1);endendfunction node = insertNode(node, new_point, dim)if isempty(node)node = Node(new_point, dim, [], []);return;endif new_point(dim) < node.point(dim)node.left_child = insertNode(node.left_child, new_point, mod(dim+1, 3) +1);elsenode.right_child = insertNode(node.right_child, new_point, mod(dim+1, 3)+1);endend4. 搜索操作kd-tree的搜索操作可以分为最近邻搜索和范围搜索。
kdtree通俗原理英文回答:Kd-tree, short for k-dimensional tree, is a data structure that is commonly used for organizing multidimensional data in computer science. It isparticularly useful for solving nearest neighbor search problems. The basic idea behind a kd-tree is to partition the data space into regions, such that each region contains a subset of the data points.To construct a kd-tree, we start with a set of data points. We choose a splitting axis and a splitting value that divides the data points into two subsets. Thesplitting axis is chosen based on some criteria, such as the axis with the largest variance in the data. Thesplitting value is usually the median value along the chosen axis. The data points are then divided into two subsets based on whether they are less than or greater than the splitting value along the splitting axis.This process is repeated recursively for each subset until a stopping condition is met. The stopping condition can be a maximum depth of the tree, a minimum number of data points in a leaf node, or any other criteria that we choose. At each level of the tree, we alternate thesplitting axis to ensure that the data points are evenly distributed across the tree.Once the kd-tree is constructed, we can perform nearest neighbor search by traversing the tree based on thesplitting criteria. Starting at the root node, we compare the query point with the splitting value along thesplitting axis. Based on the comparison, we choose thechild node to visit next. We continue this process until we reach a leaf node, which contains a subset of data points. We then calculate the distance between the query point and each data point in the leaf node, and return the nearest neighbor.Kd-tree has several advantages over other data structures for nearest neighbor search. It has a relativelyfast construction time, especially for high-dimensional data. It also has a low memory footprint, as it only requires storing the splitting axis and splitting value at each node. Additionally, kd-tree can be efficiently used for range search queries and k-nearest neighbor search.中文回答:Kd树,即k维树,是计算机科学中常用的一种用于组织多维数据的数据结构。
基于OpenCL的实时KD-Tree与动态场景光线跟踪章节一:引言介绍实时KD-Tree与动态场景光线跟踪的研究背景和意义,以及当前该领域的研究现状和存在的问题。
章节二:相关技术介绍OpenCL技术及其在光线跟踪中的应用,KD-Tree算法原理和性质,以及动态场景下光线跟踪的挑战和解决方案。
章节三:算法设计与实现描述实时KD-Tree与动态场景光线跟踪的算法设计和实现过程,包括KD-Tree的构建和维护、光线跟踪的加速和优化等部分。
章节四:性能评测与实验结果分析对实现的算法进行性能评测,包括多种不同场景下的光线跟踪速度、KD-Tree构建时间以及存储空间利用率等。
同时,分析算法实现过程中所遇到的问题及提升空间。
章节五:结论与展望总结本文所述的实时KD-Tree与动态场景光线跟踪算法,评估其实用价值,并探讨未来的研究方向和挑战。
第一章节:引言随着计算机图形学的发展,基于光线跟踪的渲染技术已经逐渐成为了3D场景渲染的主要方法之一。
光线跟踪技术模拟了光线在场景中的传播和反射,并估计每个像素点对应的颜色值。
而对于复杂场景,由于光线跟踪需要遍历所有的场景物体,因此执行速度较慢,导致实时渲染问题。
为了解决这一问题,目前研究人员提出了大量的光线跟踪加速技术,其中基于KD-Tree算法实现的加速方法已经被证明效果显著。
该方法将场景中的物体划分成多个树状结构,在光线跟踪过程中通过二叉树搜索的方式快速地找到光线所接触的物体,以达到加速的效果。
然而,KD-Tree算法也存在一些问题。
例如,由于算法构建的树是静态的,因此难以应对动态场景中物体的不断变化。
为了解决这一问题,目前研究人员提出了一些动态网格模型,但这些模型的维护代价很高,并且难以支持图形硬件加速。
因此,如何实现在动态场景下实时构建和维护KD-Tree算法是本研究所关注的问题。
在本文中,我们主要研究基于OpenCL的实时KD-Tree与动态场景光线跟踪算法。
OpenCL是一种跨平台的计算语言,可以利用硬件加速技术加速计算,并在多种平台环境下进行并行计算。
第28卷第10期计算机应用与软件Vol.28No.102011年10月Computer Applications and Software Oct.2011基于复杂场景图的光线追踪渲染的Kd-tree 构造陈立华王毅刚(杭州电子科技大学图形图像研究所浙江杭州310018)收稿日期:2010-09-01。
陈立华,硕士生,主研领域:虚拟现实。
摘要在基于光线跟踪等的全局光照绘制中,改良空间划分结构一直是各种加速策略中重要的方法之一。
对常见的空间结构构建方法进行研究,针对复杂室内场景提出一种快速的分区构建方法。
首先,算法并不直接将整个空间进行剖分,而是采用分组策略,结合包围盒进行判断,将具有一定空间联系的场景实体合并成一定数量的组;之后,对每个组使用优化后的Kd-tree 构建细分结构,并提出合理的终止条件。
与以往的方法相比,该方法构建的加速结构更适合于基于场景图构建的复杂室内环境,为快速生成真实感图形提供了有效的手段。
关键词全局光照光线跟踪Kd-tree场景图离线渲染中图分类号TP37文献标识码AKD-TREE CONSTRUCTION OF RAY-TRACING RENDERINGBASED ON COMPLEX SCENE-GRAPHChen LihuaWang Yigang(Institute of Computer Graphics ,Hangzhou Dianzi University ,Hangzhou 310018,Zhejiang ,China )AbstractIn global illumination rendering based on ray-tracing or on something else ,the improvement of spatial partition structure hasalways been one of the important methods in various acceleration policies.This paper focuses on the study of common construction approaches of spatial structure ,proposes a fast partition construction method aiming at complex indoor scenes.Firstly the authors use a grouping strategy instead of dividing the whole space directly ,and combine the scene entities which are spatial associated to a certain extent into a sum of render-groups according to the judgement made in consideration with the bounding box.Then they use the optimised Kd-tree to construct sub-structures for each group ,and put forward reasonable termination condition.Compared with previous methods ,the accelerated structure constructed by this algorithm is more adapted to the complex indoor scene constructed based on scene graph.It provides an effective means to fast generation of realistic graphics.KeywordsGlobal illuminationRay-tracingKd-treeScene graphOffline rendering0引言在高质量真实感图形绘制领域,光线跟踪算法一直是生成逼真场景图像的主要应用的算法之一,其原理简单、易于实现,并且可以生成各种逼真的视觉效果,因而在CAD 及图形学各个领域得到了广泛的应用。
KdTree算法详解kd树(k-dimensional树的简称),是⼀种分割k维数据空间的数据结构,主要应⽤于多维空间关键数据的近邻查找(Nearest Neighbor)和近似最近邻查找(Approximate Nearest Neighbor)。
⼀、Kd-tree其实KDTree就是⼆叉查找树(Binary Search Tree,BST)的变种。
⼆叉查找树的性质如下:1)若它的左⼦树不为空,则左⼦树上所有结点的值均⼩于它的根结点的值;2)若它的右⼦树不为空,则右⼦树上所有结点的值均⼤于它的根结点的值;3)它的左、右⼦树也分别为⼆叉排序树;例如:如果我们要处理的对象集合是⼀个K维空间中的数据集,我们⾸先需要确定是:怎样将⼀个K维数据划分到左⼦树或右⼦树?在构造1维BST树类似,只不过对于Kd树,在当前节点的⽐较并不是通过对K维数据进⾏整体的⽐较,⽽是选择某⼀个维度d,然后⽐较两个K维数据在该维度 d上的⼤⼩关系,即每次选择⼀个维度d来对K维数据进⾏划分,相当于⽤⼀个垂直于该维度d的超平⾯将K维数据空间⼀分为⼆,平⾯⼀边的所有K维数据在d维度上的值⼩于平⾯另⼀边的所有K维数据对应维度上的值。
也就是说,我们每选择⼀个维度进⾏如上的划分,就会将K维数据空间划分为两个部分,如果我们继续分别对这两个⼦K维空间进⾏如上的划分,⼜会得到新的⼦空间,对新的⼦空间⼜继续划分,重复以上过程直到每个⼦空间都不能再划分为⽌。
以上就是构造 Kd-Tree的过程,上述过程中涉及到两个重要的问题:1. 每次对⼦空间的划分时,怎样确定在哪个维度上进⾏划分;2. 在某个维度上进⾏划分时,怎样确保建⽴的树尽量地平衡,树越平衡代表着分割得越平均,搜索的时间也就是越少。
1、在哪个维度上进⾏划分?⼀种选取轴点的策略是median of the most spread dimension pivoting strategy,统计样本在每个维度上的数据⽅差,挑选出对应⽅差最⼤值的那个维度。
kdtree用法-回复kdtree用法:一步一步回答引言:Kdtree是一种用于高效地搜索k维空间中最近邻点的数据结构。
它的应用涵盖了许多领域,如模式识别、计算机图形学、机器学习等。
本文将一步一步介绍kdtree的用法,包括构建、插入、最近邻搜索以及删除等操作。
第一步:构建kdtree构建kdtree是使用kdtree的第一步。
首先,我们需要定义一个节点类来表示kdtree中的每个节点。
每个节点包含的属性有:point(用于存储k 维空间中的点)、left(指向左子树的指针)、right(指向右子树的指针)和axis(表示划分区域的坐标轴)。
接下来,我们需要定义一个递归函数来构建kdtree。
该函数将接收一个点集和当前节点作为参数。
它的基本思想是,找到当前点集中在当前坐标轴上的中位数点,作为当前节点的point,并将点集按照当前坐标轴上的中位数点分成两个子集,分别递归构建左子树和右子树。
具体的构建过程如下:1. 如果点集为空,则当前节点为空节点,返回。
2. 找到当前坐标轴上的中位数点m,将其作为当前节点的point。
3. 将点集按照m分成两个子集:小于m的点集和大于m的点集。
4. 递归构建左子树,将小于m的点集作为参数传入,并将左子树的根节点设为当前节点的left。
5. 递归构建右子树,将大于m的点集作为参数传入,并将右子树的根节点设为当前节点的right。
构建结束后,我们就得到了一个完整的kdtree。
第二步:插入点到kdtree在构建kdtree的基础上,我们可以进一步插入新的点。
插入点的过程与构建kdtree的过程类似。
首先,我们需要找到插入点在当前坐标轴上的中位数点m,然后与当前节点的point进行比较。
如果插入点小于m,则递归插入左子树;如果插入点大于等于m,则递归插入右子树。
如果当前节点的left或right为空节点,则创建一个新的节点,并将其作为当前节点的left或right。
具体的插入过程如下:1. 如果当前节点为空节点,则创建一个新的节点,将插入点作为其point,并返回。
Kd-tree工作总结1了解什么是kd-treekd-tree主要是用来对多维数据进行最近邻查找。
kd-Tree类似一颗二叉树,在二叉树中是根据数据和根节点的大小来进行数据的划分,对于Kd-tree这样一棵二叉树,我们首先需要确定怎样划分左子树和右子树,即一个K维数据是依据什么被划分到左子树或右子树的。
在构造一个二叉树树时,一个一维数据根据其与树的根结点和中间结点进行大小比较的结果来决定是划分到左子树还是右子树,同理,我们也可以按照这样的方式,将一个K维数据与Kd-tree的根结点和中间结点进行比较,只不过不是对K维数据进行整体的比较,而是选择某一个维度Di,然后比较两个K维数在该维度Di上的大小关系,即每次选择一个维度Di来对K维数据进行划分,相当于用一个垂直于该维度Di的平面将K维数据空间一分为二,平面一边的所有K维数据在Di维度上的值小于平面另一边的所有K维数据对应维度上的值。
也就是说,我们每选择一个维度进行如上的划分,就会将K维数据空间划分为两个部分,如果我们继续分别对这两个子K维空间进行如上的划分,又会得到新的子空间,对新的子空间又继续划分,重复以上过程直到每个子空间都不能再划分为止。
以上就是构造Kd-Tree的过程,上述过程中涉及到两个重要的问题:1)每次对子空间的划分时,怎样确定在哪个维度上进行划分;2)在某个维度上进行划分时,怎样确保在这一维度上的划分得到的两个子集合的数量尽量相等,即左子树和右子树中的结点个数尽量相等。
对于问题1,一般采用最大方差法,即每次我们选择维度进行划分时,都选择具有最大方差维度。
对于问题2,假设当前我们按照最大方差法选择了在维度i上进行K划分,此时我们需要在维度i上将K维数据集合S划分为两个子集合A和B,子集合A中的数据在维度i上的值都小于子集合B中。
首先考虑最简单的划分法,即选择第一个数作为比较对象,S中剩余的其他所有K维数据都跟该数在维度i上的值(分割值)进行比较,如果小于分割值则划A集合,大于则划入B集合。
把A集合和B集合分别看做是左子树和右子树,那么我们在构造一个二叉树的时候,当然是希望它是一棵尽量平衡的树,即左右子树中的结点个数相差不大。
而A集合和B集合中数据的个数显然跟分割值有关,因为它们是跟分割值比较后才被划分到相应的集合中去的。
好了,现在的问题就是确定分割值了。
给定一个数组,怎样才能得到两个子数组,这两个数组包含的元素个数差不多且其中一个子数组中的元素值都小于另一个子数组呢?方法很简单,找到数组中的中值(即中位数),然后将数组中所有元素与中值进行比较,就可以得到上述两个子数组。
同样,在维度i上进行划分时,分割值就选择该维度i上所有数据的中值,这样得到的两个子集合数据个数就基本相同了。
构建好一棵Kd-Tree后,之后就要进行查找了,Kd-Tree主要有两种查找方法,一种是设不定回溯次数得查找,这种查找方式保证能找到最优值,但是因为回溯的存在,当维数很高时效率下降的很快,另一种是设定回溯次数的查找方法,在回溯中有些树分支存在最近邻的可能性比其他树分支要高,因为树分支离查找点之间的距离或相交程度是不一样的,离查找点更近的树分支存在查找点的最近邻的可能性更高。
因此,我们需要区别对待每个待回溯的树分支,即采用某种优先级顺序来访问这些待回溯树分支,使得在有限的回溯次数中找到目标点的可能性很高。
2自己实现kd-tree最开始想去看看opencv中有没有实现好的Kd-Tree,但是网上查找后,基本上给出的例子都最多到3维数据的查找,于是决定自己实现一个Kd-Tree,经过一周多的努力,总算实现了从建立树到查找数据的全过程,但是通过实验发现实际查找效率上只能达到暴力搜索的三分之一,性能不是很理想,不能达到实验室需求。
于是决定分析opencv关于Kd-Tree查找的算法。
3opencv实现的kd-tree的算法网上给出的例子都是通过cv::Index这个类来调用Kd-Tree算法,经过分析发现,这个类最后调用的是cvflann::KDTreeIndex来实现具体算法。
下面来具体介绍opencv的做法。
cvflann::KDTreeIndex类的构造函数如下:首先就是Matrix<ElementType>类型的inputData,顾名思义,这个类型代表构建Kd-Tree的输入数据,这个类内数据很简单,主要就是保存了输入数据的首地址,数据的个数和每个数据的维数,以及一个operator[]函数(下标运算符的重载函数)。
这个函数的作用就是输入一个索引值,然后返回该索引值数据的首地址。
函数内就一句话return data+index*stride,因此根据这个重载函数可以得知Matrix<ElementType>类内保存的数据必须是连续存放的。
因为网上的例子都通过cv::Index这个类来调用Kd-Tree算法,其中关于数据连续的检测都在这个类中完成,但是现在我们要直接调用底层的实现类,所以要自己确保数据的连续性。
构造函数的第二个参数是IndexParams类型的params,可以看到它有默认值,这个params表示建立索引的方式,opencv实现的Kd-Tree为了实现限制回溯的查找方式,采用了把数据打乱后同时建立多棵树,这样每颗数的分割维度和分割值都不相同,查找数据时,同时对多棵树进行查找,之后回溯时根据优先队列,在多棵树上回溯查找。
这种设计主要是为了提高查找精度。
其实看源码可以发现IndexParams就是std::map<cv::String, any>的别名,而cvflann::KDTreeIndex主要用到”trees”这个参数,这就是前面说的建立Kd-Tree 的个数,如果要采用限制回溯查找次数的方法的话,这个”trees”要大于等于一,要是采用不限制回溯查找次数的方法的话,这个”trees”必须为一。
构造函数的第三个参数是Distance类型的d,参数也带有默认值,Distance表示是opencv 中用来计算距离的类,这里我们传的是计算L2距离的类。
在实际使用中,一般都是用L2距离,所以一般传前两个参数的值就行了。
这个构造函数主要做的工作就是把inputData保存到自己的类中,获取数据的维数和个数获取需要建立数的个数,分配数的根节点并保存,以及建立索引数组。
初始化之后就要建立Kd-Tree了,建立主要通过buildIndex()函数,函数如下可以看到根据需要建立Kd-Tree的个数,循环去建立,因为采用了std::random_shuffle(vind_.begin(), vind_.end());,因此每次建立的数都不一样,这个函数实际调用了工具函数divideTree这个函数完成了具体的建立树的过程,可以看到其中还调用了其他的函数来完成工作,具体建立数的原理也和我前面说的过程差不多,比如确立分割维度和分割值,这里我只说下opencv采取的提高速度的只要方法,meanSplit(ind, count, idx, cutfeat, cutval);完成具体的确立分割维度和分割值的工作,剩下的就是递归建树,首先是计算均值,这里opencv并没有计算有数据在维度上的均值,它最多取随机的100个数据进行计算,这也是前面建立多颗Kd-Tree而都不相同的原因,当然再提高了计算速度的同时,也容易造成误差,我想这也是它采取建立多颗Kd-Tree的原因,来避免这种误差。
之后对取出的最多100个数据计算方法,opencv只计算了平方和,并没有开根号,这也提高了速度,最后根据均值和方差,对索引数组进行重新排列,这是通planeSplit这个函数完成的,它把比搜索值小的放在索引前面,大的放在索引后面,然后获得索引点,之后递归的进行建立树。
如果是叶子节点的话,节点中的divefeat保存的就是索引值,通过索引,就能获得原始的数据了。
下面是数据的查找,cvflann::KDTreeIndex类用findNeighbors这个接口函数完成数据的查找:第一个参数是ResultSet<DistanceType>& result,这是个基类,在Kd-Tree查找数据时,传递的是它的派生类KNNUniqueResultSet,这个类主要可以设定查询的个数,进行查询结果使得保存,以及对距离近行排序,因为最终的结果可能返回多个,因此这个类可以实现返回距离最近的前几个的索引cvflann::KNNUniqueResultSet<float> resultSet(1);可以按照这种方法使用。
传递的1表示最终返回结果的个数。
第二个参数是const ElementType* 的数组,这就是查询的数据,传递的是指针。
第三个参数是一个查询参数,这个和前面的建树参数类似,实际上也是个map,这个查询参数主要用来设定查询是回溯的次数,可以通过设定"checks"的值,如果"checks"设定为-1,那么代表不限定回溯次数的意思,当设定"checks"为-1时,前面的建立数的参数”trees”必须为1。
在函数中可以看到如果不现在回溯次数调用getExactNeighbors,如果限制则调用getNeighbors在限制次数的搜索函数中,利用优先队列来实现对分支距离的排序,对距离最近的分支优先搜索。
具体过程就不讨论了。
4 opencv实现的kd-tree的问题但是opencv实现的kd-tree不能满足实验室的需求,因为opencv需要建立数的数据必须连续存放,而且必须按照它给出的形式存,因此需要改造Matrix<ElementType>类,改变其中的operator[]函数(下标运算符的重载函数)读取数据的方法,最开始我计划采用派生类的方法,但是发现Matrix<ElementType>类里的operator[]函数不是虚函数,于是最后采用的方法是模仿opencv的实现,自己写一个kd-tree的类,并且自己重新写一个Matrix<ElementType>类。
这样可以随意改变数据的存放方式,使用上更加灵活,并且方便扩展。
4自己的kd-tree的使用方法我主要重写了两个opencv自带的类,分别是cvflann::KDTreeIndex和Matrix<ElementType>类,我起名为My_KD_Tree和My_Matrix,这两个类的实现和opencv 自带的差不多,我主要讲讲不同点,和使用。
首先是My_Matrix类,下面是我重写的operator[]函数。
为了实验自己的想法是否可行,我这里完全改写的operator[]函数完全从硬盘中去获取数据,即My_Matrix里只保存数据的个数和维数,这个函数每给一个索引,才去硬盘中读取一条数据,保存到内存。