计算机视觉——基于Bag Of Word的图像检索

1. Bag-of-words简介

BoW模型最初是为解决文档建模问题而提出的,因为文本本身就是由单词组成的。它忽略文本的词序,语法,句法,仅仅将文本当作一个个词的集合,并且假设每个词彼此都是独立的。这样就可以使用文本中词出现的频率来对文档进行描述,将一个文档表示成一个一维的向量。

将BoW引入到计算机视觉中,就是将一幅图像看着文本对象,图像中的不同特征可以看着构成图像的不同词汇。和文本的BoW类似,这样就可以使用图像特征在图像中出现的频率,使用一个一维的向量来描述图像。

要将图像表示为BoW的向量,首先就是要得到图像的“词汇”。通常需要在整个图像库中提取图像的局部特征(例如,sift,orb等),然后使用聚类的方法,合并相近的特征,聚类的中心可以看着一个个的视觉词汇(visual word),视觉词汇的集合构成视觉词典(visual vocabulary) 。 得到视觉词汇集合后,统计图像中各个视觉词汇出现的频率,就得到了图像的BoW表示。

2. Bag-of-words原理及流程

2.1. 特征提取

过分割、密集或随机采集、关键点或稳定区域、显著区域等方式使图像形成不同的图像块,并获得各图像块处的特征。

特征必须具有较高的区分度,而且要满足旋转不变性以及尺寸不变性等,通常采用SIFT特征(也可以采用SUFT、Harrist等特征提取算法 )。SIFT会从图片上提取出很多特征点,每个特征点都是 128 维的向量,如果图片足够多的话,我们会提取出一个巨大的特征向量库。

SIFT特征提取原理参考博文:https://blog.csdn.net/weixin_45617915/article/details/104679052

在这里插入图片描述

2.2. 学习"视觉词典"

提取完特征后,进行学习"视觉词典" (visual vocabulary),采取聚类算法对这些特征向量进行聚类。通才采用k-means聚类。
在这里插入图片描述
K-Means聚类算法

  • 最小化每个特征 x i x_i xi与其对应的聚类中心 m k m_k mk之间的欧氏距离
  • D ( X , M ) = ∑ c l u s t e r   k ∑ p o i n t   i   i n   c l u s t e r   k ( x i − m k ) 2 D(X,M)= \sum_{cluster \ k} \sum_{point \ i \ in \ cluster \ k}(x_i-m_k)^2 D(X,M)=cluster kpoint i in cluster k(ximk)2

算法流程:

  • 随机初始化K个聚类中心
  • 重复下述步骤直至算法收敛:
    • 对应每个特征,根据距离关系赋值给某个中心/类别
    • 对每个类别,根据其对应的特征及重新计算聚类中心

视觉单词数量即K一般取3000~10000, 即图像整体描述的直方图维度为3000~10000。在本次实验中我们将选取多种维度学习视觉词典,(比如:10,50,100,1000,5000)。

聚类是实现视觉词典(visual vocabulary)的关键:

  • 无监督学习策略。
  • K-Means算法获取的聚类中心作为直觉单词(codevector)。
  • 视觉词典可以通过不同的训练集协同训练获得。
  • 一旦训练集准备足够充分,训练出来的视觉词典将具有普适性。

假设词典的大小为100,即有100个词。用K-means算法对所有的patch进行聚类,k=100,当k-means收敛时,我们也得到了每一个聚类最后的质心,那么这100个质心(维数128)就是词典里的100个词了,词典构建完毕。

视觉词典样例如下:
在这里插入图片描述

2.3. 对图像特征集进行量化

针对输入图片对应的特征集,根据视觉词典进行量化。

视觉词典用于对输入图像的特征集进行量化

  • 对于输出特征,量化的过程时将该特征映射到距离其最接近的视觉的单词,并实现计数。

利用单词表的中词汇表示图像。利用SIFT算法,可以从每幅图像中提取很多个特征点,这些特征点都可以用单词表中的单词近似代替,通过统计单词表中每个单词在图像中出现的次数,可以将图像表示成为一个n维数值向量。但是考虑到很大概率实际会发生这种情况:
通常,在每篇文本中,如“这”“和”“是”这类词出现的概率比较大,但是他们对于区别不同的文本并没有多大的作用,所以在单词计数时会忽略掉这些常用词,这些常用词称为停用词。于是我们为了提高匹配的精度,给每个特征点都赋上权重。

最常用的权重是TF-IDF(term frequency-inverse document frequency,词频- 逆向文档频率)

词频 Term Frequency,TF指的是一个给定的词语在该文件中出现的次数。如:一篇文档总词数为1000,单词A出现次数为3次,则TF=3/1000=0.003。其主要思想是:如果某个关键词在一篇文章中出现的频率高,说明该词语能够表征文章的内容,该关键词在其它文章中很少出现,则认为此词语具有很好的类别区分度,对分类有很大的贡献。

逆文档频率 Inverse Document Frequency,IDF是描述了某一个特定词语的普遍重要性,如果某词语在许多文档中都出现过,表明它对文档的区分力不强,则赋予较小的权重;反之亦然。如:总共有1000篇文档,有100个包含词语A,则IDF= log (1000/1,00) = 3.287。IDF的主要思想是:如果文件数据库中包含词语A的文件越少,则IDF越大,则说明词语A具有很好的类别区分能力。

单词 w w w 在文档 d d d中的词频是:
t f w , d = n w ∑ j n j tf_{w,d}=\frac{n_w}{\sum_j n_j} tfw,d=jnjnw
其中 n w n_w nw是单词 w w w 在文档 d d d中出现的次数。为了归一化,将 n w n_w nw除以整个文档中单词的总数。

逆向文档频率为:
i d f w , d = l o g ∣ ( D ) ∣ ∣ d : w ∈ d ∣ idf_{w,d}=log\frac{|(D)|}{|{d:w\in d}|} idfw,d=logd:wd(D)

最终的TF-IDF权值为词频与逆文档频率的乘积。

2.4. 将图像转换为视觉单词的频率直方图

把输入图像,根据TF-IDF转化成视觉单词( visual words)的频率直方图 ,用视觉单词直方图来表示图像。如图下所示:
在这里插入图片描述

2.5. 构造特征到图像的倒排表

构造特征到图像的倒排表,通过倒排表快速索引相关图像。

利用下面的例子说明倒排表:

文档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}
“a”: {2,1}
“banana”: {2,1}

以此可以快速使用倒排表进行计算测试图像与数据库中训练图像之间的相似性,索引到相关图片,然后和测试图像特征进行直方图匹配。

2.6. 根据索引结果进行直方图匹配

1.利用索引获取候选图像
利用建立起来的索引找到包含特定的所有图像,即相似物体、相似的脸、相似颜色等;为了获得包含多个单词的候选图像,例如一个单词直方图中全部非零元素,在每个单词上进行遍历,得到包含改单词的图像列表,合并这些列表。然后创建一个元组列表有单词id和次数构成,其中次数以候选列表中每个单词出现的次数为准。同时还以元组中的第二个元素为准进行排序。最后会得到一个包含图像id的列表,排在列表最前面的是最好的匹配图像。

2.用一幅图像进行查询
利用一副图像进行查询是没必要进行完全的搜索,对了比较单词直方图,需要从数据库中读入图像单词直方图,检索每个单词直方图及候选图像列表,对于每个候选图像,用标准的欧式距离比较它和查询图像间的直方图,返回一个已经排好序的的元组列表。

3. 实验流程及代码

3.1 数据集准备

构造不小于100张图片的数据集,如下:
在这里插入图片描述
对数据集压缩得到压缩后的数据集,此操作可以较大程度降低提取SIFT特征的时间。压缩代码如下:

from PIL import Image
from pylab import *

for i in range(1, 101):
    pil_im = Image.open("E:\\data1\\"+str(i)+".jpg")
    pil_im.thumbnail((800, 800))
    pil_im.save('E:\\data\\'+str(i)+'.jpg')

3.2 SIFT特征提取

针对数据集,做特征提取。SIFT特征提取代码如下:

# -*- coding: utf-8 -*-
from PCV.localdescriptors import sift
from PCV.tools.imtools import get_imlist

# 获取图像列表
imlist = get_imlist('E:\\bagofword\\data')
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])

3.3 学习“视觉词典(visual vocabulary)”

根据SIFT特征提取结果,采用k-means算法学习“视觉词典(visual vocabulary)”,在进行学习的时候分别设置维度为50,100,1000,5000时得到相应的视觉词典,此操作只需更改
voc.train(featlist, 50, 10)中的第二个参数即可。

Vocabulary是调用PCV下imagesearch中的文件vocabulary.py,该文件代码的作用是创建一个词汇类,以及在训练图像数据集上训练出一个词汇。其中包含了一个有单词聚类中心与每个单词对应的逆向文档频率构成的向量。该词汇类中还包含了train()函数。

train()函数是从文件中读取特征然后将所有特征并在一起,以便进行K-means聚类,遍历所有的训练图像,这是Bag of features过程的前三步工作—提取特征和学习“视觉词典”并且对视觉词典进行量化。train()方法获取包含有.sift描述子文件列表和词汇单词数k(聚类中心数),在K-means聚类过程中容易使用过多特征,所以对训练数据进行下采样加快训练速度。

最后采用pickle模块中保存视觉词汇,pickle.dump(voc, f)是将词汇写入到.pkl文件中。

代码如下:

# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import vocabulary
from PCV.localdescriptors import sift
from PCV.tools.imtools import get_imlist
# 获取图像列表
imlist = get_imlist('E:\\bagofword\\data')
nbr_images = len(imlist)
# 获取特征列表
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]
# 生成词汇
voc = vocabulary.Vocabulary('ukbenchtest')
voc.train(featlist, 1000, 10)
# 保存词汇
with open('E:\\bagofword\\data\\vocabulary1000.pkl', 'wb') as f:
    pickle.dump(voc, f)
print('vocabulary is:', voc.name, voc.nbr_words)

3.4 对数据集图像的特征集进行量化

imagesearch.Indexer(‘testImaAdd.db’, voc) 是指调用imagesearch中的索引器Indexer类,初始化(连接上数据库,初始化词汇对象之后调用了Indexer类里create_tables()函数,create_tables()用于创建数据库表单及一些索引用以加快搜索速度,里面包含了图像文件名、词汇、单词直方图。add_to_index(imlist[i], descr)作用是在索引中添加没有被索引的图像。获取图像id(图像文件名的id号)及单词(将描述子投影到词汇上,创建单词直方图),将每个单词和图像链接起来,然后存储图像的单词直方图。最后就是保存所有数据库操作,将更改写入数据库文件。

代码实现:

# -*- 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:\\bagofword\\data')
nbr_images = len(imlist)
# 获取特征列表
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]

# 载入词汇
with open('E:\\bagofword\\data\\vocabulary100.pkl', 'rb') as f:
    voc = pickle.load(f)
# 创建索引
indx = imagesearch.Indexer('testImaAdd.db', voc)
indx.create_tables()
# 遍历所有的图像,并将它们的特征投影到词汇上
for i in range(nbr_images)[:100]:
    locs, descr = sift.read_features_from_file(featlist[i])
    indx.add_to_index(imlist[i], descr)
# 提交到数据库
indx.db_commit()
con = sqlite3.connect('testImaAdd.db')
print(con.execute('select count (filename) from imlist').fetchone())
print(con.execute('select * from imlist').fetchone())

3.5 建立图像索引进行图像检索

上一步骤已经建立了图像索引,现在就可以在数据库中搜索相似的图像了。

在这里我们运用到了imagesearch中的Searcher类,常规查询时调用了query()方法,此方法作用是获取查询图像的文件名,检索其单词直方图和与候选图像列表(即其具有相似单词直方图的图像列表)。利用单词索引获得候选集,然后在候选集上进行逐一比较。对每个候选图像,都用标准的欧氏距离度量它和查询图像直方图的相似性,并按照相似性程度大小排序,返回排序后的距离及对应数据库ids大小,排在列表最前的是最好的匹配图像。

本部分创建了Searcher对象,执行定期查询,并将结果保存在res_reg列表中,然后载入res_reg列表中每一副图像的特征,并和查询图像进行匹配。最终获得排序好的检索结果图像。

代码实现:

# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import imagesearch
from PCV.geometry import homography
from PCV.tools.imtools import get_imlist
from PCV.localdescriptors import sift
import warnings
warnings.filterwarnings("ignore")

# 载入图像列表
imlist = get_imlist('E:\\workspace\\cv_code\\bagofword\\data')
nbr_images = len(imlist)
# 载入特征列表
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]

# 载入词汇
with open('E:\\workspace\\cv_code\\bagofword\\data\\vocabulary1000.pkl', 'rb') as f:
    voc = pickle.load(f, encoding='iso-8859-1')

src = imagesearch.Searcher('testImaAdd1000.db', voc)

# 查询图像索引和查询返回的图像数
q_ind = 1
nbr_results = 20

# 常规查询(按欧式距离对结果排序)
res_reg = [w[1] for w in src.query(imlist[q_ind])[:nbr_results]]
print('top matches (regular):', str(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])  # 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[:6])  # 常规查询
imagesearch.plot_results(src, res_geom[:6])  # 重排后的结果

这里有一个重排序的过程,先将检索出来的挑选出来,再把每一张图像进行一一比对,按照相似程度得到一个排列顺序,具体:
首先,载入图像列表、特征列表(分别包含图像文件名和 SIFT 特征文件)及词汇。 然后,创建一个 Searcher 对象,执行定期查询,并将结果保存在 res_reg 列表中。然 后载入 res_reg 列表中每一幅图像的特征,并和查询图像进行匹配。单应性通过计算 匹配数和计数内点数得到。最终,我们可以通过减少内点的数目对包含图像索引和内点数的字典进行排序。

4. 实验结果及分析

4.1 SIFT特征提取运行结果

在运行SIFT特征提取之后在数据集的文件夹中生成每一个图像对应的.sift文件,数据集文件夹中的部分内容截图如下:
在这里插入图片描述

4.2 学习视觉词典运行结果

如下图,在数据集文件夹中生成4个视觉词典的文件,其中维度分别为50、100、1000、5000:
在这里插入图片描述
可以看到随着维度的增加,视觉词典的大小也随之增加,在运行过程中发现维度越大,消耗 的时间也随之增加。

4.3 对数据集图像的特征集进行量化运行结果

对数据集中的所有图像进行量化,为所有图像建议索引,再遍历所有的图像,并将它们的特征投影到词汇上,最终提交到数据库保存下来。此处选取的词汇表是维度为100的词汇表。
控制台输出为下:
如下为数据集中100张图形建立了索引。在这里插入图片描述
对数据集中所有图像的特征投影到词汇上完成量化的过程,保存在数据库文件中,生成的数据库文件如下:
(由于词汇表有四个维度的生成,即50、100、1000、5000,所以量化之后的数据库也应该有对应的四个。)
在这里插入图片描述

4.4 图像检索的运行结果

在该实验结果中,第一张图像为需进行检索的原图像,第二至六张图像为第一张图像的检索结果。
由如下四组实验结果可以看到:当维度为50、100时,原图像与检索结果之间并无较大的关联;当维度为1000、5000时,检索结果与原图像的关联较大,皆属于同一个场景。

可以得出结论:当维度越来越大的时候,检索的准确率较高。思考当维度越来越大的时候,视觉词典的细分程度较高,针对于图像的检索可以对细小的元素进行比对,即比对结果较为准确。

再观察当维度为5000的时候的常规查询和重排后的结果,发现常规查询的结果较好,检索结果的前四张图像皆为同一建筑物的不同视角,而重排后的检索结果前两张图像是截然不同的两张图像,由此可以得出结论:常规查询的结果较好。

思考:在重排的代码中,有RANSAC算法的流程,因为RANSAC算法用于剔除误匹配,考虑在剔除误匹配的时候清楚了部分正确匹配,所以对检索结果的影响较大。

4.4.1 维度为50时

常规查询:
在这里插入图片描述
重排后:
在这里插入图片描述

4.4.2 维度为100时

常规查询:
在这里插入图片描述

重排后:
在这里插入图片描述

4.4.3 维度为1000时

常规查询:
在这里插入图片描述
重排后:
在这里插入图片描述

4.4.4 维度为5000时

常规查询:
在这里插入图片描述
重排后:
在这里插入图片描述

5. 实验总结

Bag of word模型的优缺点:

优点:BoW 模型因为其简单有效的优点而得到了广泛的应用。

缺点:BoW模型忽略了词典中的所有视觉词所表示的多种局部模式对应于原始图像中的图像块或区域之间的空间位置关系和语义信息,简单地说,就是BoW模型存在缺少空间信息和语义信息的不足的问题。

解决方法:通过在原始阁像屮划分规则的栅格(RegularGrid)、考虑局部特征和周围其他局部特征之间的空间排列(Spatial Layout)关系、空间金字塔匹配(SpatialPyramid Matching)或者事后验证几何关系(Verifying Geometric Relationships)的方法为BoW模型添加必要的空间位置信息;为了缓解其缺少语义信息的问题。

6. 实验遇到的问题

实验中遇到如下报错:

ImportError: No module named pysqlite2.

查询网络可知python3.6中已将pysqlite2取消,即进入报错的.py文件用如下代码段替换:from pysqlite2 import dbapi2 as sqlite

import sqlite3 as sqlite
import os,sys
from functools import cmp_to_key
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值