文章目录
1、原理
1.1 Bag of Words 表示模型
Bag of features是从文本挖掘的矢量空间模型(即 Bag of Words 表示模型)中获取的灵感,因此这里先讲矢量空间模型。
矢量空间模型是一个用于表示和搜索文本文档的模型。它基本上可以应用于任何对象类型,包括图像。该名字来源于用矢量来表示文本文档,这些矢量是由文本词频直方图构成的。换句话说,矢量包含了每个单词出现的次数,而且在其他别的地方包含很多 0 元素。由于其忽略了单词出现的顺序及位置,该模型也被称为 BOW 表示模型。
通常,在单词计数时会忽略掉一些常用词,如“这”“和”“是”等,这些常用词称为停用词。由于每篇文档长度不同,故除以直方图总和将向量归一化成单位长度。对于直方图向量中的每个元素,一般根据每个单词的重要性来赋予相应的权重。通常,数据集(或语料库)中一个单词的重要性与它在文档中出现的次数成正比,而与它在语料库中出现的次数成反比。
最常用的权重是 tf-idf(term frequency-inverse document frequency,词频 - 逆向文档频率 ),单词 w 在文档 d 中的词频是:
t
f
w
,
d
=
n
w
∑
j
n
j
tf_{w,d}=\frac{n_{w}}{\sum _{j}n_{j}}
tfw,d=∑jnjnw
n
w
n_{w}
nw 是单词w在文档d中出现的次数。为了归一化,将
n
w
n_{w}
nw 除以整个文档中单词的总数。
逆向文档频率为:
i
d
f
w
,
d
=
l
o
g
∣
(
D
)
∣
{
d
:
w
ϵ
d
}
idf_{w,d}=log\frac{\left | (D) \right |}{\{d:w\epsilon d\}}
idfw,d=log{d:wϵd}∣(D)∣
∣
D
∣
\left | D \right |
∣D∣ 是在语料库 D 中文档的数目,分母是语料库中包含单词 w 的文档数 d。将两者相乘可以得到矢量 v 中对应元素的 tf-idf 权重。
1.2 Bag of features
将Bag of Words技术应用到图像中,即为Bag of features。
1.2.1 视觉单词
通常采用 SIFT 局部描述子建立视觉等效单词。它的思想是将描述子空间量化成一些典型实例,并将图像中的每个描述子指派到其中的某个实例中。这些典型实例可以通过分析训练图像集确定,并被视为视觉单词。所有这些视觉单词构成的集合称为视觉词汇,有时也称为视觉码本。对于给定的问题、图像类型,或在通常情况下仅需呈现视觉内容,可以创建特定的词汇。
从一个(很大的训练图像)集提取特征描述子,利用一些聚类算法可以构建出视觉单词。聚类算法中最常用的是 K-means。视觉单词并不高端,只是在给定特征描述子空间中的一组向量集,在采用 K-means 进行聚类时得到的视觉单词是聚类质心。
1.2.2 创建词汇
首先使用SIFT 特征描述子提取特征描述子,再在训练图像数据集上训练出一个词汇。
1.2.3 图像索引
在索引图像前,我们需要建立一个数据库。对图像进行索引就是从这些图像中提取描述子,利用词汇将描述子转换成视觉单词,并保存视觉单词及对应图像的单词直方图。从而可以利用图像对数据库进行查询,并返回相似的图像作为搜索结果。
可以使用 SQLite 作为数据库。SQLite 将所有信息都保存到一个文件,是一个易于安装和使用的数据库。SQLite 使用 SQL 查询语言,所以如果想用别的数据库,这个转换过程非常简单。
有了数据库表单,我们便可以在索引中添加图像。建立好图像的索引,我们就可以在数据库中搜索相似的图像了。用 BoW(Bag-of-Word,词袋模型)来表示整个图像,不过这里介绍的过程是通用的,可以应用于寻找相似的物体、相似的脸、相似的颜色等,它完全取决于图像及所用的描述子。
如果图像数据库很大,逐一比较整个数据库中的所有直方图往往是不可行。我们需要找到一个大小合理的候选集(这里的“合理”是通过搜索响应时间、所需内存等确定的),单词索引的作用便在于此:我们可以利用单词索引获得候选集,然后只需在候选集上进行逐一比较。
1.2.4 利用索引获取候选图像
可以利用建立起来的索引找到包含特定单词的所有图像,这不过是对数据库做一次简单的查询。例如一个单词直方图中的全部非零元素,我们在每个单词上进行遍历,得到包含该单词的图像,并合并这些列表;如果不想使用所有单词,你可以根据其倒排文档频率权重进行排序,并使用那些权重最高的单词。
此外,可以取出该列表中任意数量的元素并比较它们的直方图。你将会看到,这可以极大地提高检索效率。
2、Python实现
2.1 数据集
上图中的.sift文件是在实验过程中产生的。
2.2 代码
2.2.1 创建词汇并保存
# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
from PCV.localdescriptors import sift
#获取图像列表
imlist = get_imlist('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\data7\\')
nbr_images = len(imlist)
print('nbr_images:',nbr_images)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#提取文件夹下图像的sift特征
for i in range(nbr_images):
sift.process_image(imlist[i], featlist[i])
#生成词汇
voc = vocabulary.Vocabulary('Image')
voc.train(featlist, 200, 10)#调用了PCV的vocabulary.py中的train函数
#保存词汇
with open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\data7\\vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)#将生成的词汇保存到vocabulary.pkl(f)中
print ('vocabulary is:', voc.name, voc.nbr_words)
2.2.2 添加图像并创建图像索引
# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import imagesearch
from PCV.localdescriptors import sift
from sqlite3 import dbapi2 as sqlite
from PCV.tools.imtools import get_imlist
#获取图像列表
imlist = get_imlist('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\data7\\')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
with open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\data7\\vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)
#创建索引
indx = imagesearch.Indexer('testImaAdd.db',voc)
indx.create_tables()
#遍历所有的图像,并将它们的特征投影到词汇上
for i in range(nbr_images)[:110]:
locs,descr = sift.read_features_from_file(featlist[i])
indx.add_to_index(imlist[i],descr)
#提交到数据库
indx.db_commit()
con = sqlite.connect('testImaAdd.db')
print (con.execute('select count (filename) from imlist').fetchone())
print (con.execute('select * from imlist').fetchone())
2.2.3 检索
# -*- coding: utf-8 -*-
import pickle
from PCV.localdescriptors import sift
from PCV.imagesearch import imagesearch
from PCV.geometry import homography
from PCV.tools.imtools import get_imlist
#载入图像列表
imlist = get_imlist('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\data7\\')
nbr_images = len(imlist)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
with open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\data7\\vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)
src = imagesearch.Searcher('testImaAdd.db',voc)
#查询图像索引和查询返回的图像数
q_ind = 42
nbr_results = 10
# 常规查询(按欧式距离对结果排序)
res_reg = [w[1] for w in src.query(imlist[q_ind])[:nbr_results]]
print ('top matches (regular):', res_reg)
# load image features for query image
#载入查询图像特征
q_locs,q_descr = sift.read_features_from_file(featlist[q_ind])
fp = homography.make_homog(q_locs[:,:2].T)
# RANSAC model for homography fitting
#用单应性进行拟合建立RANSAC模型
model = homography.RansacModel()
rank = {}
# load image features for result
#载入候选图像的特征
for ndx in res_reg[1:]:
locs,descr = sift.read_features_from_file(featlist[ndx]) # because 'ndx' is a rowid of the DB that starts at 1
# get matches
matches = sift.match(q_descr,descr)
ind = matches.nonzero()[0]
ind2 = matches[ind]
tp = homography.make_homog(locs[:,:2].T)
# compute homography, count inliers. if not enough matches return empty list
try:
H,inliers = homography.H_from_ransac(fp[:,ind],tp[:,ind2],model,match_theshold=4)
except:
inliers = []
# store inlier count
rank[ndx] = len(inliers)
# sort dictionary to get the most inliers first
sorted_rank = sorted(rank.items(), key=lambda t: t[1], reverse=True)
res_geom = [res_reg[0]]+[s[0] for s in sorted_rank]
print ('top matches (homography):', res_geom)
# 显示查询结果
imagesearch.plot_results(src,res_reg[:5]) #常规查询
imagesearch.plot_results(src,res_geom[:5]) #重排后的结果
2.3 运行结果
输入图片:
(1)K=50时:
(2)K=100时:
(3)K=200时:
(4)K=500时:
分析:第一张是常规的图,第二张是重排后的图,第三张是两种情况下的匹配度最高的前10张照片的索引。
2.4 小结
- 生成特征sift时间较慢,查询结果可以接受。
- K相同时,常规和重排后的结果相等。这说明在K位于50~500范围之间,重排对于匹配没有影响。
- K不同时,运行结果也不同,但观察可发现:每种K值取得的结果(每种10个索引),总的只有12种不同的索引。这说明,在K位于50~500范围之间,结果的波动并不大。
- 维度K越大匹配越精确。