一、Bag of word模型
1.1Bag of word模型原理
最初的Bag of words,也叫做“词袋”。Bag of words模型是信息检索领域常见的文档表示方法。在信息检索中,假设一个文本,忽视它的词序和语法等要素,将其仅仅看作是一个词集合,文本中每个词的出现都是独立的。然后根据文本中的词频分布,构造文本描述子。
例如:文本1:Lin likes eating rice, Amy likes too.
文本2:Lin also likes eating meat.
基于以上两个文本,构造一个词典:
dictionary={1:“Lin”,2:“likes”,3:“eating”,4,:“rice”,5:“Amy”,6:“too”,7:“also”,8:“meat”}
构造的这个词典包含8个不同的单词,利用词汇中的索引号,上面的两个文本均可用一个词典单词数量(这里是8维)的向量表示某个词在文本中出现的次数。
有如下结果:
文本1:【1,2,1,1,1,1,0,0】
文本2:【1,1,1,0,0,0,1,1】
根据词典中单词的在文本中词频统计(出现的次数统计),可以构造每个文本的单词频率直方图。
二、Bag of features模型
1.1Bag of features模型介绍
Bag of Feature也是借鉴了Bag of word思路,只不过在图像中,我们抽出的不再是一个个“word”,而是图像的关键特征“Feature”。
考虑将Bag-of-words模型应用于图像表示。为了表示一幅图像,我们可以将图像看作文档,即若干个“视觉单词”的集合,同样的,视觉单词相互之间没有顺序。
视觉单词: 通过分析训练图像集SIF特征描述子空间量化成一些典型实例,并将图像中的每个描述子指派到其中的某个实例中。这些典型实例即为视觉单词。
视觉词汇(视觉码本): 所有这些视觉单词构成的集合。
Q:如何构建视觉单词? A:从一个足够大数据集中提取特征描述子,利用一些聚类算法,可以构建出视觉单词。
Q:用什么聚类算法? A:最常用的是K-means聚类算法,采用K-means进行聚类时的聚类质心即为视觉单词。关于K-means聚类算法详细可见博主此篇博客——K-means聚类算法。
1.2Bag of features算法流程
1、特征提取
由于图像中的视觉单词不像文本中是已有的单词,故首先需要对图像利用算法进行特征提取。常用SIFT特征提取对图像进行提取特征描述子。关于SIFT特征提取详细可见此博客——计算机视觉——SIFT特征匹配
而后将每幅图像提取出的描述子保存在一个文件中。
2、学习 “视觉词典(visual vocabulary)”
常用K-Means聚类算法学习视觉词典。K-Means算法是一种基于样本间相似性度量的间接聚类方法,此算法以K为参数,把N个对象分为K个簇,以使簇内具有较高的相似度,而簇间相似度较低。SIFT提取的视觉词汇向量之间根据距离的远近,可以利用K-Means算法将词义相近的词汇合并,作为视觉词典中的基础词汇,假定我们将K设为4,那么单词表的构造过程如下图4所示:
3、针对数据库中每张图片的特征集,根据视觉词典以及IDF解算,将每张图片量化为单词频率直方图
下面先重点介绍下TF-IDF:
TF-IDF(term frequency–inverse document frequency,词频-逆向文档频率),是一种用于信息检索与数据挖掘的常用加权技术,常用于挖掘文章中的关键词。
假设我们要检索关于「原子能的应用」的文章,最简单的做法就是将查询分解为「原子能」、「的」、「应用」,然后统计每篇文章中这三个词出现的频率。比如,如果一篇文章的总词数是 1000 ,其中「原子能」、「的」、「应用」分别出现了 2 次、35 次和 5 次,那么它们的词频就分别是 0.002、0.035、0.005。将这三个数相加,总和 0.042 就是该文章关于「原子能的应用」的「词频」。一般来说,词频越高,文章的相关性就越强。TF-IDF 中的 TF 也就是词频(Term Frequency)的意思。
但这种方法有一个明显的漏洞,就是一些跟主题不相关的词可能占据较大的比重。比如上面例子中的「的」一词,占据了总词频的 80% 以上,而这个词对主题的检索几乎没有作用。这种词我们称为「停止词(Stop Word)」,表明在度量相关性时不考虑它们的频率。忽略「的」之后,我们的词频变为 0.007,其中「原子能」贡献了 0.002,「应用」贡献了 0.007。
根据以上两条,得出结论如果一个关键词只在很少的文章中出现,通过它就容易锁定搜索目标,它的权重就应该更大。反之,如果一个词在大量文章中频繁出现,看到它仍然不清楚要找什么内容,它的权重就应该小。
TF-IDF公式解析:
单词w在文档d中的词频是:
即tf=(单词w在文档中出现的次数)/(整个文档单词的总数)
逆向文档频率为:
即idf=log(语料库D中的文档数)/(语料库中包含单词w 的文档数d)
将
t
f
tf
tf与
i
d
f
idf
idf相乘,得出TF-IDF权重。
TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
其次关于单词频率直方图:
利用SIFT算法,可以从每幅图像中提取很多个特征点,这些特征点都可以用单词表中的单词近似代替,通过统计单词表中每个单词在图像中出现的次数,可以将图像表示成为一个K=4维数值向量。(如下图)
4、构造特征到图像的倒排表,通过倒排表快速索引相关图像
举例:
文档1:what is it?
文档2:it is a banana.
则有:
“what”: {1,1} 表示"what"出现在文档1中,频率为1次
“is”: {1,2,2} 表示"is“出现在文档1和文档2中,总频率为2次
“it”: {1,2,2}表示"it“出现在文档1和文档2中,总频率为2次
“a”: {2,1}表示"a“出现在文档2中,总频率为1次
“banana”: {2,1}表示"banana“出现在文档2中,总频率为1次
在图像检索中,操作为:获取具有相似单词的图像列表,可以检索每个单词获得候选集并将其合并到列表中,获取单词 id后,寻找候选图像,而后获取所有唯一的单词,并按出现次数反向排序,得到按匹配度由高到低的候选图像列表,通过这个倒排表可快速索引相关图像的id。
5、根据索引结果进行直方图匹配
利用已建好的索引找到包含特定单词的所有图像。为了获得包含多个单词的候选图像,有两种解决方法。
①在每个单词上进行遍历,得到包含该单词的所有图像,然后合并这些列表。接着对在合并了的列表中,对每一个图像id出现的次数进行跟踪排序,排在列表最前面的是最好的匹配图像。
②如果不想遍历所有的单词,可以根据其倒排序文档频率权重进行排序,并使用那些权重最高的单词,在这些单词上进行遍历,减少计算量,提高运行的效率。
三、基于Bag of features模型实现图像搜索
3.1基本流程
- 对数据集中的图像进行SIFT特征提取,根据特征提取结果,利用K-Means聚类算法生成视觉词典
- 建立数据库,将图像数据写入数据库
- 输入检索图像,由SIFT特征提取及tf-idf转化量化图像得到检索图像的频率直方图,获取倒排的具有相似单词的候选图像列表,通过倒排表快速索引,得到匹配度由高到底的5张匹配结果
3.2数据集、环境配置及代码准备
3.2.1数据集描述
本数据集为自行采集的110张图片,有建筑、植物、物品等。
3.2.2代码运行环境
- 电脑系统:windows10,64位
- python版本:python3.7
- 代码运行软件:pycharm2019.2.4
3.2.3代码准备
- 安装sqlite库
-
python2.7
①cmd进入python所在文件路径,命令行直接pip install pysqlite
,鉴于直接pip经常出现各种错误,建议使用法②
②手动通过安装包安装
1)先下载安装包(下载地址为:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl在该网址下使用 CTRL+F 搜索 pysqlite 即可瞬间找到我们想要的安装包:);
2)将whl文件放入python安装目录下的scripts目录;
3)命令行进入python安装目录下的scripts目录(例如D:\Python27\Scripts),输入命令pip install pysqlite-2.8.3-cp27-cp27m-win_amd64.whl
-
python3.7
自带sqlite,代码中from sqlite3 import dbapi2 as sqlite
直接导入即可
-
安装pyqt5
命令行pip install pyqt5
或手动安装参考上方 -
将PCV包和vlfeat包放入代码文件目录中(代码中需要用到PCV里的文件)
3.3代码实现
3.3.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\\image\\')
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('ukbenchtest')
voc.train(featlist, 110, 10)#调用了PCV的vocabulary.py中的train函数
#保存词汇
with open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\image\\vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)#将生成的词汇保存到vocabulary.pkl(f)中
print ('vocabulary is:', voc.name, voc.nbr_words)
- 调用
vocabulary.py
,创建出词汇类,其中的train()函数利用K-means聚类在数据集上对提取出来的所有sift特征聚类出单词,其参数有featurefiles-带sift后缀的图像特征描述子的文件列表,K-词汇单词数(聚类中心数)等,project()
函数对每幅图像的sift描述子进行统计得到每幅图像的单词直方图表示。 - 用于处理pkl文件的
dump()
方法将生成的词汇保存到指定文件中,pkl文件是python里面保存文件的一种格式,里面是序列化的数据,人难以识别。
vocabulary.py
from numpy import *
from scipy.cluster.vq import *
from PCV.localdescriptors import sift
class Vocabulary(object):
def __init__(self,name):
self.name = name
self.voc = []
self.idf = []
self.trainingdata = []
self.nbr_words = 0
def train(self,featurefiles,k,subsampling=10):
"""使用k个单词数为k的均值,从特征文件中列出的文件中的特征训练词汇。"""
"""训练数据的下采样(subsampling)可用于加速"""
"""k为聚类中心数(词汇单词数)"""
nbr_images = len(featurefiles)
# 从文件中读取特征
descr = []
descr.append(sift.read_features_from_file(featurefiles[0])[1])# 将所有的特征并在一起,以便后面进行 K-means 聚类
descriptors = descr[0] #stack all features for k-means
for i in arange(1,nbr_images):
descr.append(sift.read_features_from_file(featurefiles[i])[1])
descriptors = vstack((descriptors,descr[i]))
#K-means: 最后一个参数决定kmeans运行次数
self.voc,distortion = kmeans(descriptors[::subsampling,:],k,4)
self.nbr_words = self.voc.shape[0]
# 遍历所有的训练图像,并投影到词汇上
imwords = zeros((nbr_images,self.nbr_words))
for i in range( nbr_images ):
imwords[i] = self.project(descr[i])
nbr_occurences = sum( (imwords > 0)*1 ,axis=0)
self.idf = log( (1.0*nbr_images) / (1.0*nbr_occurences+1) )
self.trainingdata = featurefiles
def project(self,descriptors):
""" 将描述子投影到词汇上,以创建单词直方图 """
# 图像单词直方图
imhist = zeros((self.nbr_words))
words,distance = vq(descriptors,self.voc)
for w in words:
imhist[w] += 1
return imhist
def get_words(self,descriptors):
""" Convert descriptors to words. """
return vq(descriptors,self.voc)[0]
3.3.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\\image\\')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
with open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\image\\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())
-
调用PCV中的
imagesearch.py
在数据库中创建图像索引 -
i
mhistograms
包含了全部每幅图像的单词直方图
imagesearch.py
from numpy import *
import pickle
import sqlite3
from functools import cmp_to_key
import operator
class Indexer(object):
def __init__(self,db,voc):
""" 初始化数据库的名称及词汇对象 """
self.con = sqlite3.connect(db)
self.voc = voc
def __del__(self):
self.con.close()
def db_commit(self):
self.con.commit()
def get_id(self,imname):
""" 获取图像 id,如果不存在,就进行添加 """
cur = self.con.execute(
"select rowid from imlist where filename='%s'" % imname)
res=cur.fetchone()
if res==None:
cur = self.con.execute(
"insert into imlist(filename) values ('%s')" % imname)
return cur.lastrowid
else:
return res[0]
def is_indexed(self,imname):
""" 如果图像名字(imname)被索引到,就返回 True"""
im = self.con.execute("select rowid from imlist where filename='%s'" % imname).fetchone()
return im != None
def add_to_index(self,imname,descr):
""" 获取一幅带有特征描述子的图像,投影到词汇上并添加进数据库 """
if self.is_indexed(imname): return
print ('indexing', imname)
# 获取图像id
imid = self.get_id(imname)
# 获取单词
imwords = self.voc.project(descr)
nbr_words = imwords.shape[0]
#将每个单词与图像链接起来
for i in range(nbr_words):
word = imwords[i]
# wordid 就是单词本身的数字
self.con.execute("insert into imwords(imid,wordid,vocname) values (?,?,?)", (imid,word,self.voc.name))
# 存储图像的单词直方图
# 用 pickle 模块将 NumPy 数组编码成字符串
self.con.execute("insert into imhistograms(imid,histogram,vocname) values (?,?,?)", (imid,pickle.dumps(imwords),self.voc.name))
def create_tables(self):
"""创建数据库表"""
self.con.execute('create table imlist(filename)')
self.con.execute('create table imwords(imid,wordid,vocname)')
self.con.execute('create table imhistograms(imid,histogram,vocname)')
self.con.execute('create index im_idx on imlist(filename)')
self.con.execute('create index wordid_idx on imwords(wordid)')
self.con.execute('create index imid_idx on imwords(imid)')
self.con.execute('create index imidhist_idx on imhistograms(imid)')
self.db_commit()
这部分用于建立数据库,并创建imlist、imwords、imhistograms三张表(如下图所示)
class Searcher(object):
def __init__(self,db,voc):
""" 初始化数据库的名称. """
self.con = sqlite3.connect(db)
self.voc = voc
def __del__(self):
self.con.close()
def get_imhistogram(self,imname):
""" 返回一幅图像的单词直方图 . """
im_id = self.con.execute(
"select rowid from imlist where filename='%s'" % imname).fetchone()
s = self.con.execute(
"select histogram from imhistograms where rowid='%d'" % im_id).fetchone()
# 用 pickle 模块从字符串解码 Numpy 数组
return pickle.loads(s[0])
def candidates_from_word(self,imword):
""" 获取包含 imword 的图像列. """
im_ids = self.con.execute(
"select distinct imid from imwords where wordid=%d" % imword).fetchall()
return [i[0] for i in im_ids]
def candidates_from_histogram(self,imwords):
""" 获取具有相似单词的图像列表 """
# 获取单词 id
words = imwords.nonzero()[0]
# 寻找候选图像
candidates = []
for word in words:
c = self.candidates_from_word(word)
candidates+=c
# 获取所有唯一的单词,并按出现次数反向排序
tmp = [(w,candidates.count(w)) for w in set(candidates)]
tmp.sort(key=cmp_to_key(lambda x,y:operator.gt(x[1],y[1])))
tmp.reverse()
# 返回排序后的列表,最匹配的排在最前面
return [w[0] for w in tmp]
def query(self,imname):
""" 查找所有与 imname 匹配的图像列表 . """
h = self.get_imhistogram(imname)
candidates = self.candidates_from_histogram(h)
matchscores = []
for imid in candidates:
# 获取名字
cand_name = self.con.execute(
"select filename from imlist where rowid=%d" % imid).fetchone()
cand_h = self.get_imhistogram(cand_name)
cand_dist = sqrt( sum( self.voc.idf*(h-cand_h)**2 ) )
matchscores.append( (cand_dist,imid) )
# 返回排序后的距离及对应数据库 ids 列表
matchscores.sort()
return matchscores
def get_filename(self,imid):
""" 返回文件名作为图像的id """
s = self.con.execute(
"select filename from imlist where rowid='%d'" % imid).fetchone()
return s[0]
def tf_idf_dist(voc,v1,v2):
v1 /= sum(v1)
v2 /= sum(v2)
return sqrt( sum( voc.idf*(v1-v2)**2 ) )
def compute_ukbench_score(src,imlist):
""" 对查询返回的前 5 个结果计算平均相似图像数,并返回结果 """
nbr_images = len(imlist)
pos = zeros((nbr_images,5))
# 获取每幅查询图像的前 5 个结果
for i in range(nbr_images):
pos[i] = [w[1]-1 for w in src.query(imlist[i])[:5]]
# 计算分数,并返回平均分数
score = array([ (pos[i]//4)==(i//4) for i in range(nbr_images)])*1.0
return sum(score) / (nbr_images)
将Searcher
类添加到imagesearch.py
中,利用单词索引获得候选集,然后在候选集上进行逐一比较。 对候选列表中的图像,按相似性由大到小排序,返回它和查询图像间的直方图的距离,实现图片检索后找到(相似)匹配图像。tf_idf_dist()
函数就是对视觉单词的量化。
# import PIL and pylab for plotting
from PIL import Image
from pylab import *
def plot_results(src,res):
""" Show images in result list 'res'. """
figure()
nbr_results = len(res)
for i in range(nbr_results):
imname = src.get_filename(res[i])
subplot(1,nbr_results,i+1)
imshow(array(Image.open(imname)))
axis('off')
show()
最后,将此部分也加入imagesearch.py中,以可视化匹配出的图片结果。
3.3.3用一幅图像进行检索
#-*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import imagesearch
from PCV.localdescriptors import sift
from PCV.imagesearch import vocabulary
from sqlite3 import dbapi2 as sqlite
from PCV.tools.imtools import get_imlist
#获取图像列表
imlist = get_imlist('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\image\\')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
f = open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\image\\vocabulary.pkl', 'rb')
voc = pickle.load(f)
f.close()
src = imagesearch.Searcher('testImaAdd.db',voc)
locs,descr = sift.read_features_from_file(featlist[0])
iw = voc.project(descr)
print('当前图像单词词频(直方图):')
print(iw)
print ('候选图像列表:')
print (src.candidates_from_histogram(iw)[:10])#获取具有相似单词的图像列表
src = imagesearch.Searcher('testImaAdd.db',voc)
# print ('try a query...')
print('匹配结果:')
print(src.query(imlist[1])[:5])#改imlist[#]
nbr_results = 5#结果图像数
res = [w[1] for w in src.query(imlist[1])[:nbr_results]]#改imlist[#]
imagesearch.plot_results(src,res)
print("计算搜索结果得分:")
print(imagesearch.compute_ukbench_score(src,imlist[:5]))
3.3.4使用几何特性对结果排序
这是一种是用BOW模型改进检索结果的常用方法。BOW模型的一个主要缺点是在用视觉单词表示图像时不包含图像特征的位置信息,这是为了获取速度和可伸缩性而付出的代价。
最常用的方法是在查询图像与靠前图像的特征位置间拟合单应性。
# -*- 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\\image\\')
nbr_images = len(imlist)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
with open('F:\\PycharmProjects\\Test\\ch07_ImageSearch\\image\\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)
#载入查询图像特征
q_locs,q_descr = sift.read_features_from_file(featlist[q_ind])
fp = homography.make_homog(q_locs[:,:2].T)
#用单应性进行拟合建立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
# 获取匹配数
matches = sift.match(q_descr,descr)
ind = matches.nonzero()[0]
ind2 = matches[ind]
tp = homography.make_homog(locs[:,:2].T)
# 计算单应性,对内点技术。如果没有足够的匹配书则返回空列表
try:
H,inliers = homography.H_from_ransac(fp[:,ind],tp[:,ind2],model,match_theshold=4)
except:
inliers = []
# 存储内点数
rank[ndx] = len(inliers)
# 将字典排序,以首先获取最内层的内点数
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]) #重排后的结果
3.4结果分析
3.4.1改变视觉词典维度(不同聚类中心数)-数据库外图片检索(K-Means聚类次数=4)
根据上方原理中可知,利用K-Means聚类后的聚类中心即为视觉单词,而所有视觉单词构成视觉词典,所以聚类中心总数就是视觉词典维度。
在创建词汇代码中,修改train()函数的第二个参数,即可修改生成的视觉词典维度。
输入的检索图片:
k=20
k=50
k=100
k=200
k=20,候选图像列表结果:(不是检索出的结果)
k=50,检索图像结果:
k=100,检索图像结果:
k=200,检索图像结果:
K=20时,由于视觉单词过少,可以由直方图结果看出每个单词词频数值都较大,说明这些单词在所有图像中普遍性高,因为单词过少,无法解算出候选图像的tf-idf值,所以检索不出相似的图像结果。
当K增大,得到的视觉单词增多,同一图像(输入的检索图像)在不同k值下的图像单词词频不同,是因为取不同的k值,则有不同数量的聚类中心,那么就有不同数量的视觉单词,加上k-means运行了4次,每次聚类也可能会产生不同的聚类结果,所以每次生成的视觉单词也不同,因而图像对应的视觉单词词频也不同。
由检索图像结果可知,从k=50至k=200,前5张结果与检索图片的相似度越来越高,可以看到k=50和100,最相似的5张图片中,只有1张是与输入的检索图像中的建筑完全不同,k=200时最相似的5张图片均与输入的检索图像有同一主体建筑,可见K值增大,当视觉词汇维度足够大,能检索出的结果相似度越高。另外,由于SIFT特征提取具有旋转不变性,所以即使旋转角度的图像,仍能匹配出图像中同一建筑,如k=200时的图2。
3.4.2改变视觉词典维度(不同聚类中心数)-数据库内图片检索(K-Means聚类次数=4)
输入的检索图像:
K=10
视觉词典维度:
输入的检索图像的单词词频:
候选图像列表结果:(不是匹配出结果)
K=50
视觉词典维度:
输入的检索图像的单词词频:
候选图像列表结果:(不是检索出结果)
k=100
输入的检索图像的单词词频:
检索图像结果:
K=200
视觉词典维度:
输入的检索图像的单词词频:
检索图像结果:
k=500
视觉词典维度:
输入的检索图像的单词词频:
检索图像结果:
分析:
由于控制台无法直接输出直方图,故输出单词词频的方式以量化每张图片。
由上方结果可看出,当k取较小的数如20,50时,每个单词词频数值偏大,原因是SIFT特征提取的特征数量众多,在对特征进行聚类的形成视觉单词过程中,聚类质心k值过小,很多特征被聚在一类,视觉单词数量少,导致聚类后的视觉单词间区分度不高,本质上此时的视觉单词是每张图片中极为相似的部分(类似在一篇文章中提取了‘的’‘在’‘是’等通用词作为词典,当放入一篇文章进行检索,难以找到匹配的文章),于是难以找到匹配结果。此时计算搜索结果值(即对查询返回的前 5个结果计算平均相似图像数,数值在0-5间,0表示完全不匹配,5表示完全匹配)为0。
当k取较大的数如100,200,500时,聚类中心K的数量增大,构建出的视觉单词增多,视觉词典维度大幅增大,可以明显看出词频间数值差异,在某一单词的词频数值越小,说明这个单词越能代表此图像的特征,k=200和k=500时,词频中有许多的0和1,1所代表的单词是此图像的显著特征,在与数据库中图像进行检索时,只需找到有1所代表的单词的图片即为与输入的检索图像相似的图像,由检索结果图像也可看出,图像间具有相似性。结果中相似度最高的5张图片存在与检索图片仅有相似建筑(实际上并不是同一建筑)的结果,由计算搜索结果值为1.多也验证了5张图片之间相似度不高。
打印的匹配结果,按相似性由大到小,输出了候选图像和输入的检索图像间的距离,以及图像载数据库中的索引号。
由上图检索图像结果发现,k=200时的输出的匹配结果较k=100时,总体距离(指已量化的检索图像与已量化的候选图像的欧式距离)变小,与输入的检索图像相似性高的距离小,匹配出的5张图像中有两张与k=100不同。按距离计算,距离为0是完全匹配的图像,索引为7的图像比起索引为10的图像距离更小,但在k=200时前5张匹配结果中并没有这张图像,索引为9的图像确实有检索图像中的建筑在k=200时显示在了前5张检索结果中但在k=100时前5张检索结果没有出现,而同一张图像(索引为51)在k=200时的距离大于k=100时的距离,说明k值增大,视觉单词对图像的独特性更明显了,检索出的图像更加准确。
打印出的前10个候选图像列表的图像id,这里是任意显示的,并不是准确的“排在最前面的是最好的匹配图像”,但在候选图像列表中可以取出列表中任意数量的元素并比较他们的直方图,比起逐一比较所有图像的直方图,这种方法提高了检索效率。
3.4.3不同k-means聚类次数(K=200)
在PCV中的vocabulary.py中的train()函数中,改变此行最后一个参数即可
输入的检索图像:
聚类次数=1
聚类次数=3
聚类次数=4
聚类次数=7
分析:
- 固定K值下,当聚类次数增大,检索出的5张图像与输入图像的距离减小(例如索引为37的图像,在聚类次数由1增至7后,距离由21.4减小到18.8),观察图像可看出结果的相似性也趋于更高,聚类中心在不断迭代的过程中由局部最优趋于全局最优,但能否达到全局最优,需要足够多的实验次数验证以及会受K初始值影响。
- 此实验数据集仅有110张图像,上面的结论可能存在偶然性,更大的数据集匹配出的图像结果会更明显些。
- 另外,迭代次数增加要付出运行效率下降的代价。
3.4.5 几何排序结果
k=50,聚类次数4次
k=100,聚类次数4次
k=200,聚类次数4次
k=500,聚类次数4次
由以上结果可知,当k越大,检索出的图像结果相似度越高,k过小,则无法检索出结果,所以要找到相似度最高的5张图,k应多次实验取适当的值。
由于数据集不够大,不够丰富,所有结果中常规排序和单应性排序结果一致。其中单应性通过计算匹配数和内点得到,其排序通过减少内点的数目对包含图像索引和内点数的字典进行排序,这样可以有效提高准确率。
四、实验总结
4.1小结
- 当视觉词典维度较小(K值小),视觉单词较少时,进行图像检索时,无法覆盖所有图像单词,还有单词存在普遍性的问题,难以得到匹配度高的结果;
当视觉词典维度较大(K值大),视觉单词较多时,进行图像检索时,对噪声敏感,易出现过拟合现象。 - Bag of features通过if-idf解算直方图的方式,候选图像列表中可以取出列表中任意数量的元素并比较他们的直方图, 实现图片与图片间的匹配,比起逐一比较所有图像的直方图,极大提高检索效率,这区别于图像只通过SIFT提取局部特征描述子进行匹配的方法。
- 固定K值下,当聚类次数增大,聚类中心在不断迭代的过程中由局部最优趋于全局最优,匹配出的结果相似性增大。
- 若要用单应性重排搜索到的靠前结果,需要词汇量足够大的数据集。
- 本实验数据集仅有110张图像,得出的结论可能鲁棒性不够,可以用更大型的数据集进行实验。
4.2问题及解决
- 注意python2.x和python3.x数据编码方式有所不同,当用python2.×生成的pkl文件,用python3.x读取会出现编码无法识别。所以,建议只用一种环境运行所有代码。
- 一些代码运行问题,在文中代码准备部分已给出解决方案。
- 若出现提示如下(数据库已存在),在目录下删掉数据库即可。