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

C++ - Finding nearest neighbor using opencv flann

本文关键字:邻居 最近 查找 使用 opencv-frann C++-      更新时间:2023-10-16

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

  1. 线性搜索:对于集合A中的每个点,只需扫描集合B中的所有点即可找到最近的点。

  2. 使用opencv-kd树:

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

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

    _然后,对于集合A中的每个点,我进行查询以找到最近的邻居:

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

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

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

  1. 线性搜索:O(M*N)

  2. Kd树:NlogN+MlogN=>第一项用于构建Kd树,第二项用于查询

其中M是集合A中的点数,N是集合B的点数。N的范围为100~1000,M的范围为10000~100000。

因此,kd树应该比线性搜索方法运行得更快。然而,当我在笔记本电脑上进行真正的测试时,结果是kd-tree方法比线性搜索慢(0.02~0.03s vs 0.4~0.5s)

当我进行评测时,我在knnSearch()函数上获得了热点,它占用了20.3%的CPU时间,而线性搜索占用了7.9%。

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

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

摘自Flann文档。对于低维数据,应使用KDTreeSingleIndexParams

KDTreeSingleIndexParams 

当传递这种类型的对象时,索引将包含一个为搜索低维数据(例如3D点云)而优化的kd树,在您的情况下是2D点。您可以使用leap_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

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

所以我的猜测是,你花了很多时间在开销上,比如构建树,对于小N来说,这比在没有任何开销的情况下扫描这个相当小的列表要慢。