在检索原理上,无论是基于文本的图像检索还是基于内容的图像检索,主要包括三方面:一方面对用户需求的分析和转化,形成可以1检索索引数据库的提问;另一方面,收集和加工图像资源,提取特征,分析并进行标引,2建立图像的索引数据库;最后一方面是根据相似度算法,计算用户提问与索引数据库中记录的相似度大小,3提取出满足阈值的记录作为结果,按照相似度降序的方式输出。
为了进一步提高检索的准确性,许多系统结合相关反馈技术来收集用户对检索结果的反馈信息,这在CBIR
中显得更为突出,因为CBIR
实现的是逐步求精的图像检索过程,在同一次检索过程中需要不断地与用户进行交互。
基于文本的图像检索沿用了传统文本检索技术,回避对图像可视化元素的分析,而是从图像名称、图像尺寸、压缩类型、作者、年代等方面标引图像,一般以关键词形式的提问查询图像,或者是根据等级目录的形式浏览查找特定类目下的图像
基于内容的图像检索根据图像、图像的内容语义以及上下文联系进行查找,以图像语义特征为线索从图像数据库中检出具有相似特性的其它图像。因为图像的规模一般要大于纯粹的文本信息,因此,基于内容的图像检索在检索的速度和效率上要求更高。
Bag Of Word
模型,是现在一种用于图像检索的一种方法。它最早用于对于文章内容的检索,原理是将文本看作是单词的集合,不考虑其中的语法,上下文等等。通过建立词典,对每个单词出现次数进行统计,以便得到文本内容的分类。计算机视觉的专家从中获得灵感,将其用于图像的检索中,就有了Bag Of Features
。
visual vocabulary
TF-IDF
转换成视觉单词的频率直方图主要通过SIFT
描述算子进行特征提取,从而标记每张图片,达到唯一标识。特征点的提取主要是针对图像的关键部分的提取,而采用SIFT
算法的优点在于该算法对一些特殊点比较敏感,并且SIFT
算法对图像旋转,尺度缩放,亮度变化具有不变性。
通过上一步,我们获得了图像的特征点,但不没有进行分类处理,这里采用K-means
聚类算法,将图像多个特征点进行分类,这里需要注意的是,如果我们码本规模太小,我们的visual vocabulary
就不能包括所有可能的情况,相反的是规模过大,则会导致过拟合现象
对于输入特征,量化的过程是将该特征映射到距离其最近的视觉单词,并实现计数。对于输入图片利用K-means
算法聚类图片特征得到的视觉单词,根据TF-IDF
转化成视觉单词的频率直方图,用视觉单词直方图来表示图像,如上图所示。
倒排表是一种逆向的查找方式,在BOW
中是通过已经提取出来的词汇,反向查找出现过这个词汇的文章。BOF中倒排表也是同理。通过对视觉词汇的反向查找,就会得到拥有同一视觉词汇的图像集合,反复多次就能得到一张倒排表。倒排表可以快速的得到新的图像与数据库里相似的图像。
当我们做完上面的步骤,就需要对直方图进行匹配。直方图的匹配给出输入图像的频率直方图,在数据库中查找K个最近邻的图像,根据这K个近邻来投票图像的分类结果。
数据集:
该次实验数据集是博主自己家周围3种环境的图片采样,总共100张图片
并且分别设置k-means
算法K值依次为10,50,100,500
以此探究对算法精确度的影响程度。
环境配置:
vlfeat-0.9.20
版本,vlfeat的win64里的vl.dll和sift.exe和vl.lib
复制在项目里,同时注意把PCV包放在项目下。pip install PyQt5
pip install cherrypy
Cacubulary.py
# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
import sift
#获取图像列表
#imlist = get_imlist('E:/Python37_course/test7/first1000/')
imlist = get_imlist('images/')
nbr_images = len(imlist)
#获取特征列表
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, 1000, 10)
voc = vocabulary.Vocabulary('test77_test')
voc.train(featlist, 100, 10)
#保存词汇
# saving vocabulary
'''with open('E:/Python37_course/test7/first1000/vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)'''
with open('images/vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)
该函数主要实现提取每一张图片的SIFT特征值,生成.sift文件,以及在根目录下视生成觉词汇vocbulary.pkl文件
我所用的是图像集为100张。如果需要增加图像或减少只需要改代码里读取训练图像的数量。如下图红框所示,后面10表示的是训练10次。
AddImage.py
# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import imagesearch
from PCV.localdescriptors import sift
import sqlite3
from PCV.tools.imtools import get_imlist
#获取图像列表
#imlist = get_imlist('E:/Python37_course/test7/first1000/')
imlist = get_imlist('images/')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
# load vocabulary
#载入词汇
'''with open('E:/Python37_course/test7/first1000/vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)'''
with open('images/vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)
#创建索引
indx = imagesearch.Indexer('testImaAdd.db',voc)
indx.create_tables()
# go through all images, project features on vocabulary and insert
#遍历所有的图像,并将它们的特征投影到词汇上
#for i in range(nbr_images)[:1000]:
for i in range(nbr_images)[:70]:
locs,descr = sift.read_features_from_file(featlist[i])
indx.add_to_index(imlist[i],descr)
# commit to database
#提交到数据库
indx.db_commit()
con = sqlite3.connect('testImaAdd.db')
print (con.execute('select count (filename) from imlist').fetchone())
print (con.execute('select * from imlist').fetchone())
将生成的视觉词汇vocbulary.pkl文件存入数据库,上诉代码运行成功会在项目根目录生成一个testImaAdd.db文件该文件的大小会随着你的特征维度的大小而上升。
Reranking.py
# -*- coding: utf-8 -*-
import pickle
import sift
from PCV.imagesearch import imagesearch
from PCV.geometry import homography
from PCV.tools.imtools import get_imlist
# load image list and vocabulary
#载入图像列表
imlist = get_imlist('images/')
nbr_images = len(imlist)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
'''with open('E:/Python37_course/test7/first1000/vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)'''
with open('images/vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)
src = imagesearch.Searcher('testImaAdd.db',voc)
# index of query image and number of results to return
#查询图像索引和查询返回的图像数
q_ind = 0
nbr_results = 6
# regular query
# 常规查询(按欧式距离对结果排序)
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[:8]) #常规查询
imagesearch.plot_results(src,res_geom[:8]) #重排后的结果
数据库建立成功便可以输入图像进行测试了,即运行上诉代码。
查询图像在最左边,后面都是按图像列表检索的前几幅图像。对输出的结果,首先是载入图像列表、特征列表及词汇。然后创建一个Searcher对象,执行定期查询,并将结果保存在res_reg列表中,然后载入res_reg列表中每一幅图像特征,并和查询的图像进行匹配。当隐形通过计算匹配数和计数内点数得到,最终可通过减少内点数目对包含图像索引和内点数的字典进行排序。最后可视化检索靠前的匹配图像结果。可以看出查询图片的准确度很低,应该是K值太小的缘故,导致图片训练出的视觉词典维度不够。
可以明显看出图片检索准确率的上升,但是该场景的景物过于单一简单,于是我们输入另外一张图片。
发现图片匹配率过低,再次提高字典特征维度把。
依旧不理想,继续提高K值。
左边为输入的图片,右边为输出的匹配图片,可以看出提高视觉词典的特征维度就是提高了图片匹配的成功率,这也不难想象,因为提高了维度即就是提升了算法的分类水平,维度越高,特征的分类就越精细,匹配的成功率也就越高,但上诉匹配中,扔存在匹配不准的图片,这应该是数据集过小,只有100张图片,如果想要更加的精确,就只能提高数据集以及K值,但这也会导致性能开销过大,增大了运行时间。因此在K值得选择上应不易过大,同样得过小得K值容易导致算法不精确,所以K值应该取折中值。