本⽂对前⾯的⼏篇⽂章进⾏个总结,实现⼀个⼩型的图像检索应⽤。⼀个⼩型的图像检索应⽤可以分为两部分:
train,构建图像集的特征数据库。
retrieval,检索,给定图像,从图像库中返回最类似的图像构建图像数据库的过程如下:
⽣成图像集的视觉词汇表(Vocabulary)提取图像集所有图像的sift特征
对得到的sifte特征集合进⾏聚类,聚类中⼼就是Vocabulary
对图像集中的图像重新编码表⽰,可使⽤BoW或者VLAD,这⾥选择VLAD.
将图像集中所有图像的VLAD表⽰组合到⼀起得到⼀个VLAD表,这就是查询图像的数据库。得到图像集的查询数据后,对任⼀图像查找其在数据库中的最相似图像的流程如下:
提取图像的sift特征
加载Vocabulary,使⽤VLAD表⽰图像
在图像数据库中查找与该VLAD最相似的向量
构建图像集的特征数据库的流程通常是offline的,查询的过程则需要是实时的,基本流程参见下图:
由两部分构成:offline的训练过程以及online的检索查找
各个功能模块的实现
下⾯就使⽤VLAD表⽰图像,实现⼀个⼩型的图像数据库的检索程序。下⾯实现需要的功能模块
特征点提取
构建Vocabulary构建数据库
第⼀步,特征点的提取
不管是BoW还是VLAD,都是基于图像的局部特征的,本⽂选择的局部特征是SIFT,使⽤其扩展RootSift。提取到稳定的特征点尤为的重要,本⽂使⽤OpenCV体哦那个的SiftDetecotr,实例化如下:
1auto fdetector = xfeatures2d::SIFT::create(0,3,0.2,10);create的声明如下:
1static Ptr 3 double contrastThreshold = 0.04,4 double edgeThreshold = 10,5 double sigma = 1.66 )nfeatures 设置提取到的特征点的个数,每个sift的特征点都根据其对⽐度(local contrast)计算出来⼀个分数。设置了该值后,会根据分数排序,只保留前nfeatures个返回 nOctaveLayers 每个octave中的层数,该值可以根据图像的分辨率⼤⼩计算出来。D.Lowe论⽂中该值为3contrastThreshold 过滤掉低对⽐度的不稳定特征点,该值越⼤,提取到的特征点越少edgeThreshold 过滤边缘处的特征点,该值越⼤,提取到的特征点就越多sigma ⾼斯滤波器的参数,该滤波器应⽤于第0个Octave个⼈的⼀些见解。 设置参数时,主要是设置contrastThreshold和edgeThreshold。contrastThreshold是过滤掉平滑区域的⼀些不稳定的特征点,edgeThreshold是过虑类似边缘的不稳定关键点。设置参数时,应尽量保证提取的特征点个数适中,不易过多,也不要过少。另外,contrastThreshold和edgeThreshold的平衡,应根据要提取的⽬标是⽐较平滑的区域还是纹理较多的区域,来平衡这两个参数的设置。 对于有些图像,可能设置的提取特征点的参数叫严格,提取特征点的个数过少,这时候可改变宽松⼀些的参数。1 auto fdetector = xfeatures2d::SIFT::create(0,3,0.2,10);2fdetector->detectAndCompute(img,noArray(),kpts,feature);3 4if(kpts.size() < 10){ 5 fdetector = xfeatures2d::SIFT::create(); 6 fdetector->detectAndCompute(img,noArray(),kpts,feature);7} 阈值10,可根据具体的情况进⾏调节。更多关于sift的内容可以参看⽂章: 图像检索(1): 再论SIFT-基于vlfeat实现 使⽤轻量级的视觉库vlfeat提取sift特征,其提取的特征觉得更稳定⼀些,但是使⽤上就不如OpenCV⽅便了。www.jb51.net/article/181945.htm关于RootSift和VLAD可以参考前⾯的⽂章 第⼆步,构建Vocabulary Vocabulary的构建过程,实际就是对提取到的图像特征点的聚类。⾸先提取图像库图像sift特征,并将其扩展为RootSift,然后对提取到的RootSift进⾏聚类得到Vocabulary。 这⾥创建class Vocabulary,主要以下⽅法: create 从提取到的特征点构建聚类得到视觉词汇表Vocabulary 1 2void Vocabulary::create(const std::vector 3 Mat f; 4 vconcat(features,f);5 vector 6 kmeans(f,k,labels,TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,100,0.01),3,cv::KMEANS_PP_CENTERS,m_voc);7 m_k = k;8} load和save,为了使⽤⽅便,需要能够将⽣成的视觉词汇表Vocabulary保存问⽂件(.yml)tranform_vlad,将输⼊的图像进⾏转换为vlad表⽰ 1 2void Vocabulary::transform_vlad(const cv::Mat &f,cv::Mat &vlad)3{ 4 // Find the nearest center 5 Ptr 6 matcher->match(f,m_voc,matches);7 // Compute vlad 8 Mat responseHist(m_voc.rows,f.cols,CV_32FC1,Scalar::all(0));9 for( size_t i = 0; i < matches.size(); i++ ){10 auto queryIdx = matches[i].queryIdx; 11 int trainIdx = matches[i].trainIdx; // cluster index12 Mat residual; 13 subtract(f.row(queryIdx),m_voc.row(trainIdx),residual,noArray()); add(responseHist.row(trainIdx),residual,responseHist.row(trainIdx),noArray(),responseHist.type());14 }15 16 // l2-norm 17 auto l2 = norm(responseHist,NORM_L2);18 responseHist /= l2; 19 //normalize(responseHist,responseHist,1,0,NORM_L2);20 21 //Mat vec(1,m_voc.rows * f.cols,CV_32FC1,Scalar::all(0)); 22 vlad = responseHist.reshape(0,1); // Reshape the matrix to 1 x (k*d) vector23}24class Vocabulary有以下⽅法: 从图像列表中构建视觉词汇表Vocabulary 将⽣成的Vocabulary保存到本地,并提供了load⽅法将图像表⽰为VLAD 第三步,创建图像数据库 图像数据库也就是将图像VLAD表⽰的集合,在该数据库检索时,返回与query图像相似的VLAD所对应的图像。 本⽂使⽤OpenCV提供的Mat构建⼀个简单的数据库,Mat保存所有图像的vlad向量组成的矩阵,在检索时,实际就是对该Mat的检索。声明类class Database,其具有以下功能: add 添加图像到数据库 save和load 将数据库保存为⽂件(.yml) retrieval 检索,对保存的vald向量的Mat创建索引,返回最相似的结果。 第四步,Trainer 在上⾯实现了特征点的提取,构建视觉词汇表,构建图像表⽰为VLAD的数据库,这⾥将其组合到⼀起,创建Trainer类,⽅便训练使⽤。12 3class Trainer{4 5public:6 7 Trainer(); ~Trainer();8 9 Trainer(int k,int pcaDim,const std::string &imageFolder, 10 const std::string &path,const std::string &identifiery,std::shared_ptr 12 void createVocabulary();13 void createDb();14 15 void save();16 17private: 18 int m_k; // The size of vocabulary19 int m_pcaDimension; // The retrain dimensions after pca20 21 Vocabulary* m_voc;22 Database* m_db;23 24 private:25 26 /* 27 Image folder28 */ 29 std::string m_imageFolder;30 /*31 training result identifier,the name suffix of vocabulary and database32 voc-identifier.yml,db-identifier.yml33 */ 34 std::string m_identifier;35 36 /* The location of training result37 */ 38 std::string m_resultPath;39};4041使⽤Trainer 需要配置 图像集所在的⽬录视觉 词汇表的⼤⼩(聚类中⼼的个数) PCA后VLAD保留的维度,可先不管设置为0,不进⾏PCA训练后数据的保存路径。 训练后的数据保存为yml形式,命名规则是voc-m_identifier.yml和db-m_identifier.yml。 为了⽅便测试不同参数的数据,这⾥设置⼀个后缀参数m_identifier,来区分不同的参数的训练数据。其使⽤代码如下: 1 2int main(int argc, char *argv[])3{ const string image_200 = \"/home/test/images-1\";4 const string image_6k = \"/home/test/images/sync_down_1\";5 6 auto detector = make_shared Trainer trainer(,0,image_200,\"/home/test/projects/imageRetrievalService/build\8 9 trainer.createVocabulary();10 trainer.createDb(); 11 12 trainer.save();13 14 return 0;15} 偷懒,没有配置为参数,使⽤时需要设置好图像的路径,以及训练后数据的保存数据。 第五步,Searcher 在Database中,已经实现了retrieval的⽅法。 这⾥之所以再封装⼀层,是为了更好的契合业务上的⼀些需求。⽐如,图像的⼀些预处理,分块,多线程处理,查询结果的过滤等等。关于Searcher和具体的应⽤耦合⽐较深,这⾥只是简单的实现了个retrieval⽅法和查询参数的配置。1 2class Searcher{3 4public: Searcher();5 ~Searcher();6 7 void init(int keyPointThreshold); 8 void setDatabase(std::shared_ptr 10 void retrieval(cv::Mat &query,const std::string &group,std::string &md5,double &score);11 12 void retrieval(std::vector private:14 int m_keyPointThreshold;15 16 std::shared_ptr Vocabulary voc;2 3 stringstream ss; 4 ss << path << \"/voc-\" << identifier << \".yml\";5 6 cout << \"Load vocabulary from \" << ss.str() << endl;7 voc.load(ss.str());8 9 cout << \"Load vocabulary successful.\" << endl;10 11 auto detector = make_shared 13 auto db = make_shared 15 cout << \"Load database from \" << path << \"/db-\" << identifier << \".yml\" << endl;16 db->load1(path,identifier);17 db->setVocabulary(voc); 18 cout << \"Load database successful.\" << endl;19 Searcher s;20 s.init(10); 21 s.setDatabase(db);22 Summary 上图来总结下整个流程 创建Vocabulary创建Database Search Similary list 到此这篇关于基于OpenCV实现⼩型的图像数据库检索的⽂章就介绍到这了,更多相关OpenCV图像数据库检索内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持! 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuowenda.com 版权所有 湘ICP备2023022495号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务