C++ - 使用 opencv flann 查找最近的邻居

Posted

技术标签:

【中文标题】C++ - 使用 opencv flann 查找最近的邻居【英文标题】:C++ - Finding nearest neighbor using opencv flann 【发布时间】:2015-04-07 09:59:19 【问题描述】:

我有两组点 (cv::Point2f):setA 和 setB。对于 setA 中的每个点,我想在 setB 中找到它的最近邻居。所以,我尝试了两种方法:

    线性搜索:对于 setA 中的每个点,只需简单地扫描 setB 中的所有点即可找到最近的点。

    使用opencv kd-tree:

    _ 首先,我使用 opencv flann 为 setB 构建了一个 kd-tree:

    cv::flann::KDTreeIndexParams indexParams;
    cv::flann::Index kdTree(cv::Mat(setB).reshape(1), indexParams);
    

    _ 然后,对于 setA 中的每个点,我都会查询以找到最近的邻居:

    kdTree.knnSearch(point_in_setA, indices, dists, maxPoints);
    

注意:我将 maxPoints 设置为 1,因为我只需要最近的一个。

我做了一些研究,得出每个案例的时间复杂度:

    线性搜索:O(M*N)

    Kd-Tree: NlogN + MlogN => 第一项用于构建 kd-tree,第二项用于查询

其中 M 是 setA 中的点数,N 用于 setB。 N的范围:100~1000,M的范围:10000~100000。

因此,kd-tree 的运行速度应该比线性搜索方法快得多。但是,当我在笔记本电脑上运行实际测试时,结果是 kd-tree 方法比线性搜索慢(0.02~0.03s vs 0.4~0.5s)。

当我进行分析时,我在 knnSearch() 函数上遇到了热点,与线性搜索的 7.9% 相比,它需要 20.3% 的 CPU 时间。

嗯,我看了一些网上的文章,他们说查询kd-tree它通常需要logN。但是我不确定opencv是如何实现的。

有人知道这里出了什么问题吗?在 kd-tree 中是否有我应该调整的参数,或者我在代码或计算中的某个地方犯了错误?

【问题讨论】:

【参考方案1】:

取自Flann documentation。对于低维数据,您应该使用 KDTreeSingleIndexParams

KDTreeSingleIndexParams 

当传递这种类型的对象时,索引将包含一个为搜索低维数据(例如 3D 点云)而优化的单个 kd 树,在您的情况下为 2D 点。您可以使用 leaf_max_size 参数并分析您的结果。

struct KDTreeSingleIndexParams : public IndexParams

    KDTreeSingleIndexParams( int leaf_max_size = 10 );
;

max leaf size: The maximum number of points to have in a leaf for not
branching the tree any more

【讨论】:

哇,太好了!我在文档中没有看到这一点,感谢您指出。我会试试的,很快就会更新给你。【参考方案2】:

O(log(N)) 并不一定意味着它比 O(N) 快。 这仅适用于足够大的 N。 你的 N 是一个相当小的数字。如果您的 kd-tree 包含数百万个元素,您可能会看到线性扫描和对数搜索之间的区别。

所以我的猜测是,您花费大量时间来处理诸如构建树之类的开销,对于小 N 来说,这比没有任何开销地扫描这个相当小的列表要慢。

【讨论】:

是的,你的意见是有道理的,我也考虑过。这就是我进行分析的原因,它表明瓶颈来自 kd-tree 查询,而不是来自构建 kd-tree。这让我很困惑!也感谢您的观点。 你说得对,我刚刚做了一个测试。建造树不是问题。但是假设您在 kd-tree 中有 100 个条目。现在假设,一个查询的 knnSearch() 只需要比对 100 个条目的线性搜索稍长的时间,因为它有点复杂。这将与您的查询量线性相加。现在,如果您在 kd-tree 中有 100000 个条目,则线性搜索对于 1 个查询可能较慢,因此对于许多查询。为了从 kd-tree 的对数搜索中获利,您必须在其中存储许多数据。 嗯,我也是这么想的。现在,我正在尝试另一种方法,我将图像分割成小网格,并在该点所属的网格内进行局部搜索。如果网格中没有点,我会在相邻网格中搜索。 @OhMyGosh 您通常可以使用二进制空间分区(如 KD-Trees 或 Quad-Trees),也可以使用空间散列。根据您的用例,它们都有不同的优点和缺点。

以上是关于C++ - 使用 opencv flann 查找最近的邻居的主要内容,如果未能解决你的问题,请参考以下文章

PCL中使用FLANN库

c++和opencv中的向量下标超出范围错误

OpenCV图像Surf与flann特征点(转载)

BF和FLANN特征匹配

opencv动态背景下运动目标检测 FAST+SURF+FLANN配准差分 17/12/13更新图片

OpenCV实现摄像机标定和像素转换,surf寻找特征点,FLANN匹配算子进行匹配