2024-09-01
C/C++
00

一开始想使用nmslib的,conan中引用后发现nmslib的C++示例代码太难写了,改为使用flann去实现,opencv中已纳入了flann,使用起来更方便了。

下面这段代码实现了使用 KD-Tree 算法进行 K 近邻搜索的函数 knn_nmslib,其中调用了 normalizeFeatures 函数用于对特征向量进行标准化处理。最终目的是计算每个特征的k近邻是谁,余弦距离是多少。

具体来说,该函数输入一个特征向量集合 feats,输出每个特征向量的前 k 个最近邻索引和对应的距离,存储在一个 vector<vector> 和一个 vector<vector> 中,分别表示索引和距离。

在函数中,首先复制一份输入特征向量 feats 到 feats_copy,然后对 feats_copy 中的每个特征向量进行标准化处理,即将其 L2 范数归一化为 1。标准化后,将 feats_copy 转换为一个 cv::Mat 对象 features,作为 K 近邻搜索的数据集。

接着,使用 cv::flann::Index 创建一个 KD-Tree 索引,使用欧几里得距离作为距离度量。然后,将 features 作为查询数据进行 K 近邻搜索,返回每个查询数据的前 k 个最近邻的索引和距离,存储在 cv::Mat 对象 indices 和 dists 中。

最后,对于每个查询数据,将其前 k 个最近邻的索引和距离存储到 neighbours 中,同时计算每个最近邻之间的余弦距离,并保存到 distance22 中,方便后续的计算。函数最后释放 KD-Tree 索引的内存。

c
#include <opencv2/opencv.hpp> #include <opencv2/flann.hpp> #include <utility> float findCosineSimilarity(const std::vector<float>& source_representation, const std::vector<float>& test_representation) { return 1.0f - findCosineDistance(source_representation, test_representation); } float findCosineDistance(const std::vector<float>& source_representation, const std::vector<float>& test_representation) { cv::Mat source(source_representation); cv::Mat test(test_representation); double dist = 1.0 - source.dot(test) / (cv::norm(source) * cv::norm(test, cv::NORM_L2) + 0.000001); return static_cast<float>(dist); } void normalizeFeatures(std::vector<std::vector<float>>& features) { for (auto& feature : features) { cv::Mat mat(feature); double norm = cv::norm(mat, cv::NORM_L2); mat = mat / norm; for (int i = 0; i < mat.cols; ++i) { feature[i] = mat.at<float>(0, i); } } } void knn_nmslib(const std::vector<std::vector<float>>& feats, std::vector<std::pair<std::vector<int>, std::vector<float>>>& neighbours, int k) { // 复制一份feats出来 std::vector<std::vector<float>> feats_copy(feats); // 标准化 normalizeFeatures(feats_copy); cv::Mat features(feats_copy.size(), feats_copy[0].size(), CV_32F); features.forEach<float>( [&](float& pixel, const int* position) -> void { pixel = feats_copy[position[0]][position[1]]; }); // 设置搜索算法为KD-Tree,使用欧几里得距离 cv::flann::Index flannIndex(features, cv::flann::KDTreeIndexParams(4), cvflann::FLANN_DIST_EUCLIDEAN); // 设置查询数据 cv::Mat query = features; // 进行k近邻搜索 cv::Mat indices(query.rows, k, CV_32S); cv::Mat dists(query.rows, k, CV_32F); flannIndex.knnSearch(query, indices, dists, k, cv::flann::SearchParams(256)); std::map<std::pair<int, int>, float> distance22; float dist_tmp; for (int i = 0; i < query.rows; i++) { std::vector<int> index; std::vector<float> dist; for (int j = 0; j < k; j++) { index.push_back(indices.at<int>(i, j)); // 查询distance22中是否存在(i,j) if (distance22.find(std::make_pair(i, indices.at<int>(i, j))) == distance22.end()) { dist_tmp = findCosineDistance(feats[i], feats[indices.at<int>(i, j)]); distance22[std::make_pair(i, indices.at<int>(i, j))] = dist_tmp; distance22[std::make_pair(indices.at<int>(i, j), i)] = dist_tmp; } else { dist_tmp = distance22[std::make_pair(i, indices.at<int>(i, j))]; } dist.push_back(dist_tmp); // 距离保存 } neighbours.push_back(std::make_pair(index, dist)); } //释放内存 flannIndex.release(); }
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Dong

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!