关键点匹配效果

一、数据集

二、目标搜索图像

三、源码:

#define _CRT_SECURE_NO_WARNINGS
// flann_search_dataset.cpp
// Naive program to search a query picture in a dataset illustrating usage of FLANN
// 在数据集中搜索查询图片 说明 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;//匹配数目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;}//数据集目录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结构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结构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//使用检测器检测关键点,计算包含数据集图像的文件夹中的描述符//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;//关键点Mat db_descriptors;///数据集描述子 矩阵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);//数据集图像索引向量std::vector<cv::String> files;//数据集文件路径 向量utils::fs::glob(db_path, cv::String(), files);for (std::vector<cv::String>::iterator itr = files.begin(); itr != files.end(); ++itr)//遍历数据集{Mat tmp_img = imread(*itr, IMREAD_GRAYSCALE);//读取灰度图像if (!tmp_img.empty()){std::vector<KeyPoint> kpts;Mat descriptors;detector->detectAndCompute(tmp_img, noArray(), kpts, descriptors);//检测每张数据集的关键点和计算其描述子db_keypoints.insert(db_keypoints.end(), kpts.begin(), kpts.end());//所有图像关键点在一个长向量中db_descriptors.push_back(descriptors);//添加到数据集描述子矩阵中。一行一个图像的描述子db_images_indice_range.push_back(db_images_indice_range.back()+ static_cast<unsigned int>(kpts.size()));//图像最后一个关键点索引}}//-- 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);//图象数-1for (int i = 0; i < nbr_of_imgs; ++i)//遍历所有数据集图像{const unsigned int first_indice = db_images_indice_range[i];//关键点向量的  起始索引const unsigned int last_indice = db_images_indice_range[i + 1];//关键点向量的 终止索引std::fill(db_indice_2_image_lut.begin() + first_indice,db_indice_2_image_lut.begin() + last_indice,i);//第i+1张图像的关键点  起、止索引之间   都填充为图像索引i。}//-- Step 2: 构建存储描述符的结构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())//保存数据集的flann最近邻索引类的文件路径非空index = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >(db_descriptors, cvflann::SavedIndexParams(load_db_path));//FLANN 最近邻索引类。此类使用为其构建索引的元素类型进行模板化。保存索引类。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);//保存数据集的flann最近邻索引类 //如果没有设置查询图像则返回 Return if no query image was setif (img_path == String())return 0;//-- 检测关键点并计算查询图像的描述符 Detect the keypoints and compute the descriptors for the query imagestd::vector<KeyPoint> img_keypoints;//453个关键点Mat img_descriptors;//453x32  每个关键点 32列detector->detectAndCompute(img, noArray(), img_keypoints, img_descriptors);//检测要搜索图像的关键点和计算描述子//-- 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//检索数据集中与查询图像匹配的描述符// knnSearch 通过不初始化空的 Mat 属性来不遵循 OpenCV 标准const int knn = 2;//从数据集中找两个最接近的图像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个关键点相近的点索引//--使用劳氏比率检验过滤匹配 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)//遍历每个关键点匹配到的k个近邻距离{if (dists.at<DIST_TYPE>(i, 0) < ratio_thresh * dists.at<DIST_TYPE>(i, 1))//第i个关键点  与第一近邻的距离 小于0.7*与第二近邻的距离{const int indice_in_db = indices.at<int>(i, 0);//获取第一近邻在数据集中的索引DMatch dmatch(i, indice_in_db, db_indice_2_image_lut[indice_in_db],static_cast<float>(dists.at<DIST_TYPE>(i, 0)));//创建匹配对象。  第i个关键点,与第i个关键点的第一近邻的匹配关键点 索引,匹配图像索引,与第一近邻距离good_matches.push_back(dmatch);//添加到匹配项集合中: 关键点,数据集中关键点的匹配对象,匹配图像索引,距离。  matches_per_img_histogram[db_indice_2_image_lut[indice_in_db]]++;//近邻图像中匹配的关键点总数。 k=2.所有只有两个有数值,其它都为0.}}//-- Step 4: 找到匹配比例最高的数据集图像find the dataset image with the highest proportion of matchesstd::multimap<float, img_info> images_infos;//每张图像匹配信息 集合:包含总关键点数与匹配点数比例  以及 数据集对应图像信息for (int i = 0; i < nbr_of_imgs; ++i)//遍历所有数据集图像{const unsigned int nbr_of_matches = matches_per_img_histogram[i];//数据集图像i与 搜索图像匹配的关键点数if (nbr_of_matches < 4) //单应性至少需要 4 个点  we need at leat 4 points for a homographycontinue;//匹配点数小于4  不考虑const unsigned int nbr_of_kpts = db_images_indice_range[i + 1] - db_images_indice_range[i];//第i+1张数据集图像的关键点数const float inverse_proportion_of_retrieved_kpts =static_cast<float>(nbr_of_kpts) / static_cast<float>(nbr_of_matches);//图像的关键点与匹配到的点数比例img_info info(i, nbr_of_matches);//创建图像匹配信息: 第i+1张图像,与搜索图像匹配点数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;//前面最好近邻的匹配信息比率/后一匹配信息的比率      历史最好匹配比率与当前项匹配比率的比值if (it->second.nbr_of_matches * (ratio * ratio) > best_img.nbr_of_matches)//后面的图像匹配点数*比例系数平方  超过历史最好匹配关键点数,认为后面的图像匹配更好{new_matches_proportion = it->first;//更新最佳匹配比例best_img = it->second;//更新最佳匹配图像信息}++it;}//-- Step 5: 过滤属于数据集最佳图像匹配的   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)//遍历所有匹配的关键点{if (itr->imgIdx == best_img.img_index)//匹配的关键点所属图像索引   与 最佳匹配图像的图像索引 一致filtered_good_matches.push_back(*itr);// 匹配的关键点 添加到集合中}//--从数据集中检索最佳图像匹配 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学习笔记:drawmatches函数的参数详解_视觉闫小亘的博客-CSDN博客_drawmatches函数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

【opencv450-samples】flann_search_dataset.cpp在数据集中搜索查询图片 说明 FLANN 使用的简单程序相关推荐

  1. OpenCV FLANN在数据集中搜索查询图片的实例(附完整代码)

    OpenCV FLANN在数据集中搜索查询图片的实例 OpenCV FLANN在数据集中搜索查询图片的实例 OpenCV FLANN在数据集中搜索查询图片的实例 #include <iostre ...

  2. 关于大数据统计大量股票开盘平均价和收盘价的简单程序程序

    1.需要统计的文件 2.单个文件的内容 3.程序内容 package com.test4; import java.io.IOException; import java.util.Iterator; ...

  3. 微信小程序之页面内搜索查询功能

    文章目录 一.效果演示 二.如何操作 三.项目demo地址 四.参考链接 一.效果演示 二.如何操作 具体操作可以参考博客原生微信小程序,搜索框(search)组件和微信小程序页面内搜索查询(无后台) ...

  4. 易宝典——玩转O365中的EXO服务 之四十 创建就地电子数据展示搜索

    就地电子数据展示是靠就地电子数据展示搜索,将符合条件(例如关键字.开始日期和结束日期.发件人地址和收件人地址以及邮件类型等.)的邮件搜索出来用于展示.因此,在使用就地电子数据展示之前,必须为其创建就地 ...

  5. 易宝典——玩转O365中的EXO服务 之四十二 导出就地电子数据展示搜索结果

    对于搜索结果,如果仅仅只能预览查看而不能提取,那么就无法将相关结果提供给第三方(如法院)作为证据或其它电子数据需求处理. 在Office 365的Exchange Online中,提供两种提取电子数据 ...

  6. 在所有数据库的所有数据中搜索关键字

    本例脚本旨在所有数据库的所有数据中搜索关键字,可将以下脚本直接拷贝使用,注意留意注解.本例的核心思路是用游标遍历所有数据库中所有表的所有列,用列名对关键字进行匹配,对匹配结果进行判断,然后只输出有关键 ...

  7. Django 前后端分离实战项目 生鲜超市(七)之Vue展示商品分类数据和搜索

    Vue展示商品分类数据和搜索 前言 所有vue接口全部在src/api/api.js文件下 代码已上传至github:https://github.com/kalipoison/fresh-marke ...

  8. 图像搜索引擎1|使用Python颜色直方图在数据集中搜索视觉上相似的图像

    图像搜索引擎1|使用Python颜色直方图在数据集中搜索视觉上相似的图像 这篇博客将介绍如何从头到尾创建图像搜索引擎.第一步是选择一个图像描述符--使用3D RGB直方图来表征图像的颜色.然后通过提取 ...

  9. php实现关键字搜索mysql数据_PHP实现多个关键词搜索查询功能示例

    本文实例讲述了PHP实现多个关键词搜索查询功能.分享给大家供大家参考,具体如下: PHP对于数据库的搜索主要通过使用SQL语句中的like子句来实现.如果同时搜索多个关键词,可以使用union子句来将 ...

最新文章

  1. DataRow判断列名是否存在
  2. Oracle undo表空间爆满的解决
  3. 微服务可靠性设计--转
  4. Chrome浏览器官方离线安装包下载
  5. SQL Server CONVERT() 函数,Date 函数
  6. SQL数据分析实战:好用的窗口函数
  7. python列表去重的方法_Python列表中去重的多种方法
  8. 一文读懂 .NET 中的高性能队列 Channel
  9. Linux(1) 目录结构
  10. Facebook开发实时分析控制面板
  11. vue父组件引用子组件方法显示undefined问题原因及解决方法
  12. jQuery 仿写京东轮播广告图
  13. 华为诺亚方舟 | 构建1亿组图文对中文多模态数据集
  14. solaris 10 oracle 11g r2安装教程,Oracle 11gR2 on Solaris 10安装技术文档(原版英文)
  15. 读书笔记——《灰度决策:如何处理复杂、棘手、高风险的难题》
  16. Vue怎么操作父元素、兄弟元素、子元素
  17. 几款强大的PPT制作辅助软件
  18. Tansformer | 详细解读:如何在CNN模型中插入Transformer后速度不变精度剧增?
  19. PHP文字转语音排号声音_百度文字转语音免费接口使用实例
  20. 已倒闭某科技公司前员工辟谣声明

热门文章

  1. [BJDCTF 2nd]假猪套天下第一
  2. android7.1root工具,Android模拟器Root,Android7.1.1
  3. android+第三发输入法控制,android输入法属性使用,软键盘隐藏、显示状态控制大揭秘...
  4. RNN结构有什么问题?LSTM解决了RNN什么问题?怎么解决的?
  5. plsql tables 里面不显示表格
  6. 什么网站适合高防服务器,什么叫高防服务器
  7. 趣图:新手 vs 老鸟
  8. 庖丁解牛式读《Attention is all your need》
  9. CSS的再深入2(更新中···)
  10. 《肖申克的救赎》到底救赎了什么?谁在救赎?