基于OpenCV实现小型的图像数据库检索功能

本文对前面的几篇文章进行个总结,实现一个小型的图像检索应用。

一个小型的图像检索应用可以分为两部分:

  • 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,实例化如下:

auto fdetector = xfeatures2d::SIFT::create(0,3,0.2,10);

create的声明如下:

static Ptr<SIFT> cv::xfeatures2d::SIFT::create     (     int      nfeatures = 0,
        int      nOctaveLayers = 3,
        double      contrastThreshold = 0.04,
        double      edgeThreshold = 10,
        double      sigma = 1.6
    )
  • nfeatures 设置提取到的特征点的个数,每个sift的特征点都根据其对比度(local contrast)计算出来一个分数。设置了该值后,会根据分数排序,只保留前nfeatures个返回
  • nOctaveLayers 每个octave中的层数,该值可以根据图像的分辨率大小计算出来。D.Lowe论文中该值为3
  • contrastThreshold 过滤掉低对比度的不稳定特征点,该值越大,提取到的特征点越少
  • edgeThreshold 过滤边缘处的特征点,该值越大,提取到的特征点就越多
  • sigma 高斯滤波器的参数,该滤波器应用于第0个Octave

个人的一些见解。

设置参数时,主要是设置contrastThresholdedgeThresholdcontrastThreshold是过滤掉平滑区域的一些不稳定的特征点,edgeThreshold是过虑类似边缘的不稳定关键点。设置参数时,应尽量保证提取的特征点个数适中,不易过多,也不要过少。另外,contrastThresholdedgeThreshold的平衡,应根据要提取的目标是比较平滑的区域还是纹理较多的区域,来平衡这两个参数的设置。

对于有些图像,可能设置的提取特征点的参数叫严格,提取特征点的个数过少,这时候可改变宽松一些的参数。

auto fdetector = xfeatures2d::SIFT::create(0,3,0.2,10);
fdetector->detectAndCompute(img,noArray(),kpts,feature);

if(kpts.size() < 10){
    fdetector = xfeatures2d::SIFT::create();
    fdetector->detectAndCompute(img,noArray(),kpts,feature);
}

阈值10,可根据具体的情况进行调节。

更多关于sift的内容可以参看文章:

关于RootSift和VLAD可以参考前面的文章图像检索(4):IF-IDF,RootSift,VLAD

第二步,构建Vocabulary

Vocabulary的构建过程,实际就是对提取到的图像特征点的聚类。首先提取图像库图像sift特征,并将其扩展为RootSift,然后对提取到的RootSift进行聚类得到Vocabulary。
这里创建class Vocabulary,主要以下方法:

create 从提取到的特征点构建聚类得到视觉词汇表Vocabulary

void Vocabulary::create(const std::vector<cv::Mat> &features,int k)
{
    Mat f;
    vconcat(features,f);
    vector<int> labels;
    kmeans(f,k,labels,TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,100,0.01),3,cv::KMEANS_PP_CENTERS,m_voc);
    m_k = k;
}
  • loadsave,为了使用方便,需要能够将生成的视觉词汇表Vocabulary保存问文件(.yml)
  • tranform_vlad,将输入的图像进行转换为vlad表示
void Vocabulary::transform_vlad(const cv::Mat &f,cv::Mat &vlad)
{
    // Find the nearest center
    Ptr<FlannBasedMatcher> matcher = FlannBasedMatcher::create();
    vector<DMatch> matches;
    matcher->match(f,m_voc,matches);
    // Compute vlad
    Mat responseHist(m_voc.rows,f.cols,CV_32FC1,Scalar::all(0));
    for( size_t i = 0; i < matches.size(); i++ ){
        auto queryIdx = matches[i].queryIdx;
        int trainIdx = matches[i].trainIdx; // cluster index
        Mat residual;
        subtract(f.row(queryIdx),m_voc.row(trainIdx),residual,noArray());
        add(responseHist.row(trainIdx),residual,responseHist.row(trainIdx),noArray(),responseHist.type());
    }

    // l2-norm
    auto l2 = norm(responseHist,NORM_L2);
    responseHist /= l2;
    //normalize(responseHist,responseHist,1,0,NORM_L2);

    //Mat vec(1,m_voc.rows * f.cols,CV_32FC1,Scalar::all(0));
    vlad = responseHist.reshape(0,1); // Reshape the matrix to 1 x (k*d) vector
}

class Vocabulary有以下方法:

  • 从图像列表中构建视觉词汇表Vocabulary
  • 将生成的Vocabulary保存到本地,并提供了load方法
  • 将图像表示为VLAD

第三步,创建图像数据库

图像数据库也就是将图像VLAD表示的集合,在该数据库检索时,返回与query图像相似的VLAD所对应的图像。
本文使用OpenCV提供的Mat构建一个简单的数据库,Mat保存所有图像的vlad向量组成的矩阵,在检索时,实际就是对该Mat的检索。
声明类class Database,其具有以下功能:

  • add 添加图像到数据库
  • saveload 将数据库保存为文件(.yml)
  • retrieval 检索,对保存的vald向量的Mat创建索引,返回最相似的结果。

第四步,Trainer

在上面实现了特征点的提取,构建视觉词汇表,构建图像表示为VLAD的数据库,这里将其组合到一起,创建Trainer类,方便训练使用。

class Trainer{

public:

    Trainer();
    ~Trainer();

    Trainer(int k,int pcaDim,const std::string &imageFolder,
        const std::string &path,const std::string &identifiery,std::shared_ptr<RootSiftDetector> detector);

    void createVocabulary();
    void createDb();

    void save();

private:

    int m_k; // The size of vocabulary
    int m_pcaDimension; // The retrain dimensions after pca

    Vocabulary* m_voc;
    Database* m_db;

private:

    /*
        Image folder
    */
    std::string m_imageFolder;

    /*
        training result identifier,the name suffix of vocabulary and database
        voc-identifier.yml,db-identifier.yml
    */
    std::string m_identifier;

    /*
        The location of training result
    */
    std::string m_resultPath;
};

使用Trainer 需要配置

  • 图像集所在的目录视觉
  • 词汇表的大小(聚类中心的个数)
  • PCA后VLAD保留的维度,可先不管设置为0,不进行PCA训练后数据的保存路径。
  • 训练后的数据保存为yml形式,命名规则是voc-m_identifier.ymldb-m_identifier.yml。 为了方便测试不同参数的数据,这里设置一个后缀参数m_identifier,来区分不同的参数的训练数据。

其使用代码如下:

int main(int argc, char *argv[])
{
    const string image_200 = "/home/test/images-1";
    const string image_6k = "/home/test/images/sync_down_1";

    auto detector = make_shared<RootSiftDetector>(5,5,10);
    Trainer trainer(64,0,image_200,"/home/test/projects/imageRetrievalService/build","test-200-vl-64",detector);

    trainer.createVocabulary();
    trainer.createDb();

    trainer.save();

    return 0;
}

偷懒,没有配置为参数,使用时需要设置好图像的路径,以及训练后数据的保存数据。

第五步,Searcher

Database中,已经实现了retrieval的方法。 这里之所以再封装一层,是为了更好的契合业务上的一些需求。比如,图像的一些预处理,分块,多线程处理,查询结果的过滤等等。关于Searcher和具体的应用耦合比较深,这里只是简单的实现了个retrieval方法和查询参数的配置。

class Searcher{

public:
    Searcher();
    ~Searcher();

    void init(int keyPointThreshold);
    void setDatabase(std::shared_ptr<Database> db);

    void retrieval(cv::Mat &query,const std::string &group,std::string &md5,double &score);

    void retrieval(std::vector<char> bins,const std::string &group,std::string &md5,double &score);

private:
    int m_keyPointThreshold;

    std::shared_ptr<Database> m_db;
};

使用也很简单了,从文件中加载VaocabularyDatabase,设置Searcher的参数。

Vocabulary voc;

    stringstream ss;
    ss << path << "/voc-" << identifier << ".yml";

    cout << "Load vocabulary from " << ss.str() << endl;
    voc.load(ss.str());

    cout << "Load vocabulary successful." << endl;

    auto detector = make_shared<RootSiftDetector>(5,0.2,10);

    auto db = make_shared<Database>(detector);

    cout << "Load database from " << path << "/db-" << identifier << ".yml" << endl;
    db->load1(path,identifier);
    db->setVocabulary(voc);
    cout << "Load database successful." << endl;

     Searcher s;
    s.init(10);
    s.setDatabase(db);

Summary

上图来总结下整个流程

  • 创建Vocabulary
  • 创建Database
  • Search Similary list

到此这篇关于基于OpenCV实现小型的图像数据库检索的文章就介绍到这了,更多相关OpenCV图像数据库检索内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python实现图像检索的三种(直方图/OpenCV/哈希法)

    简介: 本文介绍了图像检索的三种实现方式,均用python完成,其中前两种基于直方图比较,哈希法基于像素分布. 检索方式是:提前导入图片库作为检索范围,给出待检索的图片,将其与图片库中的图片进行比较,得出所有相似度后进行排序,从而检索结果为相似度由高到低的图片.由于工程中还包含Qt界面类.触发函数等其他部分,在该文档中只给出关键函数的代码. 开发系统:MacOS 实现方式:Qt + Python 方法一:自定义的直方图比较算法 a) 基本思路 遍历图片像素点,提取R\G\B值并进行对应的计数,得

  • opencv3/C++ PHash算法图像检索详解

    PHash算法即感知哈希算法/Perceptual Hash algorithm,计算基于低频的均值哈希.对每张图像生成一个指纹字符串,通过对该字符串比较可以判断图像间的相似度. PHash算法原理 将图像转为灰度图,然后将图片大小调整为32*32像素并通过DCT变换,取左上角的8*8像素区域.然后计算这64个像素的灰度值的均值.将每个像素的灰度值与均值对比,大于均值记为1,小于均值记为0,得到64位哈希值. PHash算法实现 将图片转为灰度值 将图片尺寸缩小为32*32 resize(src

  • 基于OpenCV实现小型的图像数据库检索功能

    本文对前面的几篇文章进行个总结,实现一个小型的图像检索应用. 一个小型的图像检索应用可以分为两部分: train,构建图像集的特征数据库. retrieval,检索,给定图像,从图像库中返回最类似的图像 构建图像数据库的过程如下: 生成图像集的视觉词汇表(Vocabulary) 提取图像集所有图像的sift特征 对得到的sifte特征集合进行聚类,聚类中心就是Vocabulary 对图像集中的图像重新编码表示,可使用BoW或者VLAD,这里选择VLAD. 将图像集中所有图像的VLAD表示组合到一

  • Python基于OpenCV库Adaboost实现人脸识别功能详解

    本文实例讲述了Python基于OpenCV库Adaboost实现人脸识别功能.分享给大家供大家参考,具体如下: 以前用Matlab写神经网络的面部眼镜识别算法,研究算法逻辑,采集大量训练数据,迭代,计算各感知器的系数...相当之麻烦~而现在运用调用pythonOpenCV库Adaboost算法,无需知道算法逻辑,无需进行模型训练,人脸识别变得相当之简单了. 需要用到的库是opencv(open source computer vision),下载安装方式如下: 使用pip install num

  • Python基于opencv实现的简单画板功能示例

    本文实例讲述了Python基于opencv实现的简单画板功能.分享给大家供大家参考,具体如下: import cv2 import numpy as np drawing = False # true if mouse is pressed ix,iy = -1,-1 def nothing(x): pass # mouse callback function def draw_circle(event,x,y,flags,param): global ix,iy,drawing g = par

  • 基于OpenCv与JVM实现加载保存图像功能(JAVA 图像处理)

    目录 加载图片 保存图片 加载图片 openCv有一个名imread的简单函数,用于从文件中读取图像 imread 函数位于Imgcodecs类的同名包中. 加载图片代码 import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Core; import org.opencv.imgcodecs.Imgcodecs; import origami.Origami; public class

  • 基于JavaScript实现类似于百度学术高级检索功能

    百度学术http://xueshu.baidu.com/高级检索是通过前台生成后台内部高级语法来实现高级检索的,可以通过前台js做字符串拼接传给后台实现,难度不大: 下面是高级检索的核心功能代码,我使用的是纯js实现,并未使用jquery: <p class="fl srh-btn"> <input type="submit" class="srh-submit" style="height:px" valu

  • 基于opencv实现视频中的颜色识别功能

    目录 颜色识别的原理 opencv中的颜色模型 颜色识别的实现(c++) 颜色识别的原理 opencv中的颜色模型 RGB RGB具有三个通道其,分别表示红色通道®,绿色通道(G),蓝色通道(B),3个通道在opencv中的取值均为0~255,它的颜色由3个通道的取值来共同决定,因此如果使用RGB图像来进行颜色的识别,会丢失很多的颜色. HSV HSV具有三个通道,其分别表示色调(H),饱和度(S),亮度(V),3个通道在opencv中的取值分别如下: H:0~180 S:0~255 V:0~2

  • 基于OpenCV的路面质量检测的实现

    本期我们将展示一种对路面类型和质量进行分类的方法及其步骤.为了测试这种方法,我们使用了我们制作的RTK数据集. 路面分类 该数据集[1]包含用低成本相机拍摄的图像,以及新兴国家常见的场景,其中包含未铺砌的道路和坑洼.路面类型是有关人或自动驾驶车辆应如何驾驶的重要信息.除了乘客舒适度和车辆维护以外,它还涉及每个人的安全.我们可以通过[2]中的简单卷积神经网络(CNN)结构来实现. 在这种方法中,我们对表面类型分类任务使用特定的模型,我们将其定义为以下类别:沥青,已铺设(用于所有其他类型的路面)和未

  • Python基于OpenCV实现人脸检测并保存

    本文实例为大家分享了Python基于OpenCV实现人脸检测,并保存的具体代码,供大家参考,具体内容如下 安装opencv 如果安装了pip的话,Opencv的在windows的安装可以直接通过cmd命令pip install opencv-python(只需要主要模块),也可以输入命令pip install opencv-contrib-python(如果需要main模块和contrib模块) 详情可以点击此处 导入opencv import cv2 所有包都包含haarcascade文件.这

  • Python基于opencv调用摄像头获取个人图片的实现方法

    接触图像领域的应该对于opencv都不会感到陌生,这个应该算是功能十分强劲的一个算法库了,当然了,使用起来也是很方便的,之前使用Windows7的时候出现多该库难以安装成功的情况,现在这个问题就不存在了,需要安装包的话可以去我的资源中下载使用,使用pip安装方式十分地便捷. 今天主要是基于opencv模块来调用笔记本的内置摄像头,然后从视频流中获取到人脸的图像数据用于之后的人脸识别项目,也就是为了构建可用的数据集.整个实现过程并不复杂,具体如下: #!usr/bin/env python #en

  • Python开发之基于模板匹配的信用卡数字识别功能

    环境介绍 Python 3.6 + OpenCV 3.4.1.15 原理介绍 首先,提取出模板中每一个数字的轮廓,再对信用卡图像进行处理,提取其中的数字部分,将该部分数字与模板进行匹配,即可得到结果. 模板展示 完整代码 # !/usr/bin/env python # -*- coding: utf-8 -*- # @Time: 2020/1/11 14:57 # @Author: Martin # @File: utils.py # @Software:PyCharm import cv2

随机推荐