Bag of Feature 是一种图像特征提取方法,它借鉴了文本分类的思路(Bag of Words),从图像抽象出很多具有代表性的关键词,形成一个字典,再统计每张图片中出现的关键词数量,得到图片的特征向量。
Bag of Words 是文本分类中一种通俗易懂的策略。一般来讲,如果我们要了解一段文本的主要内容,最行之有效的策略是抓取文本中的关键词,根据关键词出现的频率确定这段文本的中心思想。比如:如果一则新闻中经常出现「iraq」、「terrorists」,那么,我们可以认为这则新闻应该跟伊拉克的恐怖主义有关。而如果一则新闻中出现较多的关键词是「soviet」、「cuba」,我们又可以猜测这则新闻是关于冷战的。(如下图)
这里所说的关键词,就是「Bag of words」中的 words ,它们是区分度较高的单词。根据这些 words ,我们可以很快地识别出文章的内容,并快速地对文章进行分类。
而 Bag of Feature 也是借鉴了这种思路,只不过在图像中,我们抽出的不再是一个个「word」,而是图像的关键特征「Feature」,所以研究人员将它更名为「Bag of Feature」。
Bag of Feature的本质是提出一种图像的特征表示方法。
按照Bag of Word的思想,首先我们要找到图像中的关键词,而且这些关键词必须具备较高的区分度。实际过程中,通常会采用SIFT特征。
特征提取:
有了特征之后,我们会将这些特征通过聚类算法得出很多聚类中心。这些聚类中心通常具有较高的代表性,比如对于人脸来说,虽然不同人的眼睛、鼻子等特征都不尽相同,但它们往往具有共性,而这些聚类中心就代表了这类共性。我们将这些聚类中心组合在一起,形成一部字典(CodeBook)。
特征聚类:
对于图像中的每个「SIFT」特征,我们能够在字典中找到最相似的聚类中心,统计这些聚类中心出现的次数,可以得到一个向量表示(直方图),这些向量就是所谓的「Bag」。这样,对于不同类别的图片,这个向量应该具有较大的区分度,基于此,我们可以训练出一些分类模型(SVM等),并用其对图片进行分类。
TF-IDF是一种用于信息检索的常用加权技术,在文本检索中, 用以评估词语对于一个文件数据库中的其中一份文件的重要程度。 词语的重要性随着它在文件中出现的频率成正比增加,但同时会随着它在文件数据库中出现的频率成反比下降。TF的主要思想是:如果某个关键词在一篇文章中出现的频率高,说明该词语能够表征文章的内容,该关键词在其它文章中很少出现,则认为此词语具有很好的类别区分度,对分类有很大的贡献。 IDF的主要思想是:如果文件数据库中包含词语A的文件越少,则IDF越大,则说明词语A具有很好的类别区分能力。
词频(Term Frequency,TF)指的是一个给定的词语在该文件中出现的次数。如:tf = 0.030 ( 3/100 )表示在包括100个词语的文档中, 词语’A’出现了3次。
逆文档频率(Inverse Document Frequency,IDF)是描述了某一个特定词语的普遍重要性,如果某词语在许多文档中都出现过,表明它对文档的区分力不强,则赋予较小的权重;反之亦然。如:idf = 13.287 ( log (10,000,000/1,000) )表示在总的10,000,000个文档中,有1,000个包含词语’A’。
最终的TF-IDF权值为词频与逆文档频率的乘积。
在索引图像前,需要建立一个数据库。对图像进行索引就是从这些图像中提取描述子,利用词汇将描述子转换成视觉单词,并保持视觉单词及对应图像的单词直方图。在这之前需要先安装sqlite库。
数据集(103张图片):
要为数据集里的图片创建视觉单词词汇,首先载入图像列表,利用SIFT提取特征描述子,获取特征列表,创建Vocabulary对象生成并保存词汇。
# -*- coding: utf-8 -*
import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
from PCV.localdescriptors import sift
# 要记得将PCV放置在对应的路径下
# 获取图像列表
imlist = get_imlist('D:\\picture\\') # 存放数据集的路径
nbr_images = len(imlist) # 获取数据集的长度
# nbr_images = 300 # 可以是自己选择用多少张图片作为训练数据集
# 获取特征列表
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('imglltest')
voc.train(featlist, 300, 10)
# 保存词汇
with open('D:\\picture\\vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)
print 'vocabulary is:', voc.name, voc.nbr_words
因为本部分有数据库操作,使用了python中的pysqlite库,pysqlite库需要自己安装,后面也有附安装教程。
本部分主要实现目的是将模型数据导入数据库。照常先载入图像列表、特征列表及词汇,创建Indexer类对象利用数据库创建图像索引。
# -*- 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
# 要记得将PCV放置在对应的路径下
# 获取图像列表
imlist = get_imlist('D:\\picture\\') # 存放数据集的路径
nbr_images = len(imlist)
# nbr_images = 300
# 获取特征列表
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]
# 载入词汇
with open('D:\\picture\\vocabulary.pkl', 'rb') as f: # 读取再上一步中保存的.pkl文件
voc = pickle.load(f)
# 创建索引
index = imagesearch.Indexer('testImaAdd.db', voc) # 创建数据库
index.create_tables() # 创建数据库表单
# 遍历所有的图像,并将它们的特征投影到词汇上
for i in range(nbr_images)[:300]:
locs, descr = sift.read_features_from_file(featlist[i])
index.add_to_index(imlist[i], descr)
# 提交到数据库
index.db_commit()
con = sqlite.connect('testImaAdd.db') # 连接到数据库
print con.execute('select count (filename) from imlist').fetchone() # 数据库操作
print con.execute('select * from imlist').fetchone()
在前面我们已经建立了图像索引,现在就可以在数据库中搜索相似的图像了。本部分创建了Searcher对象,执行定期查询,并将结果保存在res_reg列表中,然后载入res_reg列表中每一副图像的特征,并和查询图像进行匹配。最终获得排序好的检索结果图像。
# -*- 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
# 要记得将PCV放置在对应的路径下
# 载入图像列表
imlist = get_imlist('D:\\picture\\') # 存放数据集的路径
nbr_images = len(imlist)
# nbr_images = 300
# 载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
# 载入词汇
with open('D:\\picture\\vocabulary.pkl', 'rb') as f: # 存放模型的路径
voc = pickle.load(f)
src = imagesearch.Searcher('testImaAdd.db', voc)
# 查询图像索引和查询返回的图像数
q_ind = 40 # 查询图片的索引
nbr_results = 6
# 常规查询(按欧式距离对结果排序)
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 = {}
# 载入候选图像的特征
for ndx in res_reg[1:]:
locs, descr = sift.read_features_from_file(featlist[ndx])
# 获取匹配数
# get matches执行完后会出现两张图片
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[:6]) # 常规查询
imagesearch.plot_results(src, res_geom[:6]) # 重排后的结果
(1)第一组
测试图片:
常规查询:
建立RANSAC模型后:
(2)第二组:
测试图片:
常规查询:
建立RANSAC模型后:
由上面实验结果对比,可以看出所得检测结果较准确,原图像都是远景,所以检测出来的sift角点较少,导致匹配没那么准确。第一组原图为尚大楼,但检测结果中包含了其他建筑的图片。第二组是大海,能检测出都含有水的较准确的相似结果。这说明此算法在一些检测程度上效果还不足,可能在创建词汇上特征描述相似,还不够准确独特。而对于常规查询和建立RANSAC模型后查询的结果对比发现,图片结果基本一致,但顺序发生了变化。建立RANSAC模型后的重排是用单应性进行拟合,再导入候选图像特征进行排序查询,常规查询只是进行了简单的索引和查询,因此重排后的顺序是由匹配度高到低来进行,会更加准确。
第一次去官网下了最新版的,但pip install后报错了,是由于和我的python 2.7版本不兼容,后来换较低的版本下载即可。
下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl
1.运行时出现报错“table imlist already exists”,在创建词汇表单时说已存在,原因是上一次运行时项目里的.db文件没有删掉,删掉之后重新运行即解决报错。
2.在显示图像时会报出超时的错误,虽然不影响结果,但也没找到解决办法。