本文主要是介绍【python计算机视觉编程——7.图像搜索】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
python计算机视觉编程——7.图像搜索
- 7.图像搜索
- 7.1 基于内容的图像检索(CBIR)
- 从文本挖掘中获取灵感——矢量空间模型(BOW表示模型)
- 7.2 视觉单词
- **思想**
- **特征提取**:
- 创建词汇
- 7.3 图像索引
- 7.3.1 建立数据库
- 7.3.2 添加图像
- 7.4 在数据库中搜索图像
- 7.4.1 利用索引获取获选图像
- 7.4.2 用一幅图像进行查询
- 7.4.3 确定对比基准并绘制结果
- 7.5 使用几何特性对结果排序
7.图像搜索
利用文本挖掘技术对基于图像视觉内容进行图像搜索
7.1 基于内容的图像检索(CBIR)
CBIR用于检索在视觉上具有相似性的图像(颜色相似,纹理相似、图像中的物体或场景相似)。
将查询图像与数据库中所有的图像进行 完全比较(特征匹配)往往是不可行的,庞大的数据库回导致耗时过多。
矢量空间模型是一个用于表示和搜索文本文档的模型。这些矢量是由文本词频直方图构成的。换句话说,矢量包含了每个单词出现的次数。
通过单词计数来构建文档直方图向量v,从而建立文档索引。通常,在单词计数时会忽略掉一些常用词,“这” “和” “是”等,这些常用词称为停用词。由于每篇文档长度不同,故除以直方图总和将向量归一化成单位长度。对于直方图向量中的每个元素,一般根据每个单词的重要性来赋予相应的权重。通常,数据集(或语料库)中一个单词的重要性与它在文档中出现的次数成正比,而与它在语料库中出现的次数成反比。
-
单词w在文档d中的词频是
t f w , d = 单词 w 在文档 d 中出现的次数 文档 d 中单词的总数 = n w ∑ j n j {\rm tf}_{w,d}=\frac{单词w在文档d中出现的次数}{文档d中单词的总数}=\frac{n_w}{\sum_j n_j} tfw,d=文档d中单词的总数单词w在文档d中出现的次数=∑jnjnw -
逆向文档频率
i d f w , d = log 在语料库 D 中文档的数目 语料库中包含单词 w 的文档数的 = log ∣ ( D ) ∣ { d : w ∈ d } {\rm idf}_{w,d}=\log\frac{在语料库D中文档的数目}{语料库中包含单词w的文档数的}=\log\frac{|(D)|}{\{d:w\in d\}} idfw,d=log语料库中包含单词w的文档数的在语料库D中文档的数目=log{d:w∈d}∣(D)∣
7.2 视觉单词
首先需要建立视觉等效单词,通常采用sift局部描述子做到
使用sift特征描述子创建视觉单词词汇
from PIL import Image
import os
需用到如下两个函数
def process_image(imagename, resultname, params="--edge-thresh 10 --peak-thresh 5"):if imagename[-3:] != 'pgm':im = Image.open(imagename).convert('L')im.save('tmp.pgm')imagename = 'tmp.pgm' cmmd = str(".\sift.exe " + imagename + " --output=" + resultname + " " + params)os.system(cmmd)print('processed', imagename, 'to', resultname)
# 可以返回目录中所有jpg图像的列表
def get_imlist(path):return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]
imlist=get_imlist(r'.\first1000')
nbr_images=len(imlist)
featlist=[imlist[i][:-3]+'sift' for i in range(nbr_images)]for i in range(nbr_images):process_image(imlist[i],featlist[i])
-
创建类名为Vocabulary,内含有两个函数train和project
-
train函数:从图像特征文件中读取特征描述符,使用 K-means 聚类算法生成视觉单词,并计算每个图像的视觉单词直方图。随后,它计算每个视觉单词的 IDF,并保存训练数据的文件路径。
- 步骤:
- 读取特征:
- 从第一个特征文件中读取特征,并将其存储在 descriptors 中。
- 依次读取其他特征文件,将它们的特征垂直堆叠到 descriptors 中。
- 训练视觉单词:
- 使用 kmeans 聚类算法对特征进行聚类,得到视觉单词。
- descriptors[::subsampling, :]表示对特征进行子采样,以提高计算效率。
- self.voc存储视觉单词,distortion存储聚类的失真度。
- 计算视觉单词直方图:
- 对每个图像计算视觉单词直方图。
- 统计每个视觉单词在所有图像中的出现次数,计算 IDF 值。
- 存储训练数据:
- 保存特征文件路径到 self.trainingdata
- 读取特征:
- 步骤:
-
project函数:计算每张图像的视觉单词直方图
-
步骤
-
将特征描述符映射到视觉单词:
- 使用 vq(向量量化)方法将特征描述符映射到最近的视觉单词。
- words 包含每个特征描述符对应的视觉单词索引。
-
创建视觉单词直方图:
- 遍历 words,统计每个视觉单词的出现次数。
- 返回图像的视觉单词直方图 imhist。
-
-
class Vocabulary(object):def __init__(self,name):self.name=name # 词汇表的名称self.voc=[] # 存储视觉单词(词汇)的列表self.idf=[] # 逆文档频率(IDF)值列表,用于加权视觉单词self.trainingdata=[] # 训练数据的文件列表self.nbr_words=0 # 视觉单词的数量def train(self,featurefiles,k=100,subsampling=10):"""featurefiles:存储特征文件路径的列表,每个文件包含图像的特征k:视觉单词的数量,默认为 100subsampling:子采样比例,默认为10"""nbr_images=len(featurefiles)descr=[]descr.append(read_features_from_file(featurefiles[0])[1]) # 从第一个特征文件中读取特征,并将其存储在descriptors中descriptors=descr[0]for i in range(1,nbr_images): # 依次读取其他特征文件,将它们的特征垂直堆叠到 descriptors 中descr.append(read_features_from_file(featurefiles[0])[1])descriptors=np.vstack((descriptors,descr[i]))# 使用kmeans聚类算法对特征进行聚类,得到视觉单词# self.voc存储视觉单词,distortion存储聚类的失真度self.voc,distortion=kmeans(descriptors[::subsampling,:],k,1) #descriptors[::subsampling,:]:表示对特征进行子采样,以提高计算效率self.nbr_words=self.voc.shape[0]imwords=np.zeros((nbr_images,self.nbr_words)) #每一行表示一张图像的视觉单词直方图for i in range(nbr_images):imwords[i]=self.project(descr[i]) # 计算每张图像的视觉单词直方图,并将其存储在imwords的对应行中nbr_occurences=np.sum((imwords>0)*1,axis=0) # 计算每个视觉单词在多少张图像中出现过self.idf=np.log((1.0*nbr_images)/(1.0*nbr_occurences+1)) #计算每个视觉单词的逆文档频率(IDF)。IDF是用来加权视觉单词的,避免频繁出现的视觉单词占据主导地位。+1 是为了避免除零错误self.trainingdata=featurefiles # 保存训练数据文件路径列表,以便后续使用def project(self,descriptors):imhist=np.zeros((self.nbr_words)) # 使用 vq(向量量化)方法将特征描述符映射到最近的视觉单词words,distance=vq(descriptors,self.voc) # words 包含每个特征描述符对应的视觉单词索引for w in words:imhist[w]+=1return imhist
voc=Vocabulary('ukbenchtest')
voc.train(featlist,1000,10) # 使用 featlist 中的特征文件训练 voc 对象,设定视觉单词数量为 1000,子采样比例为 10with open('vocabulary.pkl','wb') as f:pickle.dump(voc,f)
print('vocabulary is:',voc.name,voc.nbr_words)
7.3 图像索引
7.3.1 建立数据库
from numpy import *
import pickle
import sqlite3 as sqlite
class Indexer(object):def __init__(self,db,voc):""" Initialize with the name of the database and a vocabulary object. """self.con = sqlite.connect(db) #使用SQLite的connect函数连接到指定的数据库文件db,并将连接对象存储在 self.con 中self.voc = voc # 将传入的 voc(词汇对象)存储在 self.voc 中,以便后续操作中使用def __del__(self):self.con.close() # 在对象被销毁时,关闭与数据库的连接,以释放资源。def db_commit(self):self.con.commit() # 提交当前事务,将对数据库的所有修改保存到数据库中。def create_tables(self): """ Create the database tables. """self.con.execute('create table imlist(filename)') #创建一个名为 imlist 的表,该表包含一个字段 filename,用于存储图像文件名。self.con.execute('create table imwords(imid,wordid,vocname)') # 创建一个名为 imwords 的表,包含字段 imid(图像ID)、wordid(视觉单词ID)和 vocname(词汇名称)。此表用于存储每张图像中视觉单词的出现情况。self.con.execute('create table imhistograms(imid,histogram,vocname)') #创建一个名为 imhistograms 的表,包含字段 imid(图像ID)、histogram(图像的视觉单词直方图)和 vocname(词汇名称)。此表用于存储每张图像的视觉单词直方图。 self.con.execute('create index im_idx on imlist(filename)') #为 imlist 表中的 filename 字段创建一个索引,以加快对图像文件名的查询速度。self.con.execute('create index wordid_idx on imwords(wordid)') #为 imwords 表中的 wordid 字段创建一个索引,以加快对视觉单词ID的查询速度。self.con.execute('create index imid_idx on imwords(imid)') #为 imwords 表中的 imid 字段创建一个索引,以加快对图像ID的查询速度。self.con.execute('create index imidhist_idx on imhistograms(imid)') #为 imhistograms 表中的 imid 字段创建一个索引,以加快对图像ID的查询速度。
7.3.2 添加图像
因为需要在索引中添加图像,所以还需要在类中添加以下几个方法
def add_to_index(self,imname,descr):"""将图像及其特征描述符添加到数据库中"""if self.is_indexed(imname): return # 如果图像已经被索引(即已经存在于数据库中),则退出方法,避免重复索引。print('indexing', imname)imid = self.get_id(imname)imwords = self.voc.project(descr) #使用词汇对象 self.voc 的 project 方法,将特征描述符 descr 转换为视觉单词的直方图 imwordsnbr_words = imwords.shape[0] #获取视觉单词直方图中视觉单词的数量 nbr_wordsfor i in range(nbr_words):word = imwords[i]# wordid is the word number itselfself.con.execute("insert into imwords(imid,wordid,vocname) values (?,?,?)", (imid,word,self.voc.name))# 将图像 ID (imid)、视觉单词直方图 imwords(使用 pickle.dumps 将 NumPy 数组编码为字符串)和词汇名称 (self.voc.name) 插入到 imhistograms 表中,记录图像的视觉单词直方图self.con.execute("insert into imhistograms(imid,histogram,vocname) values (?,?,?)", (imid,pickle.dumps(imwords),self.voc.name))
def is_indexed(self,imname):"""检查图像是否已被索引"""#执行 SQL 查询,检查 imlist 表中是否存在与 imname 匹配的 filename#fetchone() 方法返回查询结果的第一行。如果存在匹配的记录,则返回该记录;否则,返回 Noneim = self.con.execute("select rowid from imlist where filename='%s'" % imname).fetchone()return im != None
def get_id(self,imname):"""获取图像的唯一 ID。如果图像不在数据库中,则将其添加进去并返回新 ID"""#执行 SQL 查询,从 imlist 表中获取与 imname 匹配的图像 ID(rowid)cur = self.con.execute("select rowid from imlist where filename='%s'" % imname)res=cur.fetchone()if res==None:cur = self.con.execute("insert into imlist(filename) values ('%s')" % imname)return cur.lastrowidelse:return res[0]
接着我们遍历整个数据库中的样本图像,并将其加入我们的索引
nbr_images=len(imlist)
with open('vocabulary.pkl','rb') as f:voc=pickle.load(f)indx=Indexer('test.db',voc)
indx.create_tables()for i in range(nbr_images)[:1000]:locs,descr=read_features_from_file(featlist[i])indx.add_to_index(imlist[i],descr)indx.db_commit()
con=sqlite.connect('test.db')
print(con.execute('select count (filename) from imlist').fetchone())
print(con.execute('select * from imlist').fetchone())
7.4 在数据库中搜索图像
为实现搜索,我们创建一个Searcher类
class Searcher(object):def __init__(self,db,voc):""" Initialize with the name of the database. """self.con = sqlite.connect(db)self.voc = vocdef __del__(self):self.con.close()
7.4.1 利用索引获取获选图像
需要利用建立起来的索引找到包含特定单词的所有图像,因此添加candidates_from_word函数到Searcher类中
def candidates_from_word(self,imword):""" Get list of images containing imword. """im_ids = self.con.execute("select distinct imid from imwords where wordid=%d" % imword).fetchall()return [i[0] for i in im_ids]
需要在合并了的列表中对每一个图像id出现的次数进行跟踪
def candidates_from_histogram(self,imwords):""" Get list of images with similar words. """# get the word idswords = imwords.nonzero()[0]# find candidatescandidates = []for word in words:c = self.candidates_from_word(word)candidates+=c# take all unique words and reverse sort on occurrence tmp = [(w,candidates.count(w)) for w in set(candidates)]# tmp.sort(cmp=lambda x,y:cmp(x[1],y[1]))tmp.sort(key=lambda x: x[1], reverse=True)tmp.reverse()# return sorted list, best matches first return [w[0] for w in tmp]
src=Searcher('test.db',voc)
locs,descr=read_features_from_file(featlist[0])
iw=voc.project(descr)print('ask using a histogram...')
print(src.candidates_from_histogram(iw)[:10])
7.4.2 用一幅图像进行查询
添加get_imhistogram函数到Searcher类中
def get_imhistogram(self,imname):""" Return the word histogram for an image. """im_id = self.con.execute("select rowid from imlist where filename='%s'" % imname).fetchone()s = self.con.execute("select histogram from imhistograms where rowid='%d'" % im_id).fetchone()# use pickle to decode NumPy arrays from string# return pickle.loads(str(s[0]))
# print(type(s[0]))return pickle.loads(s[0])
添加query函数到Searcher类中
def query(self,imname):""" Find a list of matching images for imname. """h = self.get_imhistogram(imname)candidates = self.candidates_from_histogram(h)matchscores = []for imid in candidates:# get the namecand_name = self.con.execute("select filename from imlist where rowid=%d" % imid).fetchone()cand_h = self.get_imhistogram(cand_name)cand_dist = sqrt( sum( self.voc.idf*(h-cand_h)**2 ) )matchscores.append( (cand_dist,imid) )# return a sorted list of distances and database idsmatchscores.sort()return matchscores
src=Searcher('test.db',voc)
print('try a query...')
print(src.query(imlist[0])[:10])
7.4.3 确定对比基准并绘制结果
def compute_ukbench_score(src,imlist):""" Returns the average number of correctimages on the top four results of queries. """nbr_images = len(imlist)pos = zeros((nbr_images,4))# get first four results for each imagefor i in range(nbr_images):pos[i] = [w[1]-1 for w in src.query(imlist[i])[:4]]# compute score and return averagescore = array([ (pos[i]//4)==(i//4) for i in range(nbr_images)])*1.0return sum(score) / (nbr_images)
compute_ukbench_score(src,imlist)
定义plot_results函数
def plot_results(src,res):""" Show images in result list 'res'. """figure()nbr_results = len(res)for i in range(nbr_results):imname = src.get_filename(res[i])subplot(1,nbr_results,i+1)imshow(array(Image.open(imname)))axis('off')show()
src=Searcher('test.db',voc)
nbr_results=6
res=[w[1] for w in src.query(imlist[0])[:nbr_results]]
plot_results(src,res)
7.5 使用几何特性对结果排序
#载入图像列表
imlist=get_imlist(r'.\first1000')
nbr_images = len(imlist)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]nbr_images=len(imlist)with open('vocabulary.pkl','rb') as f:voc=pickle.load(f)src=Searcher('test.db',voc)q_ind=50
nbr_results=20res_reg=[w[1] for w in src.query(imlist[q_ind])[:nbr_results]]
print('top matches (regular):',res_reg)q_locs,q_descr=read_features_from_file(featlist[q_ind])
fp=make_homog(q_locs[:,:2].T)model=RansacModel()rank={}
for ndx in res_reg[1:]:locs,descr=read_features_from_file(featlist[ndx])matches=match(q_descr,descr)ind=matches.nonzero()[0]ind2=matches[ind]tp=make_homog(locs[:,:2].T)try:H,inliers=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)plot_results(src,res_reg[:8])
plot_results(src,res_geom[:8])
这篇关于【python计算机视觉编程——7.图像搜索】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!