基于python3计算机视觉编程(八)BOW图像检索

一. 图像检索简介

在检索原理上,无论是基于文本的图像检索还是基于内容的图像检索,主要包括三方面:一方面对用户需求的分析和转化,形成可以1检索索引数据库的提问;另一方面,收集和加工图像资源,提取特征,分析并进行标引,2建立图像的索引数据库;最后一方面是根据相似度算法,计算用户提问与索引数据库中记录的相似度大小,3提取出满足阈值的记录作为结果,按照相似度降序的方式输出。
为了进一步提高检索的准确性,许多系统结合相关反馈技术来收集用户对检索结果的反馈信息,这在CBIR中显得更为突出,因为CBIR实现的是逐步求精的图像检索过程,在同一次检索过程中需要不断地与用户进行交互。

二. 检索的分类

  • 文本检索

基于文本的图像检索沿用了传统文本检索技术,回避对图像可视化元素的分析,而是从图像名称、图像尺寸、压缩类型、作者、年代等方面标引图像,一般以关键词形式的提问查询图像,或者是根据等级目录的形式浏览查找特定类目下的图像

  • 内容检索CBIR

基于内容的图像检索根据图像、图像的内容语义以及上下文联系进行查找,以图像语义特征为线索从图像数据库中检出具有相似特性的其它图像。因为图像的规模一般要大于纯粹的文本信息,因此,基于内容的图像检索在检索的速度和效率上要求更高。

三. BOW图像检索

3.1 BOW原理简述

Bag Of Word模型,是现在一种用于图像检索的一种方法。它最早用于对于文章内容的检索,原理是将文本看作是单词的集合,不考虑其中的语法,上下文等等。通过建立词典,对每个单词出现次数进行统计,以便得到文本内容的分类。计算机视觉的专家从中获得灵感,将其用于图像的检索中,就有了Bag Of Features

3.2 算法实现步骤

  1. 特征提取
  2. 学习“视觉词典”visual vocabulary
  3. 针对输入特征集,根据视觉词典进行量化
  4. 把输入图像,根据TF-IDF转换成视觉单词的频率直方图
  5. 构造特征到图像的倒排表,通过倒排表快速索引相关图像
  6. 根据索引结果进行直方图匹配

(1) 特征提取

主要通过SIFT描述算子进行特征提取,从而标记每张图片,达到唯一标识。特征点的提取主要是针对图像的关键部分的提取,而采用SIFT算法的优点在于该算法对一些特殊点比较敏感,并且SIFT算法对图像旋转,尺度缩放,亮度变化具有不变性。

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

通过上一步,我们获得了图像的特征点,但不没有进行分类处理,这里采用K-means聚类算法,将图像多个特征点进行分类,这里需要注意的是,如果我们码本规模太小,我们的visual vocabulary 就不能包括所有可能的情况,相反的是规模过大,则会导致过拟合现象
在这里插入图片描述

(3) 视觉词典量化,把输入图像转化成频率直方图

在这里插入图片描述
对于输入特征,量化的过程是将该特征映射到距离其最近的视觉单词,并实现计数。对于输入图片利用K-means算法聚类图片特征得到的视觉单词,根据TF-IDF转化成视觉单词的频率直方图,用视觉单词直方图来表示图像,如上图所示。

(4) 构造特征到图像的倒排表

倒排表是一种逆向的查找方式,在BOW中是通过已经提取出来的词汇,反向查找出现过这个词汇的文章。BOF中倒排表也是同理。通过对视觉词汇的反向查找,就会得到拥有同一视觉词汇的图像集合,反复多次就能得到一张倒排表。倒排表可以快速的得到新的图像与数据库里相似的图像。

(5) 根据索引结果进行直方图匹配

当我们做完上面的步骤,就需要对直方图进行匹配。直方图的匹配给出输入图像的频率直方图,在数据库中查找K个最近邻的图像,根据这K个近邻来投票图像的分类结果。

四. 算法代码实现

4.1 数据集及环境配置

数据集:
该次实验数据集是博主自己家周围3种环境的图片采样,总共100张图片
在这里插入图片描述
并且分别设置k-means算法K值依次为10,50,100,500以此探究对算法精确度的影响程度。

环境配置:

  • vlfeat-0.9.20版本,vlfeat的win64里的vl.dll和sift.exe和vl.lib复制在项目里,同时注意把PCV包放在项目下。
    在这里插入图片描述
  • cmd中安装PyQt5 :pip install PyQt5
  • cmd中安装cherrypy :pip install cherrypy

4.2 代码实现

Cacubulary.py

# -*- coding: utf-8 -*-
import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
import sift

#获取图像列表
#imlist = get_imlist('E:/Python37_course/test7/first1000/')
imlist = get_imlist('images/')
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)
voc = vocabulary.Vocabulary('test77_test')
voc.train(featlist, 100, 10)
    
#保存词汇
# saving vocabulary
'''with open('E:/Python37_course/test7/first1000/vocabulary.pkl', 'wb') as f:
    pickle.dump(voc, f)'''
with open('images/vocabulary.pkl', 'wb') as f:
    pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)

该函数主要实现提取每一张图片的SIFT特征值,生成.sift文件,以及在根目录下视生成觉词汇vocbulary.pkl文件
在这里插入图片描述
我所用的是图像集为100张。如果需要增加图像或减少只需要改代码里读取训练图像的数量。如下图红框所示,后面10表示的是训练10次。
在这里插入图片描述

AddImage.py

# -*- 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:/Python37_course/test7/first1000/')
imlist = get_imlist('images/')
nbr_images = len(imlist)
#获取特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]

# load vocabulary
#载入词汇
'''with open('E:/Python37_course/test7/first1000/vocabulary.pkl', 'rb') as f:
    voc = pickle.load(f)'''
with open('images/vocabulary.pkl', 'rb') as f:
    voc = pickle.load(f)
#创建索引
indx = imagesearch.Indexer('testImaAdd.db',voc)
indx.create_tables()
# go through all images, project features on vocabulary and insert
#遍历所有的图像,并将它们的特征投影到词汇上

#for i in range(nbr_images)[:1000]:
for i in range(nbr_images)[:70]:
    locs,descr = sift.read_features_from_file(featlist[i])
    indx.add_to_index(imlist[i],descr)
# commit to database
#提交到数据库
indx.db_commit()

con = sqlite3.connect('testImaAdd.db')
print (con.execute('select count (filename) from imlist').fetchone())
print (con.execute('select * from imlist').fetchone())

将生成的视觉词汇vocbulary.pkl文件存入数据库,上诉代码运行成功会在项目根目录生成一个testImaAdd.db文件该文件的大小会随着你的特征维度的大小而上升。
在这里插入图片描述
Reranking.py


# -*- coding: utf-8 -*-
import pickle
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('images/')
nbr_images = len(imlist)
#载入特征列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]

#载入词汇
'''with open('E:/Python37_course/test7/first1000/vocabulary.pkl', 'rb') as f:
    voc = pickle.load(f)'''
with open('images/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 = 6

# 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]) #重排后的结果

数据库建立成功便可以输入图像进行测试了,即运行上诉代码。

4.3 测试数据集

修改K值可以在vocabulary函数里进行修改
在这里插入图片描述

在这里插入图片描述
数据集100张图片 K值 = 10
在这里插入图片描述

查询图像在最左边,后面都是按图像列表检索的前几幅图像。对输出的结果,首先是载入图像列表、特征列表及词汇。然后创建一个Searcher对象,执行定期查询,并将结果保存在res_reg列表中,然后载入res_reg列表中每一幅图像特征,并和查询的图像进行匹配。当隐形通过计算匹配数和计数内点数得到,最终可通过减少内点数目对包含图像索引和内点数的字典进行排序。最后可视化检索靠前的匹配图像结果。可以看出查询图片的准确度很低,应该是K值太小的缘故,导致图片训练出的视觉词典维度不够。

数据集100张图片 K值 = 50
在这里插入图片描述

可以明显看出图片检索准确率的上升,但是该场景的景物过于单一简单,于是我们输入另外一张图片。
在这里插入图片描述
发现图片匹配率过低,再次提高字典特征维度把。

数据集100张图片 K值 = 100
在这里插入图片描述

依旧不理想,继续提高K值。

数据集100张图片 K值 = 1000
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

左边为输入的图片,右边为输出的匹配图片,可以看出提高视觉词典的特征维度就是提高了图片匹配的成功率,这也不难想象,因为提高了维度即就是提升了算法的分类水平,维度越高,特征的分类就越精细,匹配的成功率也就越高,但上诉匹配中,扔存在匹配不准的图片,这应该是数据集过小,只有100张图片,如果想要更加的精确,就只能提高数据集以及K值,但这也会导致性能开销过大,增大了运行时间。因此在K值得选择上应不易过大,同样得过小得K值容易导致算法不精确,所以K值应该取折中值。

BOW (Bag of Words) 是一种常用的图像检索方法,其基本思想是将图像转化为向量,然后通过计算向量之间的距离来实现图像检索。下面是一个基于PythonBOW图像检索系统的设计思路和实现方法。 ## 设计思路 1. 预处理阶段:对于每张图片,提取其SURF特征,并使用K-means算法将特征向量聚类,得到视觉词汇表。 2. 训练阶段:对于每张图片,使用其SURF特征和视觉词汇表,构造其Bag of Words向量,并使用SVM分类器对图像进行分类。 3. 检索阶段:对于查询图片,提取其SURF特征并构造其Bag of Words向量,计算查询向量与训练集中所有图像的距离,并返回距离最近的前K张图片作为检索结果。 ## 实现方法 下面是一个基于PythonBOW图像检索系统的实现方法。 ### 1. 预处理阶段 ```python import cv2 import numpy as np from sklearn.cluster import KMeans # 定义SURF特征提取器 surf = cv2.xfeatures2d.SURF_create() # 定义K-means聚类器 kmeans = KMeans(n_clusters=50) # 加载训练集图片 train_images = [] for i in range(1, 11): img = cv2.imread(f'train/{i}.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) kp, des = surf.detectAndCompute(gray, None) train_images.append(des) # 将特征向量聚类,得到视觉词汇表 features = np.vstack(train_images) kmeans.fit(features) vocabulary = kmeans.cluster_centers_ ``` 以上代码中,我们使用OpenCV的SURF特征提取器提取训练集图片的SURF特征,并使用K-means算法将特征向量聚类,得到视觉词汇表。 ### 2. 训练阶段 ```python import os # 构造训练集的Bag of Words向量和标签 train_data = [] train_labels = [] for i in range(1, 11): img = cv2.imread(f'train/{i}.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) kp, des = surf.detectAndCompute(gray, None) words = kmeans.predict(des) bow_vector = np.zeros((1, 50)) for w in words: bow_vector[0, w] += 1 train_data.append(bow_vector) train_labels.append(i) # 使用SVM分类器对图像进行分类 svm = cv2.ml.SVM_create() svm.setType(cv2.ml.SVM_C_SVC) svm.setKernel(cv2.ml.SVM_LINEAR) svm.train(np.array(train_data), cv2.ml.ROW_SAMPLE, np.array(train_labels)) ``` 以上代码中,我们对每张训练集图片提取SURF特征并构造其Bag of Words向量,然后使用SVM分类器对图像进行分类。 ### 3. 检索阶段 ```python # 加载查询图片 query_img = cv2.imread('query.jpg') query_gray = cv2.cvtColor(query_img, cv2.COLOR_BGR2GRAY) query_kp, query_des = surf.detectAndCompute(query_gray, None) # 构造查询图片的Bag of Words向量 query_words = kmeans.predict(query_des) query_bow = np.zeros((1, 50)) for w in query_words: query_bow[0, w] += 1 # 计算查询向量与训练集中所有图像的距离 distances = [] for i in range(1, 11): img = cv2.imread(f'train/{i}.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) kp, des = surf.detectAndCompute(gray, None) words = kmeans.predict(des) bow_vector = np.zeros((1, 50)) for w in words: bow_vector[0, w] += 1 dist = np.linalg.norm(query_bow - bow_vector) distances.append((i, dist)) # 返回距离最近的前K张图片作为检索结果 k = 5 results = sorted(distances, key=lambda x: x[1])[:k] for r in results: print(f'train/{r[0]}.jpg') ``` 以上代码中,我们对查询图片提取SURF特征并构造其Bag of Words向量,然后计算查询向量与训练集中所有图像的距离,并返回距离最近的前K张图片作为检索结果。 ## 总结 BOW图像检索系统是一种基于特征向量的图像检索方法,在实际应用中具有广泛的应用。本文介绍了基于PythonBOW图像检索系统的设计思路和实现方法,希望能够对读者有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值