【opencv450-samples】flann_search_dataset.cpp在数据集中搜索查询图片 说明 FLANN 使用的简单程序
?關(guān)鍵點匹配效果
一、數(shù)據(jù)集
?
二、目標(biāo)搜索圖像
?三、源碼:
#define _CRT_SECURE_NO_WARNINGS // flann_search_dataset.cpp // Naive program to search a query picture in a dataset illustrating usage of FLANN // 在數(shù)據(jù)集中搜索查詢圖片 說明 FLANN 使用的簡單程序// #include <iostream> #include <vector> #include "opencv2/core.hpp" #include "opencv2/core/utils/filesystem.hpp" #include "opencv2/highgui.hpp" #include "opencv2/features2d.hpp" #include "opencv2/flann.hpp"using namespace cv; using std::cout; using std::endl;#define _ORB_const char* keys = "{ help h | | Print help message. }" "{ dataset |C:/Users/Zohar/Pictures/Test | Path to the images folder used as dataset. }" "{ image | C:/Users/Zohar/Pictures/pujing.bmp | Path to the image to search for in the dataset. }" "{ save | C:/Users/Zohar/Pictures/pujingflann.bmp | Path and filename where to save the flann structure to. }" "{ load | | Path and filename where to load the flann structure from. }";struct img_info {int img_index;//圖片索引unsigned int nbr_of_matches;//匹配數(shù)目img_info(int _img_index, unsigned int _nbr_of_matches): img_index(_img_index), nbr_of_matches(_nbr_of_matches){} };int main(int argc, char* argv[]) {//-- 測試程序選項CommandLineParser parser(argc, argv, keys);if (parser.has("help")){parser.printMessage();return -1;}//讀取要搜索的圖像const cv::String img_path = parser.get<String>("image");Mat img = imread(samples::findFile(img_path), IMREAD_GRAYSCALE);if (img.empty()){cout << "Could not open the image " << img_path << endl;return -1;}//數(shù)據(jù)集目錄const cv::String db_path = parser.get<String>("dataset");if (!utils::fs::isDirectory(db_path)){cout << "Dataset folder " << db_path.c_str() << " doesn't exist!" << endl;return -1;}//加載flann結(jié)構(gòu)const cv::String load_db_path = parser.get<String>("load");if ((load_db_path != String()) && (!utils::fs::exists(load_db_path))){cout << "File " << load_db_path.c_str()<< " where to load the flann structure from doesn't exist!" << endl;return -1;}//保存flann結(jié)構(gòu)const cv::String save_db_path = parser.get<String>("save");//-- Step 1: Detect the keypoints using a detector, compute the descriptors// in the folder containing the images of the dataset//使用檢測器檢測關(guān)鍵點,計算包含數(shù)據(jù)集圖像的文件夾中的描述符//SIFT \ ORB 特征 #ifdef _SIFT_int minHessian = 400;Ptr<Feature2D> detector = SIFT::create(minHessian); #elif defined(_ORB_)Ptr<Feature2D> detector = ORB::create(); #elsecout << "Missing or unknown defined descriptor. ""Only SIFT and ORB are currently interfaced here" << endl;return -1; #endifstd::vector<KeyPoint> db_keypoints;//關(guān)鍵點Mat db_descriptors;///數(shù)據(jù)集描述子 矩陣std::vector<unsigned int> db_images_indice_range; //存儲每個圖像的索引范圍store the range of indices per imagestd::vector<int> db_indice_2_image_lut; //將描述符索引與其圖像匹配 match descriptor indice to its imagedb_images_indice_range.push_back(0);//數(shù)據(jù)集圖像索引向量std::vector<cv::String> files;//數(shù)據(jù)集文件路徑 向量utils::fs::glob(db_path, cv::String(), files);for (std::vector<cv::String>::iterator itr = files.begin(); itr != files.end(); ++itr)//遍歷數(shù)據(jù)集{Mat tmp_img = imread(*itr, IMREAD_GRAYSCALE);//讀取灰度圖像if (!tmp_img.empty()){std::vector<KeyPoint> kpts;Mat descriptors;detector->detectAndCompute(tmp_img, noArray(), kpts, descriptors);//檢測每張數(shù)據(jù)集的關(guān)鍵點和計算其描述子db_keypoints.insert(db_keypoints.end(), kpts.begin(), kpts.end());//所有圖像關(guān)鍵點在一個長向量中db_descriptors.push_back(descriptors);//添加到數(shù)據(jù)集描述子矩陣中。一行一個圖像的描述子db_images_indice_range.push_back(db_images_indice_range.back()+ static_cast<unsigned int>(kpts.size()));//圖像最后一個關(guān)鍵點索引}}//-- Set the LUTdb_indice_2_image_lut.resize(db_images_indice_range.back());//const int nbr_of_imgs = static_cast<int>(db_images_indice_range.size() - 1);//圖象數(shù)-1for (int i = 0; i < nbr_of_imgs; ++i)//遍歷所有數(shù)據(jù)集圖像{const unsigned int first_indice = db_images_indice_range[i];//關(guān)鍵點向量的 起始索引const unsigned int last_indice = db_images_indice_range[i + 1];//關(guān)鍵點向量的 終止索引std::fill(db_indice_2_image_lut.begin() + first_indice,db_indice_2_image_lut.begin() + last_indice,i);//第i+1張圖像的關(guān)鍵點 起、止索引之間 都填充為圖像索引i。}//-- Step 2: 構(gòu)建存儲描述符的結(jié)構(gòu)build the structure storing the descriptors #if defined(_SIFT_)cv::Ptr<flann::GenericIndex<cvflann::L2<float> > > index;if (load_db_path != String())index = cv::makePtr<flann::GenericIndex<cvflann::L2<float> > >(db_descriptors,cvflann::SavedIndexParams(load_db_path));elseindex = cv::makePtr<flann::GenericIndex<cvflann::L2<float> > >(db_descriptors,cvflann::KDTreeIndexParams(4));#elif defined(_ORB_)// L1、L2、Hamming 等距離計算方法cv::Ptr<flann::GenericIndex<cvflann::Hamming<unsigned char> > > index;if (load_db_path != String())//保存數(shù)據(jù)集的flann最近鄰索引類的文件路徑非空index = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >(db_descriptors, cvflann::SavedIndexParams(load_db_path));//FLANN 最近鄰索引類。此類使用為其構(gòu)建索引的元素類型進行模板化。保存索引類。elseindex = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >(db_descriptors, cvflann::LshIndexParams());// #elsecout << "Descriptor not listed. Set the proper FLANN distance for this descriptor" << endl;return -1; #endifif (save_db_path != String())index->save(save_db_path);//保存數(shù)據(jù)集的flann最近鄰索引類 //如果沒有設(shè)置查詢圖像則返回 Return if no query image was setif (img_path == String())return 0;//-- 檢測關(guān)鍵點并計算查詢圖像的描述符 Detect the keypoints and compute the descriptors for the query imagestd::vector<KeyPoint> img_keypoints;//453個關(guān)鍵點Mat img_descriptors;//453x32 每個關(guān)鍵點 32列detector->detectAndCompute(img, noArray(), img_keypoints, img_descriptors);//檢測要搜索圖像的關(guān)鍵點和計算描述子//-- Step 3: retrieve the descriptors in the dataset matching the ones of the query image// /!\ knnSearch doesn't follow OpenCV standards by not initialising empty Mat properties//檢索數(shù)據(jù)集中與查詢圖像匹配的描述符// knnSearch 通過不初始化空的 Mat 屬性來不遵循 OpenCV 標(biāo)準const int knn = 2;//從數(shù)據(jù)集中找兩個最接近的圖像Mat indices(img_descriptors.rows, knn, CV_32S);//近鄰索引 矩陣 #if defined(_SIFT_) #define DIST_TYPE floatMat dists(img_descriptors.rows, knn, CV_32F); #elif defined(_ORB_) #define DIST_TYPE intMat dists(img_descriptors.rows, knn, CV_32S);//近鄰距離 矩陣 #endifindex->knnSearch(img_descriptors, indices, dists, knn, cvflann::SearchParams(32));//計算搜索圖像的k近鄰索引和距離 453x2 2:兩張相似圖像。 每個圖像中與搜索圖像的453個關(guān)鍵點相近的點索引//--使用勞氏比率檢驗過濾匹配 Filter matches using the Lowe's ratio testconst float ratio_thresh = 0.7f;//比率閾值std::vector<DMatch> good_matches; //匹配項 集合 containsstd::vector<unsigned int> matches_per_img_histogram(nbr_of_imgs, 0);//匹配直方圖for (int i = 0; i < dists.rows; ++i)//遍歷每個關(guān)鍵點匹配到的k個近鄰距離{if (dists.at<DIST_TYPE>(i, 0) < ratio_thresh * dists.at<DIST_TYPE>(i, 1))//第i個關(guān)鍵點 與第一近鄰的距離 小于0.7*與第二近鄰的距離{const int indice_in_db = indices.at<int>(i, 0);//獲取第一近鄰在數(shù)據(jù)集中的索引DMatch dmatch(i, indice_in_db, db_indice_2_image_lut[indice_in_db],static_cast<float>(dists.at<DIST_TYPE>(i, 0)));//創(chuàng)建匹配對象。 第i個關(guān)鍵點,與第i個關(guān)鍵點的第一近鄰的匹配關(guān)鍵點 索引,匹配圖像索引,與第一近鄰距離good_matches.push_back(dmatch);//添加到匹配項集合中: 關(guān)鍵點,數(shù)據(jù)集中關(guān)鍵點的匹配對象,匹配圖像索引,距離。 matches_per_img_histogram[db_indice_2_image_lut[indice_in_db]]++;//近鄰圖像中匹配的關(guān)鍵點總數(shù)。 k=2.所有只有兩個有數(shù)值,其它都為0.}}//-- Step 4: 找到匹配比例最高的數(shù)據(jù)集圖像find the dataset image with the highest proportion of matchesstd::multimap<float, img_info> images_infos;//每張圖像匹配信息 集合:包含總關(guān)鍵點數(shù)與匹配點數(shù)比例 以及 數(shù)據(jù)集對應(yīng)圖像信息for (int i = 0; i < nbr_of_imgs; ++i)//遍歷所有數(shù)據(jù)集圖像{const unsigned int nbr_of_matches = matches_per_img_histogram[i];//數(shù)據(jù)集圖像i與 搜索圖像匹配的關(guān)鍵點數(shù)if (nbr_of_matches < 4) //單應(yīng)性至少需要 4 個點 we need at leat 4 points for a homographycontinue;//匹配點數(shù)小于4 不考慮const unsigned int nbr_of_kpts = db_images_indice_range[i + 1] - db_images_indice_range[i];//第i+1張數(shù)據(jù)集圖像的關(guān)鍵點數(shù)const float inverse_proportion_of_retrieved_kpts =static_cast<float>(nbr_of_kpts) / static_cast<float>(nbr_of_matches);//圖像的關(guān)鍵點與匹配到的點數(shù)比例img_info info(i, nbr_of_matches);//創(chuàng)建圖像匹配信息: 第i+1張圖像,與搜索圖像匹配點數(shù)nbr_of_matchesimages_infos.insert(std::pair<float, img_info>(inverse_proportion_of_retrieved_kpts,info));//添加到近鄰圖像匹配信息集合中}if (images_infos.begin() == images_infos.end())//沒有匹配項{cout << "No good match could be found." << endl;//沒找到相似圖像return 0;}//-- if there are several images with a similar proportion of matches,// select the one with the highest number of matches weighted by the// squared ratio of proportions如果有幾張圖像的匹配比例相似,則選擇匹配比例最高的一張const float best_matches_proportion = images_infos.begin()->first;//取出第一張近鄰的匹配信息的比率信息float new_matches_proportion = best_matches_proportion;//初始化最佳比率img_info best_img = images_infos.begin()->second;//第一張近鄰的匹配信息的圖像信息std::multimap<float, img_info>::iterator it = images_infos.begin();++it;while ((it != images_infos.end()) && (it->first < 1.1 * best_matches_proportion))//遍歷匹配信息中所有項{const float ratio = new_matches_proportion / it->first;//前面最好近鄰的匹配信息比率/后一匹配信息的比率 歷史最好匹配比率與當(dāng)前項匹配比率的比值if (it->second.nbr_of_matches * (ratio * ratio) > best_img.nbr_of_matches)//后面的圖像匹配點數(shù)*比例系數(shù)平方 超過歷史最好匹配關(guān)鍵點數(shù),認為后面的圖像匹配更好{new_matches_proportion = it->first;//更新最佳匹配比例best_img = it->second;//更新最佳匹配圖像信息}++it;}//-- Step 5: 過濾屬于數(shù)據(jù)集最佳圖像匹配的 goodmatches filter goodmatches that belong to the best image match of the datasetstd::vector<DMatch> filtered_good_matches;for (std::vector<DMatch>::iterator itr(good_matches.begin()); itr != good_matches.end(); ++itr)//遍歷所有匹配的關(guān)鍵點{if (itr->imgIdx == best_img.img_index)//匹配的關(guān)鍵點所屬圖像索引 與 最佳匹配圖像的圖像索引 一致filtered_good_matches.push_back(*itr);// 匹配的關(guān)鍵點 添加到集合中}//--從數(shù)據(jù)集中檢索最佳圖像匹配 Retrieve the best image match from the datasetMat db_img = imread(files[best_img.img_index], IMREAD_GRAYSCALE);//最佳匹配圖像//--繪制匹配項 Draw matchesMat img_matches;//輸出圖像drawMatches(img, img_keypoints, db_img, db_keypoints, filtered_good_matches, img_matches, Scalar::all(-1),Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//繪制匹配對//--顯示檢測到的匹配項 Show detected matchesimshow("Good Matches", img_matches);waitKey();return 0; }四、gif 演示
?
參考:?
opencv feature2D模塊(二)_cshilin的博客-CSDN博客https://blog.csdn.net/cshilin/article/details/52107813?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165347471716780357230974%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165347471716780357230974&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-52107813-null-null.142^v10^pc_search_result_control_group,157^v12^new_style1&utm_term=Feature2D&spm=1018.2226.3001.4187
OpenCV使用 GenericIndex 進行 KNN 搜索_mightbxg的博客-CSDN博客https://blog.csdn.net/mightbxg/article/details/118338302?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165347465116782248516618%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165347465116782248516618&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-118338302-null-null.142^v10^pc_search_result_control_group,157^v12^new_style1&utm_term=GenericIndex&spm=1018.2226.3001.4187
opencv+flann庫+GenericIndex類_大王叫我來巡山228的博客-CSDN博客https://blog.csdn.net/weixin_40710375/article/details/80594960?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165347465116782248586689%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165347465116782248586689&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-80594960-null-null.142^v10^pc_search_result_control_group,157^v12^new_style1&utm_term=GenericIndex&spm=1018.2226.3001.4187
C++ map容器和multimap容器(STL map容器)_MagnumLu的博客-CSDN博客_c++ multimaphttps://blog.csdn.net/qq_28584889/article/details/83855734?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165347460916782184657270%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165347460916782184657270&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-83855734-null-null.142^v10^pc_search_result_control_group,157^v12^new_style1&utm_term=multimap&spm=1018.2226.3001.4187
OpenCV學(xué)習(xí)筆記:drawmatches函數(shù)的參數(shù)詳解_視覺閆小亙的博客-CSDN博客_drawmatches函數(shù)https://blog.csdn.net/two_ye/article/details/100576029?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165347450816782425119139%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165347450816782425119139&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-100576029-null-null.142^v10^pc_search_result_control_group,157^v12^new_style1&utm_term=drawMatches&spm=1018.2226.3001.4187
總結(jié)
以上是生活随笔為你收集整理的【opencv450-samples】flann_search_dataset.cpp在数据集中搜索查询图片 说明 FLANN 使用的简单程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gphp32.exe是什么文件?
- 下一篇: unity 物理碰撞