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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章