当我们对特定的一张图像进行图像特征匹配时,若采用暴力匹配法,则需要多久呢?
(1)250,000 张图像 --> ~ 310亿个图像对 --> 每个图相对2秒 匹配–> 500台并行计算机需要1 年才能完成计算
(2) 1,000,000 张图像–> 5000亿个图像对 --> 500台并行计算机需要15 年才能完成计算
可以看出,采用暴力匹配法,其效率是及其之低的,复杂度过高。
下面介绍一种方法-------Bag of features
Bag of Feature 是一种图像特征提取方法,它借鉴了文本分类的思路(Bag of Words),从图像抽象出很多具有代表性的关键词,形成一个字典,再统计每张图片中出现的关键词数量,得到图片的特征向量。
在介绍Bag of features之前,我们先来了解一下Bag of words模型
Bag Of Words
一、介绍
Bag of Words 是文本分类中的一种策略,是信息检索领域常用的文档表示方法。如果我们要了解一段文本的主要内容,最有效的策略是获取文本中的关键词,根据关键词出现的频率确定这段文本的中心思想。
在信息检索中,BOW模型是假定对于一个文档,忽略它的单词顺序和语法、句法等要素,将其仅仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的,不依赖于其它单词是否出现。
比如:如果一则新闻中经常出现iraq、terrorists,那么,我们可以认为这则新闻应该跟伊拉克的恐怖主义有关。如果一则新闻中出现较多的关键词是soviet、cuba,我们可以猜测这则新闻是关于冷战的
这里所说的关键词,就是Bag of words中的 words ,它们是在一片文档中出现频率较高的单词。根据这些 words ,我们可以很快地识别出文章的内容,并快速地对文章进行分类。
图像可以视为一种文档对象,图像中不同的特征可看做构成图像的词汇,其中相近的区域或其特征可以视作为一个词。这样,就能够把文本检索及分类的方法用到图像分类及检索中去。
由BOW对文档分类的思想我们可以引申出Bag of Features-----即对图像进行分类
Bag Of Features
概念
Bag-of-Features模型是仿照文本检索BOW的方法,把每幅图像描述为一个局部区域/关键点特征的无序集合。使用聚类算法(如K-means)将局部特征进行聚类,每个聚类中心被看作是词典中的一个视觉词汇(Visual Word),相当于文本检索中的 “word”,视觉词汇由聚类中心对应特征形成的码字(code word)来表示(可看当为一种特征量化过程)。所有视觉词汇形成一个视觉词典(Visual Vocabulary),对应一个码本(code book),即码字的集合(数据库)。
词典中所含词的个数反映了词典的大小。图像中的每个特征都将被映射到视觉词典的某个词上,这种映射可以通过计算特征间的距离去实现,然后统计每个视觉词的出现与否或次数,图像可描述为一个维数相同的直方图向量,即Bag-of-Features。
基础流程
1、特征提取
2、学习“视觉词典”
3、针对输入特征集,根据视觉词典进行量化
4、把输入图像转化成视觉单词(visual words) 的频率直方图
5、构造特征到图像的倒排表,通过倒排表快速 索引相关图像
6、根据索引结果进行直方图匹配
(一)特征提取
特征必须具有较高的区分度,而且要满足旋转不变性以及尺寸不变性等,因此,我们通常都会采用SIFT特征提取。SIFT会从图片上提取出很多特征点,每个特征点都是 128 维的向量,因此,当图片足够多的话,会提取出一个巨大的特征向量库。
(二)学习“视觉词典(visual vocabulary)”
采用聚类方式,将所有特征向量归类
K-Means聚类算法
最小化每个特征 xi 与其相对应的聚类中心 mk 之间的欧式距离
算法流程
(1)随机初始化 K 个聚类中心
(2)重复下述步骤直至算法收敛:
对应每个特征,根据距离关系赋值给某个中心/类别
对每个类别,根据其对应的特征集重新计算聚类中心
(3)聚类是实现 visual vocabulary /codebook的关 键
无监督学习策略
k-means 算法获取的聚类中心作为 codevector
Codebook 可以通过不同的训练集协同训练获得
一旦训练集准备足够充分, 训练出来的码本( codebook)将具有普适性
(4)码本/字典用于对输入图片的特征集进行量化
对于输入特征,量化的过程是将该特征映射到距离其最接近 的 codevector ,并实现计数
码本 = 视觉词典
Codevector = 视觉单词
由聚类中心代表的视觉词汇形成视觉词典:
聚类完成后,我们就得到了这 k 个向量组成的字典
(三)针对输入特征集,根据视觉词典进行量化
视觉词典存在的问题
(1)如何选择视觉词典/码本的规模? •
太少:视觉单词无法覆盖所有可能出现的情况 •
太多: 计算量大,容易过拟合
(2)如何提升计算效率? •
Vocabulary trees (Nister & Stewenius, 2006)
(四)把输入图像转化成视觉单词(visual words) 的频率直方图
对于上一步训练得到的字典,是为了这一步对图像特征进行量化。对于一幅图像而言,我们可以提取出大量的SIFT特征点,但这些特征点缺乏代表性。因此,这一步,是根据字典重新提取图像的高层特征。
对于图像中的每一个SIFT特征,都可以在字典中找到一个最相似的 visual word,这样,我们可以统计一个 k 维的直方图,代表该图像的SIFT特征在字典中的相似度频率。
对于上图这辆车的图片,我们匹配图片的SIFT向量与字典中的 visual word,统计出最相似的向量出现的次数,最后得到这幅图片的直方图向量。
当我们得到每幅图片的直方图向量后,剩下的这一步跟以往的步骤是一样的。无根据数据库图片的向量以及图片的标签,训练分类器模型。然后对需要预测的图片,我们仍然按照上述方法,提取SIFT特征,再根据字典量化直方图向量,用分类器模型对直方图向量进行分类。当然,也可以直接根据 KNN 算法对直方图向量做相似性判断。
给定图像的bag-of-features直方图特征,如何 实现图像分类/检索?
给定输入图像的BOW直方图, 在数据库中查找 k 个最近邻 的图像
对于图像分类问题,可以根据这k个近邻图像的分类标签, 投票获得分类结果
当训练数据足以表述所有图像的时候,检索/分类效果良 好
常用参数设置:
视觉单词数量(K-means算法获取的聚类中心)一般为 K=3000~10000. 即图像整体描述的直方图维度为 3000~10000.
求解近邻的方法一般采用L2-范数:即 Euclidean 距离.
目前普适的视觉单词采用 Lowe 的SIFT特征描述子. 特征 点检测采用 DOG (Difference of Gaussians).
TF-IDF
在一篇文档里,“的”“你”“我”等字眼的出现频率高,那么这些word在直方图上的bin就会比较高,并且在每篇文档里都会出现,但是这些并不能作为文档的标识,因此要弱化这些共性特征的权重。
TF-IDF是一种用于信息检索的常用加权技术,在文本检索中,用来评估“word”对于一个文件数据库中的其中一份文件的重要程度。词语的重要性随着它在文件中出现的频率成正比增加,但同时会随着它在文件数据库中出现的频率成反比下降。
TF的主要思想是:如果某个关键词在一篇文章中出现的频率高,说明该词语能够表征文章的内容,该关键词在其它文章中很少出现,则认为此词语具有很好的类别区分度,且对分类有很大的贡献。IDF的主要思想是:如果文件数据库中包含词语A的文件越少,则IDF越大,则说明词语A具有很好的类别区分能力。
• 词频(Term Frequency,TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(term count)的归一化,以防止它偏向长的文件。
如: tf = 0.030 ( 3/100 )表示在包括100个词语的文档中, 词语’A’出现了3次。
• 逆文档频率(Inverse Document Frequency,IDF)是一个词语普遍重要性的度量,如果某词语在许多文档中都出现过,表明它对文档的区分力不强,则赋予较小的权重;反之亦然。
某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。如:idf = 13.287 ( log (10,000,000/1,000) )表示在总的10,000,000个文档中,有1,000个包含词语’A’。
最终的TF-IDF权值为词频与逆文档频率的乘积。
倒排表
基本思想:直方图映射为特征向量
Ci :特征向量
Ii :数据库中的图片
假设现有一个集合(特征向量Ci分别在图片Ii中出现):
C1={I1,I101} C2={I2,I101} C50={I1,I5,I100} C3000={I1,I3,I101}
输入一张图Ix,这张图中分别有以下特征向量C1,C50,C3000
可以得出检索对象为I1,I3,I5,I100,I101
可以设置对阈值出现两次以上的图像进行检索,如本例中的I1,I101
使用倒排表可以避免一一比对的现象,降低计算时间
存在问题
然而传统的BoF图像分类问题也存在问题:
(1)SIFT局部描述子能较好的描述图像纹理信息(描述方向梯度),但是也有可能造成其他方面信息的丢失
(2)这种一个特征点对应于一个词的方法显然忽略图像空间上下文(Spatial Context Modeling),这很容易造成词语义的模糊
(3)图像中一般都存在同主题无关的内容,会影响分类的结果,改进方法是检测兴趣区域(detection of regions-of-interest(ROI))并给不同的权重。
(4)使用k-means聚类,不仅K和聚类中心如何选择一个难点,并且对于海量数据,输入矩阵的大数量将使得内存溢出及效率低下。
(5)字典大小的选择也是问题,字典过大,单词缺乏一般性,对噪声敏感,计算量大,关键是图象投影后的维数高;字典太小,单词区分性能差,对相似的目标特征无法表示。
(6)相似性测度函数用来将图象特征分类到单词本的对应单词上,其涉及线型核,塌方距离测度核,直方图交叉核等的选择。
实验代码
在这之前,要安装两个安装包
1.安装pyqt5 :pip install PyQt5
2.安装cherrypy : pip install cherrypy
# -*- 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
# load image list and vocabulary
#载入图像列表
imlist = get_imlist('first1000/')
nbr_images = len(imlist)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
with open('first1000/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 = 20
# 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]) #重排后的结果
# -*- 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('first1000/')
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)
#保存词汇
# saving vocabulary
with open('first1000/vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)
_test_candidates.py
# -*- 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('first1000/')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]
#载入词汇
f = open('first1000/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 ('ask using a histogram...')
print (src.candidates_from_histogram(iw)[:10])
src = imagesearch.Searcher('testImaAdd.db',voc)
print ('try a query...')
nbr_results = 12
res = [w[1] for w in src.query(imlist[0])[:nbr_results]]
imagesearch.plot_results(src,res)
ch07_cocabulary.py
# -*- 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('first1000/')
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)
#保存词汇
# saving vocabulary
with open('first1000/vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)
# -*- coding: utf-8 -*-
import cherrypy
import pickle
import urllib
import os
from numpy import *
#from PCV.tools.imtools import get_imlist
from PCV.imagesearch import imagesearch
import random
"""
This is the image search demo in Section 7.6.
"""
class SearchDemo:
def __init__(self):
# 载入图像列表
self.path = 'first1000/'
#self.path = 'D:/python_web/isoutu/first500/'
self.imlist = [os.path.join(self.path,f) for f in os.listdir(self.path) if f.endswith('.jpg')]
#self.imlist = get_imlist('./first500/')
#self.imlist = get_imlist('E:/python/isoutu/first500/')
self.nbr_images = len(self.imlist)
print (self.imlist)
print (self.nbr_images)
self.ndx = list(range(self.nbr_images))
print (self.ndx)
# 载入词汇
# f = open('first1000/vocabulary.pkl', 'rb')
with open('first1000/vocabulary.pkl','rb') as f:
self.voc = pickle.load(f)
#f.close()
# 显示搜索返回的图像数
self.maxres = 10
# header and footer html
self.header = """
<!doctype html>
<head>
<title>Image search</title>
</head>
<body>
"""
self.footer = """
</body>
</html>
"""
def index(self, query=None):
self.src = imagesearch.Searcher('testImaAdd.db', self.voc)
html = self.header
html += """
<br />
Click an image to search. <a href='?query='> Random selection </a> of images.
<br /><br />
"""
if query:
# query the database and get top images
#查询数据库,并获取前面的图像
res = self.src.query(query)[:self.maxres]
for dist, ndx in res:
imname = self.src.get_filename(ndx)
html += "<a href='?query="+imname+"'>"
html += "<img src='"+imname+"' alt='"+imname+"' width='100' height='100'/>"
print (imname+"################")
html += "</a>"
# show random selection if no query
# 如果没有查询图像则随机显示一些图像
else:
random.shuffle(self.ndx)
for i in self.ndx[:self.maxres]:
imname = self.imlist[i]
html += "<a href='?query="+imname+"'>"
html += "<img src='"+imname+"' alt='"+imname+"' width='100' height='100'/>"
print (imname+"################")
html += "</a>"
html += self.footer
return html
index.exposed = True
#conf_path = os.path.dirname(os.path.abspath(__file__))
#conf_path = os.path.join(conf_path, "service.conf")
#cherrypy.config.update(conf_path)
#cherrypy.quickstart(SearchDemo())
cherrypy.quickstart(SearchDemo(), '/', config=os.path.join(os.path.dirname(__file__), 'service.conf'))
service.conf
[global]
server.socket_host = "127.0.0.1"
server.socket_port = 8080
server.thread_pool = 50
tools.sessions.on = True
[/]
tools.staticdir.root = "实验代码的路径"
tools.staticdir.on = True
tools.staticdir.dir = ""
实验数据:1000张,有4张为拍摄同一物品的图片,每次输出10张匹配到的图
实验结果:
从实验结果可以看出,可以完整输出数据库中与之相像的图。