• TF-IDF
  • RootSift
  • VLAD

TF-IDF

TF-IDF是一种用于信息检索的常用加权技术,在文本检索中,用以评估词语对于一个文件数据库中的其中一份文件的重要程度。词语的重要性随着它在文件中出现的频率成正比增加,但同时会随着它在文件数据库中出现的频率成反比下降。像‘的’,‘我们’,‘地’等这些常用词在所有文章中出现的频率会很高,并不能很好的表征一个文档的内容。

同样的在图像检索中也引入了IF-IDF权重,

  • 词频(Term Frequency,TF) 一个visual word在一个图像中出现的频率的很高,则说明该visual word 能够很好的代表图像的内容。 $$TF = \frac{图像中某个word出现的次数}{图像word的总的个数}$$

  • 逆文档词频(Inverse Document Frequency,IDF) 一些常见的word,会在每一图像出现的频率都很高,但是这些word并不能很好的表示图像的内容,所以要给这一部分word低一些的权重。IDF,描述一个word的普遍重要性,如古欧word在很多的图像中出现的频率都很高,则给予其较低的权重。$$IDF = \log(\frac{图像的总个数}{含有该word的图像数 + 1})$$

    将分母+1是为了防止除数为0的情况出现。从上式中可以看出,包含当前word的图像个数越多,IDF的值越小,说明该词越不重要。反之,该词越重要。

计算得到了TF和IDF,则有

\[TF-IDF = TF * IDF
\]

从TF和IDF的计算公式可以看出,IDF是针对整个图像数据库而言的,可以在训练完成后计算一次得到。而TF则是针对具体的某张图像来说的,需要多次计算。

将TF-IDF权值赋给BoW向量,再进行\(l_2\)的归一化,即可得到一个可用于图像检索的向量。

C++实现

void compute_idf(const vector<<vector<int>> &bow,vector<float> &idf){

    int img_count = bow.size();
int clu_count = bow[0].size(); idf = vector<float>(clu_count,1.0); for(int i = 0; i < img_count; i ++){
for(int j = 0; j < clu_count; j ++){
if(bow[i][j] != 0) idf[j] ++;
}
} for(int i = 0; i < idf.size(); i ++){
idf[i] = log(img_count / idf[i]);
}
}

上面代码计算的是图像库的IDF(IDF是针对整个图像库而言的)。

针对某一张图片,都需要计算一次TF。TF的计算公式:\(TF = \frac{图像中某个word出现的次数}{图像word的总的个数}\),可以看着是对图像的BoW向量进行\(l_1\)的归一化。

void compute_tf(const vector<int> &bow,vector<float> &tf){

    tf = vector<float>(bow.size(),0);

    int sum = 0; // All words in the image
for(int i = 0; i < bow.size(); i ++){
sum += bow[i];
} for(int i = 0; i < bow.size(); i ++){
tf[i] = (float)bow[i] / sum;
}
}

RootSift

在Arandjelovic和Zisserman 2012的论文Three things everyone should know to improve object retrieval 提出了RootSift

当比较直方图时,使用欧氏距离通常比卡方距离或Hellinger核时的性能差,但是在使用sift特征点为什么一直都使用欧氏距离呢?

不论是对sift特征点进行匹配,还是对sift特征集合进行聚类得到视觉词汇表,又或者对图像进行BoW编码,都使用的是欧氏距离。但是sift特征描述子本质上也是一种直方图,为什么对sift特征描述子进行比较的时候要使用欧氏距离呢,有没有一种更精确的比较方法呢?

sift描述子统计的是关键点邻域的梯度直方图,更详细的介绍可以参考图像检索(1): 再论SIFT-基于vlfeat实现

Zisserman 认为只所以一直使用欧氏距离来测量sift特征的相似度,是由于在sift提出的时候,使用的是欧氏距离的度量,可以找出一种比较欧氏距离更为精确的度量方法。Arandjelovic和Zisserman提出了RootSift对sift特征进行扩展。

在提取得到sift的描述向量\(x\)后,进行以下处理,即可得到RootSift

1.对特征向量\(x\)进行\(l_1\)的归一化(\(l_1-normalize\))得到\(x'\)

2.对\(x'\)的每一个元素求平方根

3.进行\(l_2-normalize\),可选

最后一步,是否进行\(l_2\)的归一化,有些不一致。 在paper 并没有指出需要进行\(l_2\)的归一化,但是在presentation, 却有\(l_2\)归一化这一步。也有认为,显式地执行L2规范化是不需要的。通过采用L1规范,然后是平方根,已经有L2标准化的特征向量,不需要进一步的标准化。

Python实现

# import the necessary packages
import numpy as np
import cv2 class RootSIFT:
def __init__(self):
# initialize the SIFT feature extractor
self.extractor = cv2.DescriptorExtractor_create("SIFT") def compute(self, image, kps, eps=1e-7):
# compute SIFT descriptors
(kps, descs) = self.extractor.compute(image, kps) # if there are no keypoints or descriptors, return an empty tuple
if len(kps) == 0:
return ([], None) # apply the Hellinger kernel by first L1-normalizing and taking the
# square-root
descs /= (descs.sum(axis=1, keepdims=True) + eps)
descs = np.sqrt(descs)
#descs /= (np.linalg.norm(descs, axis=1, ord=2) + eps) # return a tuple of the keypoints and descriptors
return (kps, descs)

来自 https://www.pyimagesearch.com/2015/04/13/implementing-rootsift-in-python-and-opencv/

C++ 实现

for(int i = 0; i < siftFeature.rows; i ++){
// Conver to float type
Mat f;
siftFeature.row(i).convertTo(f,CV_32FC1); normalize(f,f,1,0,NORM_L1); // l1 normalize
sqrt(f,f); // sqrt-root root-sift
rootSiftFeature.push_back(f);
}

VLAD

局部聚合向量(Vector of Locally Aggregated Descriptors,VLAD)

前面介绍的BoW方法,在图像的检索和检索中有这广泛的应用。BoW通过聚类,对图像的局部特征进行重新编码,有很强的表示能力,并且使用SVM这样基于样本间隔的分类器,也能取得了很好的分类效果。但是在图像规模比较大的情况下,由于视觉词汇表Vocabulary大小的限制,BoW对图像的表示会越来越粗糙,编码后损失的图像信息较多,检索精度也随之而降低。

2010年,论文Aggregating local descriptors into a compact image representation中提出了一对新的图像表示方法,VLAD。从三个方面进行改进:

  • 使用VLAD表示图像的局部特征
  • PCA
  • 索引ADC的构建方法

BoW的表示方法中,是统计每个特征词汇在图像中出现的频率。VLAD则是求落在同一个聚类中心的特征和该聚类中心残差的累加和。公式表示如下:

\[v_{i,j} = \sum_{x\ such\ that\ NN(x)=c_i}x_j - c_{i,j}
\]

\(x_j\)是图像的第\(j\)个特征点,\(c_i\)则是和该特征点最近的聚类中心,\(x_j - c_{i,j}\)表示特征点和其最近聚类中心的差。假如使用的是sift特征,视觉词汇表Vocabulary的大小为\(k\),则可以得到\(k\)个128维的向量\(v_{i,j}\)。

然后将这\(k*d\)个向量(\(d\)为图像特征的长度,例如sift为128维)\(v_{i,j}\)拉伸为一个\(k*d\)长度的一维向量,再对拉伸后的向量做一个\(l_2\)的归一化就得到图像的VLAD表示。

由于,VLAD是特征和其最邻近的聚类中心的残差和,该向量的很多分量很多分量都为0,也就是说该向量是稀疏的(sparse,很少的分量占有大部分的能量),所以可以对VLAD进行降维处理(例如,PCA)能进一步缩小向量的大小。

实现

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
}

借助于OpenCV实现还是比较简单的。这里使用FlannBasedMatchermatch方法来查找特征最邻近的聚类中心(视觉词汇)。可以分为以下三步:

  • subtract计算特征和其最近邻的聚类中心的差值,add将同一个聚类中心的差值累加起来
  • 对上面得到的矩阵的responseHist进行\(l_2\)的归一化处理
  • 使用reshape方法,将矩阵拉伸为一维\(k*d(128)\)的一维向量VLAD。

Summary

Bow通常要搭配TF-IDF进行使用,但是由于Vocabulary的大小的限制,VLAD是一个不错的替代选择。RootSift是对原生sift的扩展。

图像检索(4):IF-IDF,RootSift,VLAD的更多相关文章

  1. 图像检索(5):基于OpenCV实现小型的图像数据库检索

    本文对前面的几篇文章进行个总结,实现一个小型的图像检索应用. 一个小型的图像检索应用可以分为两部分: train,构建图像集的特征数据库. retrieval,检索,给定图像,从图像库中返回最类似的图 ...

  2. 图像检索(3):BoW实现

    在上一篇文章中图像检索(2):均值聚类-构建BoF中,简略的介绍了基于sift特征点的BoW模型的构建,以及基于轻量级开源库vlfeat的一个简单实现. 本文重新梳理了一下BoW模型,并给出不同的实现 ...

  3. 图像检索——VLAD

    今天主要回顾一下关于图像检索中VLAD(Vector of Aggragate Locally Descriptor)算法,免得时间一长都忘记了.关于源码有时间就整理整理. 一.简介 虽然现在深度学习 ...

  4. 哈希学习(2)—— Hashing图像检索资源

    CVPR14 图像检索papers——图像检索 1.  Triangulation embedding and democratic aggregation for imagesearch (Oral ...

  5. Bag of Features (BOF)图像检索算法

    1.首先.我们用surf算法生成图像库中每幅图的特征点及描写叙述符. 2.再用k-means算法对图像库中的特征点进行训练,生成类心. 3.生成每幅图像的BOF.详细方法为:推断图像的每一个特征点与哪 ...

  6. 图像检索(6):局部敏感哈希索引(LSH)

    图像检索中,对一幅图像编码后的向量的维度是很高.以VLAD为例,基于SIFT特征点,设视觉词汇表的大小为256,那么一幅图像编码后的VLAD向量的长度为$128 \times 256 = 32768 ...

  7. 图像检索(2):均值聚类-构建BoF

    在图像检索时,通常首先提取图像的局部特征,这些局部特征通常有很高的维度(例如,sift是128维),有很多的冗余信息,直接利用局部特征进行检索,效率和准确度上都不是很好.这就需要重新对提取到的局部特征 ...

  8. 图像检索(1): 再论SIFT-基于vlfeat实现

    概述 基于内容的图像检索技术是采用某种算法来提取图像中的特征,并将特征存储起来,组成图像特征数据库.当需要检索图像时,采用相同的特征提取技术提取出待检索图像的特征,并根据某种相似性准则计算得到特征数据 ...

  9. CVPR14 图像检索papers

    CVPR14年关于图像检索方面的papers,汇总成一个list,方便阅读. 图像检索 Triangulation embedding and democratic aggregation for i ...

随机推荐

  1. 【bzoj 3131】[Sdoi2013]淘金

    Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块.    一阵风吹 ...

  2. jdk源码剖析一:OpenJDK-Hotspot源码包目录结构

    开启正文之前,先说一下源码剖析这一系列,就以“死磕到底”的精神贯彻始终,JDK-->JRE-->JVM(以openJDK代替) 最近想看看JDK8源码,但JDK中JVM(安装在本地C:\P ...

  3. js默认参数实现方法

    function simue (){ var a = arguments[0] ? arguments[0] : 1; var b = arguments[1] ? arguments[1] : 2; ...

  4. ionic3 懒加载在微信上缓存的问题

    1.懒加载是什么? 在ionic2中所有的组件.模块.服务.管道等都堆积在app.module.ts模块中,在页面初始化的时候会一次性加载所有的资源,导致资源过大,页面渲染缓慢,也导致app.modu ...

  5. 学会python可以上天!20行代码获取斗鱼平台房间数据,就是这么牛逼!

    Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定.它包含了一组完善而且容易理 ...

  6. HTML5仿微信聊天界面、微信朋友圈实例

    这几天使用H5开发了一个仿微信聊天前端界面,尤其微信底部编辑器那块处理的很好,使用HTML5来开发,虽说功能效果并没有微信那么全,但是也相当不错了,可以发送消息.表情,发送的消息自动回滚定位到底部,另 ...

  7. 基于promtheus的监控解决方案

    一.前言 鄙人就职于某安全公司,团队的定位是研发安全产品云汇聚平台,为用户提供弹性伸缩的云安全能力.前段时间产品组提出了一个监控需求,大致要求:平台对vm实行动态实时监控,输出相应图表界面,并提供警报 ...

  8. Java开源生鲜电商平台-团购模块设计与架构(源码可下载)

    Java开源生鲜电商平台-团购模块设计与架构(源码可下载) 说明:任何一个电商系统中,对于促销这块是必不可少的,毕竟这块是最吸引用户的,用户也是最爱的模块之一,理由很简单,便宜. 我的经验是无论是大的 ...

  9. JS 图片放大镜

    今天练习一个小demo, 从本地读取图片, 然后实现类似淘宝放大镜的效果, 再加两个需求 1 .可以调节缩放比例,默认放大两倍 2 . 图片宽高自适应, 不固定宽高 话不多说先看效果: 原理:1, 右 ...

  10. MIUI目前为止最简单安装谷歌服务框架教程

    安装谷歌服务框架方法有很多,比如用第三方 rec卡刷gapps包.用第三方工具安装......然而这些对于新手来说还是比较难的! 我今天说的方法可以说是最简单的:1.不需要修改文件:2.不需要借助第三 ...