求解如下紅色點的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;
}