最近还是一直在研究SIFT算法,而SIFT特征点匹配是一个比较经典的问题,使用暴力匹配的话确实可以得到结果,但是运行速度较慢。我的计算机处理是i5的二代系列,匹配两张各检测有2000+个SIFT特征点的图像,通过正反匹配(即取图像1与图像2的匹配结果余图像2和图像1的匹配结果的交集),再加上OpenMP多线程加速,使用暴力匹配,大概要花20多秒,还是比较慢的。所以这一周啥也没做,一直在实现kd树和对应的bbf算法。下面详细介绍下种数据结构。
一、k-d树的介绍与实现
1.1 k-d树的创建
k-d树其实就是一种树形的数据结构,但是在创建这棵树时有一些固定的规则。下面来讲一下kd树的创建过程
输入:一组数据点集,n个数据点,每个点有m维
输出:k-d树的根结点指针
过程:(1)分别计算这n个数据点在m维中各个维度的方差,取方差最大的维度dim作为分割维度;
(2)把数据点集按照该维度中值的大小进行排列,选择具有中间值的点作为该树的根结点;
(3)前半部分点进行如(1)、(2)所示的递归操作,选出的递归子树的根节点作为(2)中得到的根节点的左孩子;
同理,后半部分也这样操作。如此一直递归,直到各个递归子树的数据点集为空则算法截止。
例子:以2维平面上的点集为例,设有6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}。
(1) 首先计算这6个点的横坐标和纵坐标的方差值,横坐标的方差值为39,纵坐标上的方差值为28.63,因此第一次分割取横坐标上的值作为分割标准。把这些点按照横坐标进行排序得到{(2,3),(4,7),(5,4),(7,2),(8,1),(9,6)},取中间点为(7,2),因此根节点为(7,2)进行分割,如下图所示:
图1 分割示意图
(2)接下来对{(2,3),(4,7),(5,4)}和{(8,1),(9,6)}分别进行分割,在{(2,3),(4,7),(5,4)}中纵坐标的方差较大,因此按纵坐标进行排序后分割,则(5,4)为(7,2)的左孩子,{(8,1),(9,6)}中也是纵坐标方差较大,因此选纵坐标进行排序后分割,这里算则(9,6)作为(7,2)的右孩子。
(3)依次递归进行分割,最终形成的分割图和树状结构如下所示:
图2 上例中形成的分割图
图3 上例中形成的树状结构
1.2 k-d树的查询
k-d树建立好以后,需要查询它的最近邻,方法如下:
(1)查询点与k-d树的根节点进行比较,比较两者在根节点划分时的维度的值的大小,若查询点在该维的值小,则进入根节点的左子树,否则进入右子树。依次类推,进行查找,直到到达树的叶子节点。
(2)设当前到达的叶子节点为目前的最近邻(注意:可能并非真正的最近邻),并且记录目前的最近邻距离。沿着来时的路向前回溯,让目前的最近邻距离与查找点与当前叶子节点的父节点形成的分割超平面的距离进行比较,若当前最近邻比较小,则不用遍历当前叶子节点的父节点的另一边,否则需要遍历查找以更新最近邻距离和最近邻节点。
(3)按照(2)中所说依次遍历,直到到达根节点为止,查询结束。