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