opencv kdtree的用法

求解如下红色点的3个最近邻居
在这里插入图片描述

1、测试代码

int main() {
	//用于构造kdtree的点集
	vector<cv::Point2f> features = { { 1,1 },{ 2, 2},{ 3, 3},{ 4, 4},{ 2, 4} };
	cv::Mat source = cv::Mat(features).reshape(1);
	source.convertTo(source, CV_32F);

	cv::flann::KDTreeIndexParams indexParams(2);
	cv::flann::Index kdtree(source, indexParams); 

	//预设knnSearch所需参数及容器
	int queryNum = 3;//用于设置返回邻近点的个数
	vector<float> vecQuery(2);//存放查询点的容器
	vector<int> vecIndex(queryNum);//存放返回的点索引
	vector<float> vecDist(queryNum);//存放距离
	cv::flann::SearchParams params(32);//设置knnSearch搜索参数

	//KD树knn查询
	vecQuery = { 3, 4};
	kdtree.knnSearch(vecQuery, vecIndex, vecDist, queryNum, params);

	cout << "vecDist: " << endl;
	for (auto&x : vecDist)
		cout << x << " ";
	cout << endl;

	cout << "vecIndex: " << endl;
	for (auto&x : vecIndex)
		cout << x << " ";

	return 0;
}

输出:
vecDist: (注意这里是距离的平方)
1 1 1
vecIndex:
2 3 4

2、可能的问题

我写程序需要构建多个kdtree, 我试图用vector存储多个kdtree,我写成如下的代码就会报错

int main(){
	vector<cv::flann::Index> kdtrees;

	vector<cv::Point2f> features = { { 1,1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 2, 4 } };
	cv::Mat source = cv::Mat(features).reshape(1);
	cout << source;
	source.convertTo(source, CV_32F);

	cv::flann::KDTreeIndexParams indexParams(2);
	cv::flann::Index kdtree(source, indexParams);

	kdtrees.push_back(kdtree);
}

报错为:
在这里插入图片描述
这里显示的应该是vector释放的时候出了问题,初步查明 cv::flann::Index 这个class有一个 指针类型:void* index. 很有可能是浅拷贝的时候,释放kdtree的时候将 index释放,然后在释放vector的是时候再次释放index出了问题。

protected:
    cvflann::flann_distance_t distType;
    cvflann::flann_algorithm_t algo;
    int featureType;
    void* index;

因此,这里直接改为指针形式即可。

int main(){
	vector<cv::flann::Index*> kdtrees;

	vector<cv::Point2f> features = { { 1,1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 2, 4 } };
	cv::Mat source = cv::Mat(features).reshape(1);
	cout << source;
	source.convertTo(source, CV_32F);

	cv::flann::KDTreeIndexParams indexParams(2);
	cv::flann::Index* pkdtree = new cv::flann::Index(source, indexParams);

	kdtrees.push_back(pkdtree);
}

3、knnsearch返回无穷大的值

当我写成如下形式的时候(注意kdtrees后面加了局部作用域)

int main() {
	int queryNum = 3;//用于设置返回邻近点的个数
	vector<float> vecQuery(2);//存放查询点的容器
	vector<int> vecIndex(queryNum);//存放返回的点索引
	vector<float> vecDist(queryNum);//存放距离
	cv::flann::SearchParams params(32);//设置knnSearch搜索参数
	cv::flann::KDTreeIndexParams indexParams(2);
									   
	vecQuery[0] = 3, vecQuery[1] = 4;


	vector<cv::flann::Index*> kdtrees;
	{
		vector<cv::Vec2d> features = { { 1,1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 2, 4 } };
		cv::Mat source = cv::Mat(features).reshape(1);
		source.convertTo(source, CV_32F);
		cv::flann::Index* kdtree = new cv::flann::Index(source, indexParams);
		kdtrees.push_back(kdtree);
	}

	kdtrees[0]->knnSearch(vecQuery, vecIndex, vecDist, queryNum, params);
	for (int i = 0; i < vecIndex.size(); i++)
		cout << "nearest id: " << vecIndex[i] << "\tdist:" << vecDist[i] << endl;

	return 0;
}

输出结果为:

nearest id: 2 dist:7.98718e+36
nearest id: 3 dist:7.98718e+36
nearest id: 4 dist:7.98718e+36

为何是这么大的值?调查发现构建kdtree使用的 cv::Mat source是局部变量,被释放后,kdtree被破坏,于是把cv::Mat source定义为全局变量即可。 最主要的问题是 opencv的矩阵如果是浅拷贝的话,有一个引用计数的问题,如果引用计数为0,那么数据会被释放。

int main() {
	int queryNum = 3;//用于设置返回邻近点的个数
	vector<float> vecQuery(2);//存放查询点的容器
	vector<int> vecIndex(queryNum);//存放返回的点索引
	vector<float> vecDist(queryNum);//存放距离
	cv::flann::SearchParams params(32);//设置knnSearch搜索参数
	cv::flann::KDTreeIndexParams indexParams(2);
									   
	vecQuery[0] = 3, vecQuery[1] = 4;

	cv::Mat source;
	vector<cv::flann::Index*> kdtrees;
	{
		vector<cv::Vec2d> features = { { 1,1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 2, 4 } };
		source = cv::Mat(features).reshape(1);
		source.convertTo(source, CV_32F);
		cv::flann::Index* kdtree = new cv::flann::Index(source, indexParams);
		kdtrees.push_back(kdtree);
	}

	kdtrees[0]->knnSearch(vecQuery, vecIndex, vecDist, queryNum, params);
	for (int i = 0; i < vecIndex.size(); i++)
		cout << "nearest id: " << vecIndex[i] << "\tdist:" << vecDist[i] << endl;

	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章