图像检索(4):IF-IDF,RootSift,VLAD
- 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的计算公式可以看出,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则是求落在同一个聚类中心的特征和该聚类中心残差的累加和。公式表示如下:
\]
\(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实现还是比较简单的。这里使用FlannBasedMatcher的match方法来查找特征最邻近的聚类中心(视觉词汇)。可以分为以下三步:
subtract计算特征和其最近邻的聚类中心的差值,add将同一个聚类中心的差值累加起来- 对上面得到的矩阵的
responseHist进行\(l_2\)的归一化处理 - 使用
reshape方法,将矩阵拉伸为一维\(k*d(128)\)的一维向量VLAD。
Summary
Bow通常要搭配TF-IDF进行使用,但是由于Vocabulary的大小的限制,VLAD是一个不错的替代选择。RootSift是对原生sift的扩展。
图像检索(4):IF-IDF,RootSift,VLAD的更多相关文章
- 图像检索(5):基于OpenCV实现小型的图像数据库检索
本文对前面的几篇文章进行个总结,实现一个小型的图像检索应用. 一个小型的图像检索应用可以分为两部分: train,构建图像集的特征数据库. retrieval,检索,给定图像,从图像库中返回最类似的图 ...
- 图像检索(3):BoW实现
在上一篇文章中图像检索(2):均值聚类-构建BoF中,简略的介绍了基于sift特征点的BoW模型的构建,以及基于轻量级开源库vlfeat的一个简单实现. 本文重新梳理了一下BoW模型,并给出不同的实现 ...
- 图像检索——VLAD
今天主要回顾一下关于图像检索中VLAD(Vector of Aggragate Locally Descriptor)算法,免得时间一长都忘记了.关于源码有时间就整理整理. 一.简介 虽然现在深度学习 ...
- 哈希学习(2)—— Hashing图像检索资源
CVPR14 图像检索papers——图像检索 1. Triangulation embedding and democratic aggregation for imagesearch (Oral ...
- Bag of Features (BOF)图像检索算法
1.首先.我们用surf算法生成图像库中每幅图的特征点及描写叙述符. 2.再用k-means算法对图像库中的特征点进行训练,生成类心. 3.生成每幅图像的BOF.详细方法为:推断图像的每一个特征点与哪 ...
- 图像检索(6):局部敏感哈希索引(LSH)
图像检索中,对一幅图像编码后的向量的维度是很高.以VLAD为例,基于SIFT特征点,设视觉词汇表的大小为256,那么一幅图像编码后的VLAD向量的长度为$128 \times 256 = 32768 ...
- 图像检索(2):均值聚类-构建BoF
在图像检索时,通常首先提取图像的局部特征,这些局部特征通常有很高的维度(例如,sift是128维),有很多的冗余信息,直接利用局部特征进行检索,效率和准确度上都不是很好.这就需要重新对提取到的局部特征 ...
- 图像检索(1): 再论SIFT-基于vlfeat实现
概述 基于内容的图像检索技术是采用某种算法来提取图像中的特征,并将特征存储起来,组成图像特征数据库.当需要检索图像时,采用相同的特征提取技术提取出待检索图像的特征,并根据某种相似性准则计算得到特征数据 ...
- CVPR14 图像检索papers
CVPR14年关于图像检索方面的papers,汇总成一个list,方便阅读. 图像检索 Triangulation embedding and democratic aggregation for i ...
随机推荐
- 【bzoj 3131】[Sdoi2013]淘金
Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹 ...
- jdk源码剖析一:OpenJDK-Hotspot源码包目录结构
开启正文之前,先说一下源码剖析这一系列,就以“死磕到底”的精神贯彻始终,JDK-->JRE-->JVM(以openJDK代替) 最近想看看JDK8源码,但JDK中JVM(安装在本地C:\P ...
- js默认参数实现方法
function simue (){ var a = arguments[0] ? arguments[0] : 1; var b = arguments[1] ? arguments[1] : 2; ...
- ionic3 懒加载在微信上缓存的问题
1.懒加载是什么? 在ionic2中所有的组件.模块.服务.管道等都堆积在app.module.ts模块中,在页面初始化的时候会一次性加载所有的资源,导致资源过大,页面渲染缓慢,也导致app.modu ...
- 学会python可以上天!20行代码获取斗鱼平台房间数据,就是这么牛逼!
Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定.它包含了一组完善而且容易理 ...
- HTML5仿微信聊天界面、微信朋友圈实例
这几天使用H5开发了一个仿微信聊天前端界面,尤其微信底部编辑器那块处理的很好,使用HTML5来开发,虽说功能效果并没有微信那么全,但是也相当不错了,可以发送消息.表情,发送的消息自动回滚定位到底部,另 ...
- 基于promtheus的监控解决方案
一.前言 鄙人就职于某安全公司,团队的定位是研发安全产品云汇聚平台,为用户提供弹性伸缩的云安全能力.前段时间产品组提出了一个监控需求,大致要求:平台对vm实行动态实时监控,输出相应图表界面,并提供警报 ...
- Java开源生鲜电商平台-团购模块设计与架构(源码可下载)
Java开源生鲜电商平台-团购模块设计与架构(源码可下载) 说明:任何一个电商系统中,对于促销这块是必不可少的,毕竟这块是最吸引用户的,用户也是最爱的模块之一,理由很简单,便宜. 我的经验是无论是大的 ...
- JS 图片放大镜
今天练习一个小demo, 从本地读取图片, 然后实现类似淘宝放大镜的效果, 再加两个需求 1 .可以调节缩放比例,默认放大两倍 2 . 图片宽高自适应, 不固定宽高 话不多说先看效果: 原理:1, 右 ...
- MIUI目前为止最简单安装谷歌服务框架教程
安装谷歌服务框架方法有很多,比如用第三方 rec卡刷gapps包.用第三方工具安装......然而这些对于新手来说还是比较难的! 我今天说的方法可以说是最简单的:1.不需要修改文件:2.不需要借助第三 ...