コンピュータの視覚的な学習(VIII):BOWの画像検索

原則

我々は、文書画像とみなすことができる画像に適用されるバッグ・オブ・ワードモデル、「視覚的語彙」、同複数のすなわちセットに、各視覚的語彙との間には秩序が存在しません。

言葉で画像がテキスト文書の準備ができていないようので、私たちは、最初の、典型的には、3つのステップが必要で、画像の独立した視覚的な語彙から抽出する必要があります:

  1. 特徴検出
  2. 特徴表現
  3. この世代のことば
画像から独立して抽出された視覚的な語彙

観察することで、同じクラスの目標の異なるインスタンス間の違いはあるもののことがわかりますが、別の人は、比較的大きな違いに直面しているが、我々はまだ、そのような顔、それらの間にいくつかの共通の場所を見つけることができますが、目、口、鼻、いくつかの比較的小さな部品ではなく、大きな違いを観察し、我々は、ターゲットのこのタイプを識別するための視覚的な語彙として抽出し、これらの異なるインスタンス間の部分を一緒に置くことができます。

SIFTアルゴリズムは、画像内の局所的な不変の特徴を抽出するために最も広く使用されるアルゴリズムであるので、我々は、視覚的語彙の画像から抽出されたSIFTアルゴリズム不変特徴点を使用して、単語リストを構築することができ、テーブル内のワード単位でaは画像。

次に、我々は画像によるモデルのワードバッグ-方法を実証する、画像をベクトル値として表されます。3つのターゲットのカテゴリー、すなわち顔、自転車やギターがあります。

最初のステップ:以下に示すように、全て一緒に、各カテゴリの視覚画像から視覚的語彙の単語を抽出するアルゴリズムを使用してSIFT:

各画像クラスから視覚的語彙を抽出します

ステップ2:K平均アルゴリズムを使用するには、単語リストを構築します。K平均アルゴリズムは、間接的クラスタリング方法に基づいて、サンプル間の類似性の尺度であり、Kはクラスタ、クラスタ間内の類似度が高いとするように、アルゴリズムのパラメータは、Nは、K個のクラスタにオブジェクトであります低い類似。K平均アルゴリズムを使用してベクターから異なる距離に抽出し、視覚的語彙SIFTは、テーブル内の単語の基本的な語彙として単語同様の意味を組み合わせることができるとの間に、我々は、Kは、その後、4〜図以下の単語リスト構築プロセスを設定されていると仮定する。下記:
 

k平均アルゴリズム構成された単語リストを使用して

ステップ3:語彙リストを使用するには、画像を表現します。SIFTアルゴリズムを用いて、特徴点の数が各画像から抽出することができる、特徴点は、単語の類似語リストに置き換えることができ、語彙統計における回数各単語が画像に現れ、画像を表現することができますK = 4次元ベクトルの値。

每幅图像的直方图表示

上图中,我们从人脸、自行车和吉他三个目标类图像中提取出的不同视觉词汇,而构造的词汇表中,会把词义相近的视觉词汇合并为同一类,经过合并,词汇表中只包含了四个视觉单词,分别按索引值标记为1,2,3,4。通过观察可以看到,它们分别属于自行车、人脸、吉他、人脸类。统计这些词汇在不同目标类中出现的次数可以得到每幅图像的直方图表示(我们假定存在误差,实际情况亦不外如此):

    人脸:  [3,30,3,20]
    自行车:[20,3,3,2]
    吉他:  [8,12,32,7]

其实这个过程非常简单,就是针对人脸、自行车和吉他这三个文档,抽取出相似的部分(或者词义相近的视觉词汇合并为同一类),构造一个词典,词典中包含4个视觉单词,即Dictionary = {1:”自行车”, 2. “人脸”, 3. “吉他”, 4. “人脸类”},最终人脸、自行车和吉他这三个文档皆可以用一个4维向量表示,最后根据三个文档相应部分出现的次数画成了上面对应的直方图。

需要说明的是,以上过程只是针对三个目标类非常简单的一个示例,实际应用中,为了达到较好的效果,单词表中的词汇数量K往往非常庞大,并且目标类数目越多,对应的K值也越大,一般情况下,K的取值在几百到上千,在这里取K=4仅仅是为了方便说明。

 

下面,我们再来总结一下如何利用Bag-of-words模型将一幅图像表示成为数值向量:

第一步:利用SIFT算法从不同类别的图像中提取视觉词汇向量,这些向量代表的是图像中局部不变的特征点;

第二步:将所有特征点向量集合到一块,利用K-Means算法合并词义相近的视觉词汇,构造一个包含K个词汇的单词表;

第三步:统计单词表中每个单词在图像中出现的次数,从而将图像表示成为一个K维数值向量。
具体的,假设有5类图像,每一类中有10幅图像,这样首先对每一幅图像划分成patch(可以是刚性分割也可以是像SIFT基于关键点检测的),这样,每一个图像就由很多个patch表示,每一个patch用一个特征向量来表示,咱就假设用Sift表示的,一幅图像可能会有成百上千个patch,每一个patch特征向量的维数128。

 

接下来就要进行构建Bag of words模型了,假设Dictionary词典的Size为100,即有100个词。那么咱们可以用K-means算法对所有的patch进行聚类,k=100,我们知道,等k-means收敛时,我们也得到了每一个cluster最后的质心,那么这100个质心(维数128)就是词典里德100个词了,词典构建完毕。

词典构建完了怎么用呢?是这样的,先初始化一个100个bin的初始值为0的直方图h。每一幅图像不是有很多patch么?我们就再次计算这些patch和和每一个质心的距离,看看每一个patch离哪一个质心最近,那么直方图h中相对应的bin就加1,然后计算完这幅图像所有的patches之后,就得到了一个bin=100的直方图,然后进行归一化,用这个100维德向量来表示这幅图像。对所有图像计算完成之后,就可以进行分类聚类训练预测之类的了。

图像的特征用到了Dense Sift,通过Bag of Words词袋模型进行描述,当然一般来说是用训练集的来构建词典,因为我们还没有测试集呢。虽然测试集是你拿来测试的,但是实际应用中谁知道测试的图片是啥,所以构建BoW词典我这里也只用训练集。

用BoW描述完图像之后,指的是将训练集以及测试集的图像都用BoW模型描述了,就可以用SVM训练分类模型进行分类了。

上述BOW原理参考文章:https://blog.csdn.net/tiandijun/article/details/51143765

代码

1、利用现有的数据集来生成sift特征和图像搜索时所需要的模型文件

  为创建视觉单词词汇,首先需要提取特征描述子。这里,我们使用sift特征描述子,得到每副图像提取出的描述子后,将他们保存在一个文件中。并且创建一个词汇类以及在训练图像数据集上训练出一个词汇的方法。

# -*- 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('first100/')
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('first100/vocabulary.pkl', 'wb') as f:
    pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)
生成的模型文件和词汇

2、将模型数据导入数据库

  将图片资料存放到数据库中,使用了python中的pysqlite库,关于这个库可以到网站https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl中下载和自己系统版本对应的版本,下载后在下载的文件夹中pip install即可

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

# load vocabulary
#载入词汇
with open('first100/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]:
    locs,descr = sift.read_features_from_file(featlist[i])
    indx.add_to_index(imlist[i],descr)
# commit to database
#提交到数据库
indx.db_commit()

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

3、将数据放进数据库中之后就可以开始测试图片索引

  利用一些考虑到特征几何关系的准则重排搜索到的靠前结果,可以提高准确率。最常用的方法是在查询图像与靠前图像的特征位置间拟合单应性。

  为了提高效率,可以将特征位置存储在数据库中,并由特征的单词id决定他们之间的关联。然而,这需要大幅重写我们上面的数据库和代码,并复杂化表达形式。为了进行说明,我们仅重载靠前图像的特征,并对它们进行匹配。

  下面是一个载入所有模型文件并用单应性对靠前的图像进行重排的例子

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

#载入词汇
with open('first100/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]) #重排后的结果

首先,载入图像列表、特征列表(分别包含图像文件名和sift特征文件)以及 词汇。然后创建一个searcher对象,执行定期查询,并将结果保存在res_reg列表中。然后载入res_reg列表中每一幅图像的特征,并和查询图像进行匹配。单应性通过计算匹配数和计数内点数得到。最终,我们可以通过减少内点的数目对包含图像索引和内点数的字典进行排序。打印搜索结果列表到控制台,并可视化检索靠前的图像。

运行完后输出结果以及会两张Figure如下

4、建立演示程序和web应用

  需要先安装CherryPy包

  首先我们需要用一些html标签进行初始化,并用pickle载入数据。另外,还需要有与数据库进行交互的searcher对象词汇。

# -*- 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 = 'first100/'
        #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('first100/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'))

运行此代码还需要一个web服务器的配置文件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 = "D:\\python3.7\\ch07"
tools.staticdir.on = True
tools.staticdir.dir = ""

第一部分为IP地址和端口,第二部分为图库的地址

我的图库是在 D:\python3.7\ch07/first100/ 地址下,然后我在数据库中保存 的路径是 first100/xxx.jpg 所以我只要将图库的地址设置成 D:\python3.7\ch07 就行。
最后我们运行的时候会将我们设置的图库的地址(也就是 D:\python3.7\ch07) 和我们保存在数据库中的地址(first100/xxx.jpg)连接起来,用于显示图片。
在浏览器中打开上面的IP地址加端口:http://127.0.0.1:8080

就会显示随机挑选出来的图像的初始页面。

然后点击一幅图像进行查询,会显示出搜索出来的前几幅图像,在搜索出来的图像中单击某图像可以开始新的查询。此外,页面上有一个链接可以返回原来随机选择的状态。比如点击上图的第一张图片之后出来如下结果

 

 

 

 

 

おすすめ

転載: blog.csdn.net/weixin_43955429/article/details/89967267