Bag of features图像检索
一、原理
1、Bag-of-words
Bag-of-words用于图像和视频的检索,其来源于文本分类技术。在信息检索中,给定一个文本,根据文本中的词频分布,构造文本描述子。文本中每个词的出现都是独立的,与其出现的位置和顺序无关。 因此,图像检索可以从每张图片中提取关键特征,使用某种聚类算法将局部特征进行聚类,每个聚类中心被看作是词典中的一个视觉词汇,所有视觉词汇形成一个视觉词典。图像中的每个特征映射到视觉词典中的某个词上,统计每个视觉词汇出现的次数,形成一个直方图。
2、步骤
2.1 特征提取
采用SIFT方法提取所有图片的关键特征,
2.2 学习 “视觉词典(visual vocabulary)
采用K-means进行聚类,假设输入的图片经特征提取后,有n个特征点,由聚合得到k个集合,每个集合的聚类中心就构成了视觉词典。
步骤:
- 随机初始化 K 个聚类中心
- 重复下述步骤直至算法收敛:
- 对应每个特征,根据欧氏距离计算每个特征 xi 与其相对应的聚类中心 mk 之间的欧式距离,找到最小的那个聚类中心,将该特征点放入此聚类质心集合中。
- 对每个集合,根据其对应的特征集重新计算聚类中心
如图表示:
如何选择视觉词典/码本的规模?
- 太少:视觉单词无法覆盖所有可能出现的情况
- 太多: 计算量大,容易过拟合
常见设置:视觉单词数量(K-means算法获取的聚类中心)一般为K=3000~10000. 即图像整体描述的直方图维度为3000~10000.
2.3 针对输入特征集,根据视觉词典进行量化
量化的过程就是计算每个特征到所有的视觉单词的距离,映射到距离最近的,并将对应视觉单词的频数加1
2.4 把输入图像,根据TF-IDF(词频-逆向文档频率)转化成视觉单词(visual words)的频率直方图
2.4.1TF-IDF
在文本检索中,不同单词对文本检索的贡献有差异,
一个单词出现在文档中的比例越大,它对匹配的用处就越小,比如“的”,每篇文章中出现的概率很大,区别不大。
- TF:词频,指的是一个给定的词语在该文件中出现的次数
- IDF:逆文档频率,描述了某一个特定词语的普遍重要性,如果某词语在许多文档中都出现过,表明它对文档的区分力不强,则赋予较小的权重,IDF越小;反之IDF越大。
2.4.2 构建频率直方图
将投票值(TF)与“视觉单词”(IDF)权重相乘,得到频率直方图。
2.5 构造特征到图像的倒排表,通过倒排表快速索引相关图像
倒排表就是逆向的检索方法,在前面几步查找出来的词汇,反向查找出现这些词汇的文章,如图,查找多个词汇,就形成了一个倒排表
BOF通过对视觉词汇的反向查找,得到拥有同一视觉词汇的图像集合,反复多次就得到一张倒排表。这样根据倒排表就可以快速地在图像库中找到与新图像相似的图像。
2.6根据索引结果进行直方图匹配
输入一张图片进行图像的检索,用K邻近算法,给出前面图像集的直方图,在数据库中查找K个最近邻的图像。
二、实验
1、步骤
- 构造不小于100张图片的数据集
- 针对数据集,做SIFT特征提取
- 根据SIFT特征提取结果,采用k-means算法学习“视觉词典(visual vocabulary)”,其中维度至少满足4个量级(比如10,50,100,1000,5000)
- 根据IDF原理,计算每个视觉单词的权
- 针对数据库中每张图片的特征集,根据视觉词典进行量化 ,以及TF-IDF解算。每张图片转化成特征向量
- 对于输入的检索图像(非数据库中图片),计算SIFT特征,并根据TF-IDF转化成频率直方图/特征向量
- 构造检索图像特征到数据库图像的倒排表,快速索引相关候选匹配图像集
- 针对候选匹配图像集与检索图像进行直方图/特征匹配
2、代码
1、SIFT特征提取并建立视觉词典
# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
#from PCV.localdescriptors import sift
import sift
#获取图像列表
#imlist = get_imlist('E:/Vision/sift/BagOfFeature/u')
imlist = get_imlist('E:/Vision/sift/BagOfFeature/datas/jpg')
nbr_images = len(imlist)
print(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('test77_test')
voc.train(featlist, 1000, 10)
#保存词汇
with open('E:/Vision/sift/BagOfFeature/BOW/vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)
词汇保存在.pkl文件中,voc.train(featlist, 1000, 10)中的第二个参数对应维度K,不同的K值生成的词汇数量不同,分别存储在不同的文件件中,以下分别对应K=50,100,500,1000的词汇
2、建立图像索引和数据库
import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
from PCV.localdescriptors import sift
from PCV.imagesearch import imagesearch
from PCV.geometry import homography
from sqlite3 import dbapi2 as sqlite # 使用sqlite作为数据库
#提取特征描述子以建立视觉单词词汇
#获取图像列表
#imlist = get_imlist('E:/Vision/sift/BagOfFeature/u')
imlist = get_imlist('E:/Vision/sift/BagOfFeature/datas/jpg')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
# load vocabulary
#载入词汇
with open('E:/Vision/sift/BagOfFeature/BOW/vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)
#创建索引
indx = imagesearch.Indexer('testImaAdd1000.db',voc) # 在Indexer这个类中创建表、索引,将图像数据写入数据库
indx.create_tables() # 创建表
# go through all images, project features on vocabulary and insert
#遍历所有的图像,并将它们的特征投影到词汇上
for i in range(nbr_images)[:888]:
locs,descr = sift.read_features_from_file(featlist[i])
indx.add_to_index(imlist[i],descr) # 使用add_to_index获取带有特征描述子的图像,投影到词汇上
# 将图像的单词直方图编码存储
# commit to database
#提交到数据库
indx.db_commit()
con = sqlite.connect('testImaAdd1000.db')
print (con.execute('select count (filename) from imlist').fetchone())
print (con.execute('select * from imlist').fetchone())
不同的维度K生成不同的数据库,所以数据库名字要和前面的不一样
3、图像检索
import pickle
from PCV.localdescriptors 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('E:/Vision/sift/BagOfFeature/datas/jpg')
nbr_images = len(imlist)
print(nbr_images)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
with open('E:/Vision/sift/BagOfFeature/BOW/vocabulary.pkl', 'rb') as f:
voc = pickle.load(f)
src = imagesearch.Searcher('testImaAdd1000.db',voc)
# index of query image and number of results to return
#查询图像索引和查询返回的图像数
q_ind = 17
nbr_results =8
# 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[1:8]) #常规查询
imagesearch.plot_results(src,res_geom[1:8]) #重排后的结果
4、结果:
使用的图片集一共有10种类型,每种类型大概30多张,共315张图片
数据集下载
查询的图片:
kmeans聚类时选取维度K不同得到的结果也不同,下列分别输出匹配前七的图片:
50维
常规查询
重排后的结果:
100维常规查询:
100维重排后:
500维常规查询:
500维重排后:
1000维常规查询:
1000维重排后:
总结:
总体来看,匹配效果并不是很好,可能 是由于图片集只有300多张,数据量不够,特征点不够多。
测试的图片其实是一只狗,但发现匹配的结果很多是猫,而且随着维度的增加,匹配到猫的数量越多,可能是因为测试图片的动物和猫的颜色纹理特征都有很大的相似,所以匹配错误。
但是相对前几维来说,随着维度增加,查询的效果越好。重排后的结果和常规查询一模一样,可能还是图片集数量少,图片特征不明显,匹配的点少。