Opencv+Kdtree在C++和Python下的使用
最近有个项目使用到Opencv下的flann模块,该模块主要在数据的搜索,搜索速度还算可以,就研究它在C++和Python下的使用方法,关于kdtree的搜索原理,请自行百度.
Kdtree主要有Tree的加载树和建立树,其中建树主要的场景为:数据第一次初始化及更新;加载则为程序重启时,不需要读取数据重新建树,而节省了时间.
C++下
建树
cv::flann::Index Kdtree;
Kdtree.build(Input_Mat,cv::flann::KDTreeIndexParams(1),cvflann::FLANN_DIST_EUCLIDEAN);
其中:
cv::flann::KDTreeIndexParams(1)为建立树的个数,这里选择树的个数为1,源码中默认的为4棵树.需要注意的是,如果建立的树越多,则消耗的时间越长.
cvflann::FLANN_DIST_EUCLIDEAN:建树时使用欧式距离,Opencv支持的距离类型有限.支持的数据类型如下:
FLANN_DIST_EUCLIDEAN = 1,
FLANN_DIST_L2 = 1,
FLANN_DIST_MANHATTAN = 2,
FLANN_DIST_L1 = 2,
FLANN_DIST_MINKOWSKI = 3,
FLANN_DIST_MAX = 4,
FLANN_DIST_HIST_INTERSECT = 5,
FLANN_DIST_HELLINGER = 6,
FLANN_DIST_CHI_SQUARE = 7,
FLANN_DIST_CS = 7,
FLANN_DIST_KULLBACK_LEIBLER = 8,
FLANN_DIST_KL = 8,
FLANN_DIST_HAMMING = 9,
Kdtree建立完毕后,为了以后使用方便,可以将树的模型保存到本地.
Kdtree.save("tree.mat");
cv::FileStorage storage("tree.yml", cv::FileStorage::WRITE);
storage << "kdtree" << Input_Mat;
storage.release();
将树的模型及输入矩阵保存到本地后,如果后续Input_Mat的数据没有改变,当程序再次启动时,就可以直接从本地加载,加载通过load()函数.
加载树
首先,看一下load()函数的原型:
load(InputArray features, const String& filename);
对应的使用为:
cv::FileStorage fs("tree.yml", cv::FileStorage::READ);
fs["kdtree"] >> ClusterCenters;
fs.release();
Kdtree.load(ClusterCenters,"tree.mat");
搜索
前面的建树或加载树,都是为了搜索时使用的,搜索通过knnSearch
void knnSearch(InputArray query, OutputArray indices,
OutputArray dists, int knn, const SearchParams& params=SearchParams());
query:要查询的矩阵,它的数据类型必须与Input_Mat的类型一致.
indices:输出的结果的索引
dists:输出结果对应的距离值,该值为欧式距离的平方.
knn:索引的top N
knn:索引的top N
SearchParams:搜索的节点树,如果值为-1,则在树的所有节点进行搜索.
Kdtree.knnSearch(ClusterMembers, matches, distances, 1, cv::flann::SearchParams(-1));
至此,C++下的Kdtree的建立,加载,搜索完毕,是不是感觉很简单.python下的流程与C++下的是一致的.
Python
kdtree=cv2.flann.Index()//定义flann
input_mat=np.float32(np.random.randn(10,4))//输入矩阵
search_mat=np.float32(np.random.randn(1,4))//检索矩阵
FLANN_INDEX_KDTREE=1//使用kdtree,使用不同的算法如下
其它的算法定义如下:
FLANN_INDEX_LINEAR = 0,
FLANN_INDEX_KDTREE = 1,
FLANN_INDEX_KMEANS = 2,
FLANN_INDEX_COMPOSITE = 3,
FLANN_INDEX_KDTREE_SINGLE = 4,
FLANN_INDEX_HIERARCHICAL = 5,
FLANN_INDEX_LSH = 6,
FLANN_INDEX_SAVED = 254,
FLANN_INDEX_AUTOTUNED = 255,
#从本地加载模型
params=dict(algorithm=FLANN_INDEX_KDTREE,trees=1)
def read_yml():
fs = cv2.FileStorage("tree.yml", cv2.FILE_STORAGE_READ)
fn = fs.getNode("tree")
print(fn.mat())
#建树
kdtree.build(input_mat,params)
print("build tree success")
#检索
indices, dists=kdtree.knnSearch(search_mat,2,params=-1)
print("search kdtree")
#输出检索结果
print(indices)
print(np.sqrt(dists))
#使用numpy下的欧式距离验证结果是否正确
print((np.linalg.norm(input_mat-search_mat,axis=1)))
#模型
保存到本地
f=cv2.FileStorage("tree.yml",cv2.FILE_STORAGE_WRITE)
f.write("tree",input_mat)
f.release()