計算機視覺 — 圖像檢索

一、Bag of Feature

  1. 簡介

    Bag of Features模型仿照文本檢索領域的Bag of Words方法,把每幅圖像描寫敘述爲一個局部區域/關鍵點(Patches/Key Points)特徵的無序集合。使用某種聚類算法(如K-means)將局部特徵進行聚類。每一個聚類中心被看作是詞典中的一個視覺詞彙(Visual Word)。相當於文本檢索中的詞。視覺詞彙由聚類中心相應特徵形成的碼字(code word)來表示(可看當爲一種特徵量化過程)。全部視覺詞彙形成一個視覺詞典(Visual Vocabulary),相應一個碼書(code book),即碼字的集合,詞典中所含詞的個數反映了詞典的大小。圖像中的每一個特徵都將被映射到視覺詞典的某個詞上,這樣的映射能夠通過計算特徵間的距離去實現,然後統計每一個視覺詞的出現與否或次數。圖像可描寫敘述爲一個維數相同的直方圖向量,即Bag of Features。

  2. 算法流程

    (1) 構造不小於 100張圖片的數據集

    (2) 針對數據集,做SIFT特 徵提取

    (3) 根據SIFT特徵提取結果,採用k-means算法學習“視覺詞典.(visual vocabulary)”,其中維度至少滿足4個量級(比如10, 50, 100, 1000, 5000 )

    (4) 根據IDF原理,計算每個視覺單詞的權

    (5) 針對數據庫中每張圖片的特徵集,根據視覺詞典進行量化以及TF-IDF解算。每張圖片轉化成特徵向量

    (6) 對於輸入的檢索圖像(非數據庫中圖片),計算SIFT特徵,.並根據TF-IDF轉化成頻率直方圖/特徵向量

    (7) 構造檢索圖像特徵到數據庫圖像的倒排表,快速索引相關候選匹配圖像集

    (8) 針對候選匹配圖像集與檢索圖像進行直方圖/特徵匹配

  3. 算法
    Bag of Feature的本質是提出一種圖像的特徵表示方法。

    按照Bag of Feature算法的思想,首先我們要找到圖像中的關鍵詞,而且這些關鍵詞必須具備較高的區分度。實際過程中,通常會採用SIFT特徵。

    有了特徵之後,我們會將這些特徵通過聚類算法得出很多聚類中心。這些聚類中心通常具有較高的代表性,比如,對於人臉來說,雖然不同人的眼睛、鼻子等特徵都不盡相同,但它們往往具有共性,而這些聚類中心就代表了這類共性。我們將這些聚類中心組合在一起,形成一部字典(CodeBook)。

    對於圖像中的每個SIFT特徵,我們能夠在字典中找到最相似的聚類中心,統計這些聚類中心出現的次數,可以得到一個向量表示(有些文章稱之爲直方圖),如本文開篇的圖片所示。這些向量就是所謂的Bag。這樣,對於不同類別的圖片,這個向量應該具有較大的區分度,基於此,我們可以訓練出一些分類模型(SVM等),並用其對圖片進行分類。

    提取圖像特徵:

    特徵必須具有較高的區分度,而且要滿足旋轉不變性以及尺寸不變性等,因此,我們通常都會採用SIFT特徵。SIFT算法是提取圖像中局部不變特徵的應用最廣泛的算法,因此我們可以用SIFT算法從圖像中提取不變特徵點,作爲視覺詞彙,並構造單詞表,用單詞表中的單詞表示一幅圖像。SIFT會從圖片上提取出很多特徵點,每個特徵點都是 128 維的向量,因此,如果圖片足夠多的話,我們會提取出一個巨大的特徵向量庫。

    訓練字典( visual vocabulary ):

    提取完特徵後,我們會採用一些聚類算法對這些特徵向量進行聚類。最常用的聚類算法是 k-means。至於 k-means 中的 k 如何取,要根據具體情況來確定。另外,由於特徵的數量可能非常龐大,這個聚類的過程也會非常漫長。聚類完成後,我們就得到了這 k 個向量組成的字典,這 k 個向量有一個通用的表達,叫 visual word。在這裏插入圖片描述
    圖片直方圖表示:

    上一步訓練得到的字典,是爲了這一步對圖像特徵進行量化。對於一幅圖像而言,我們可以提取出大量的SIFT特徵點,但這些特徵點仍然屬於一種淺層(low level)的表達,缺乏代表性。因此,這一步的目標,是根據字典重新提取圖像的高層特徵。

    具體做法是,對於圖像中的每一個SIFT特徵,都可以在字典中找到一個最相似的 visual word,這樣,我們可以統計一個 k 維的直方圖,代表該圖像的SIFT特徵在字典中的相似度頻率。


    例如:對於上圖這輛車的圖片,我們匹配圖片的「SIFT」向量與字典中的 visual word,統計出最相似的向量出現的次數,最後得到這幅圖片的直方圖向量。

    訓練分類器:

    當我們得到每幅圖片的直方圖向量後,剩下的這一步跟以往的步驟是一樣的。無非是根據數據庫圖片的向量以及圖片的標籤,訓練分類器模型。然後對需要預測的圖片,我們仍然按照上述方法,提取SIFT特徵,再根據字典量化直方圖向量,用分類器模型對直方圖向量進行分類。當然,也可以直接根據 KNN 算法對直方圖向量做相似性判斷。

二、實驗結果

我選取了100張左右的圖片作爲我的數據集,並對其進行sift特徵提取。如下圖所示。
在這裏插入圖片描述
提取文件夾下圖像的sift特徵,生成詞彙。
在這裏插入圖片描述
創建索引,遍歷所有的圖像,並將它們的特徵投影到詞彙上,提交到數據庫。
在這裏插入圖片描述
利用建立起來的索引找到包含特定單詞的所有圖像。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

三、小結

  1. 結果輸出的圖片匹配較大的5張圖片,regular的五張圖片爲普通查詢的結果,即利用歐氏距離,homography的五張圖片爲RANSEC模型排序查詢的結果,可以看到兩張方法查詢得出的圖片是一致的,但第二張方法輸出的圖片圖像特徵較爲相同的排在前面。

  2. 第一組只是進行了簡單的索引和查詢,而第二組用單應性進行擬合建立RANSAC模型,再使用候選圖像的特徵進行排序查詢,結果較爲準確。

  3. 圖像特徵比較明顯的圖像,匹配效果更爲理想。

  4. 對於字典的規模,字典過大,單詞缺乏一般性,對噪聲敏感,計算量大,關鍵是圖象投影后的維數高;字典太小。單詞區分性能差,對類似的目標特徵無法表示。

  5. 遇到的錯誤:TypeError: ‘cmp’ is an invalid keyword argument for sort()。

    python3中修改了cmp的參數,將imagesearch.py中的tmp.sort(cmp=lambda x,y:cmp(x[1],y[1]))替換tmp.sort(key=cmp_to_key(lambda x,y:operator.gt(x[1],y[1])))
    ,同時加上from functools import cmp_to_key。

四、代碼

import pickle
from PCV.imagesearch import vocabulary
from PCV.tools.imtools import get_imlist
from PCV.localdescriptors import sift

#獲取圖像列表
imlist = get_imlist('C:/Users/Desktop/pi1/')
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, 888, 10)
#保存詞彙
# saving vocabulary
with open('C:/Users/Desktop/BOW/vocabulary.pkl', 'wb') as f:
    pickle.dump(voc, f)
print ('vocabulary is:', voc.name, voc.nbr_words)

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('C:/Users/Desktop/pi1/')
nbr_images = len(imlist)
#獲取特徵列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]

# load vocabulary
#載入詞彙
with open('C:/Users/Desktop/BOW/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)[:888]:
    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())
import pickle
from PCV.imagesearch import imagesearch
from PCV.localdescriptors import sift
from PCV.tools.imtools import get_imlist


#獲取圖像列表
imlist = get_imlist('C:/Users/Desktop/pi1/')
nbr_images = len(imlist)
#獲取特徵列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]

#載入詞彙
f = open('C:/Users/Desktop/BOW/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)
# -*- 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('C:/Users/Desktop/pi1/')
nbr_images = len(imlist)
#載入特徵列表
featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]

#載入詞彙
with open('C:/Users/Desktop/BOW/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 = 23
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[:5]) #常規查詢
imagesearch.plot_results(src,res_geom[:5]) #重排後的結果
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章