程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python計算機視覺--基於(BOW)的圖像檢索與識別

編輯:Python

目錄

前言

一、基本原理

1.1 圖像分類簡介

1.2 Bag-of-words模型

1.3 Bag-of-features模型

1.4  Bag-of-features算法

1.5  Bag-of-features過程

1.6 TF-IDF

二、代碼實現

2.1 數據集

2.2 創建詞匯

2.3 建立數據庫

2.4 在數據庫中搜素圖像

2.5 遇到的問題

參考文章


前言

本次實驗將參照Bag-of-words模型實現簡單的圖像檢索操作。

環境:Pycharm,python3.8.5


一、基本原理

1.1 圖像分類簡介

圖像分類,即通過圖像內容的不同將圖像劃分為不同的類別,基於內容的圖像分類技術不需要對圖像的語義信息進行人工標注,而是通過計算機提取圖像中所包含的特征,並對特征進行處理和分析,得出分類結果。
 
常用的圖像特征有 色彩紋理灰度等信息。而圖像分類過程中,提取的特征要求不容易受隨機因素干擾,特征的有效提取可提高圖像分類的精度。特征提取完成後,選擇合適的算法創建圖像類型與視覺特征之間的關聯度,對圖像進行類別劃分。
 
圖像分類領域中,根據圖像分類要求,一般可以分為 場景分類目標分類兩類問題。
場景分類指的是從多幅圖像中區分出具有相似場景特征的圖像。
目標分類指的是對圖像中 出現的目標 (物體)進行識別或分類。

1.2 Bag-of-words模型

Bow起始可以理解為一種直方圖統計,開始是用於自然語言處理和信息檢索中的一種簡單的文檔表示方法。BoW也只是統計頻率信息,並沒有序列信息。Bow是選擇words字典,然後統計字典中每個單詞出現的次數。

BoW(Bag of Words)詞袋模型最初被用在文本分類中,將文檔表示成特征矢量。它的基本思想是假定對於一個文本,忽略其詞序和語法、句法,僅僅將其看做是一些詞匯的集合,而文本中的每個詞匯都是獨立的。簡單說就是將每篇文檔都看成一個袋子(因為裡面裝的都是詞匯,所以稱為詞袋,Bag of words即因此而來),然後看這個袋子裡裝的都是些什麼詞匯,將其分類。如果一篇文檔中豬、馬、牛、羊、山谷、土地、拖拉機這樣的詞匯多些,而銀行、大廈、汽車、公園這樣的詞匯少些,我們就傾向於判斷它是一篇描繪鄉村的文檔,而不是描述城鎮的。

1.3 Bag-of-features模型

Bag of Feature 也是借鑒了這種思路,只不過在圖像中,我們抽出的不再是一個個word,而是圖像的關鍵特征Feature,所以研究人員將它更名為Bag of Feature。
Bag of Feature在檢索中的算法流程和分類幾乎完全一樣,唯一的區別在於,對於原始的 BOF 特征,也就是直方圖向量,我們引入TF-IDF 權值。

1.4  Bag-of-features算法

Bag of Feature的本質是提出一種圖像的特征表示方法

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

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

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

1.5  Bag-of-features過程

算法流程:

  1. 提取圖像特征
  2. 對特征進行聚類,得到一部視覺字典( visual vocabulary )
  3. 針對輸入特征集,根據視覺詞典進行量化
  4. 把輸入圖像根據TF-IDF轉化成視覺單詞的頻率直方圖
  5. 構造特征到圖像的倒排表,通過倒排表快速索引相關圖像
  6. 根據索引結果進行直方圖匹配

(1)提取圖像特征

特征提取及描述主要是將一些 具有代表性區分性較強全局或局部特征從圖像中進行抽取,並對這些特征進行描述。
這些特征一般是類別之間差距比較明顯的特征,可以將其與其他類別區分開,其次,這些特征還要求具有 較好的穩定性,能夠最大限度的在光照視角尺度噪聲以及各種外在因素變化的情況下保持穩定,不受其影響。這樣即使在非常復雜的情況下,計算機也能通過這些穩定的特征很好的檢測與識別出這個物體。

特征提取最簡單且有效的方法就是 規則網格方法
該方法采用均勻網格對圖像進行劃分,從而得到圖像的局部區域特征。

興趣點檢測方法是另一個有效的特征提取方法,興趣點檢測的基本思想是:
在人為判斷一幅圖像的類別時,首先捕捉到物體的整體輪廓特征,然後聚焦於物體與其他物體具有顯著特征區別的地方,最後判斷出圖像的類別。即通過該物體與其他物體 區別開的 顯著特征,進而判斷圖像的類別。

在提取完圖像的特征後,下一步就要應用特征描述子來對抽取的圖像特征進行描述,特征描述子所表示的特征向量一般在處理算法時會作為輸入數據,因此,如果描述子具有一定的判別性及可區分性,則該描述子會在後期的圖像處理過程中起著很大的作用。

SIFT描述子是近年比較經典且被廣泛應用的一種描述子。
SIFT會從圖片上提取出很多特征點,每個特征點都是128維的向量,因此,如果圖片足夠多的話,我們會提取出一個巨大的特征向量庫。

(2) 學習視覺詞典(visual vocabulary)

提取完特征後,我們會采用一些聚類算法對這些特征向量進行聚類。

最常用的聚類算法是 :k-means

K-means算法是度量樣本間相似性的一種方法,該算法設置參數為K,把N個對象分成K個簇,簇內之間的相似度較高,而簇間的相似度較低。

至於 K-means 中的 K如何取,要根據具體情況來確定。另外,由於特征的數量可能非常龐大,這個聚類的過程也會非常漫長。聚類完成後得到K個聚類中心,每個聚類中心稱為“視覺單詞”,而將所有視覺單詞組成的集合稱為視覺詞典/碼本(codebook)。構建視覺單詞的過程如圖所示:

關於碼本的大小:

(1)如果碼本規模太小,我們的視覺詞典不能包括所有可能的情況;

(2)如果碼本過大,會使得計算量增加,且有過擬合現象出現。

 (3)圖片直方圖表示

利用視覺詞典中的詞匯表示待分類圖像。計算每幅圖像中的SIFT特征到這K個視覺單詞的距離,
其中 距離最近的視覺單詞為該SIFT特征對應的視覺單詞
通過統計每個單詞在圖像中出現的次數,將圖像表示成一個K維數值向量
如圖所示,其中K=4,每幅圖像用直方圖進行描述:

(4)量化

這一步驟通過對圖像特征提取,然後將提取出來的特征點,根據第三步,轉換為頻率直方圖。

這裡在轉換為頻率直方圖時候,有使用到TF-IDF,即詞頻(Term Frequency,TF)與逆文檔頻率(Inverse Document Frequency,IDF)乘積作為權值。引入這個權值的目的是為了降低一些重復特征所帶來的影響。比如在BOW中,一些常用詞匯譬如the,it,do等等詞匯,不能體現文本內容特征,但是出現頻率卻很高,利用tf-idf可以降低這種不必要詞匯的影響。同理,在BOF圖像搜索中,圖像之間也會有這樣的無意義的特征出現,所以需要降低這類特征的權值。

(5)構造倒排表

倒排表是一種逆向的查找方式,在BOW中大體的思路是通過已經提取出來的詞匯,反向查找出現過這個詞匯的文章。如圖,查找多個詞匯,就形成了一個倒排表。

BOF中倒排表也是同理。通過對視覺詞匯的反向查找,就會得到擁有同一視覺詞匯的圖像集合,反復多次就能得到一張倒排表。倒排表可以快速的得到新的圖像與數據庫裡相似的圖像。

(6)匹配直方圖

當我們做完上面的步驟,就需要對直方圖進行匹配。直方圖的匹配給出輸入圖像的頻率直方圖,在數據庫中查找K個最近鄰的圖像,根據這K個近鄰來投票圖像的分類結果。

1.6 TF-IDF

TF-IDF(Term frequency-Inverse document frequency)是一種統計方法,用來評估特征詞的重要程度。根據TF-IDF公式,特征詞的權重與在 語料庫中出現的頻率有關,也與在文檔裡出現的頻率有關。傳統的TF-IDF公式如下:

TF-IDF用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨著它在文件中出現的次數成正比增加,但同時會隨著它在語料庫中出現的頻率成反比下降。就目前來說,如果一個 關鍵詞只在很少的網頁中出現,我們通過它就 容易鎖定搜索目標,它的 權重也就應該。反之如果一個詞在大量網頁中出現,我們看到它仍然 不是很清楚要找什麼內容,因此它的 權重 應該

TF-IDF公式詳細介紹:CSDN編程社區 (smartapps.cn)

二、代碼實現

2.1 數據集

通過爬蟲在百度爬取三類圖片各60張,並利用批處理工具將所有數據圖像裁剪成一致大小,統一裁剪為640*480

A場景:60張汽車圖片

 B場景:60張貓的圖像

 C場景:60張小狗的圖像

 2.2 創建詞匯

createSift.py

# -*- coding: utf-8 -*-
"""
@author: RRJ
@software: PyCharm
@file: createSift.py
@time: 2022/6/12 22:44
"""
import pickle
from newPCV.imagesearch import vocabulary
from newPCV.tools.imtools import get_imlist
from newPCV.Localdescriptors import sift
# 獲取圖像列表
imlist = get_imlist('D:\\python\\RRJ\\pycharmproject\\Bag_of_words\\Imgtrain\\BOW_train\\')
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('training')
voc.train(featlist, 180, 10)
# 保存詞匯
# saving vocabulary
with open('D:\\python\\RRJ\\pycharmproject\\Bag_of_words\\BOW\\vocabulary.pkl', 'wb') as f:
pickle.dump(voc, f)
print('vocabulary is:', voc.name, voc.nbr_words)

訓練函數:train

def train(self,featurefiles,k=100,subsampling=10):
""" 用含有k個單詞的 K-means 列出在 featurefiles 中的特征文件訓練出一個詞匯。對訓練數據下采樣可以加快訓練速度 """
nbr_images = len(featurefiles)
# 從文件中讀取特征
descr = []
descr.append(sift.read_features_from_file(featurefiles[0])[1])
# 將所有的特征並在一起,以便後面進行 K-means 聚類
descriptors = descr[0]
for i in arange(1,nbr_images):
descr.append(sift.read_features_from_file(featurefiles[i])[1])
descriptors = vstack((descriptors,descr[i]))
#K-means: 最後一個參數決定運行次數
self.voc,distortion = kmeans(descriptors[::subsampling,:],k,1)
self.nbr_words = self.voc.shape[0]
# 遍歷所有的訓練圖像,並投影到詞匯上
imwords = zeros((nbr_images,self.nbr_words))
for i in range( nbr_images ):
imwords[i] = self.project(descr[i])
nbr_occurences = sum( (imwords > 0)*1 ,axis=0)
self.idf = log( (1.0*nbr_images) / (1.0*nbr_occurences+1) )
self.trainingdata = featurefiles
def project(self,descriptors):
""" 將描述子投影到詞匯上,以創建單詞直方圖 """
# 圖像單詞直方圖
imhist = zeros((self.nbr_words))
words,distance = vq(descriptors,self.voc)
for w in words:
imhist[w] += 1
return imhist

 部分結果:

 同時生成了數據模型vocabulary.pkl,如果數據模型為空,在後面存入數據庫會出現報錯,讀入數據為空。判斷.pkl是否為空可根據查看它的大小,如下圖所示,這裡pkl為196KB,故不為空。

 2.3 建立數據庫

將上面得到的數據模型存放數據庫testImaAdd.db中,即運行下面代碼會生成一個testImaAdd.db數據庫文件。

createDatabase.py

# -*- coding: utf-8 -*-
"""
@author: RRJ
@software: PyCharm
@file: createDatabase.py
@time: 2022/6/12 23:20
"""
import pickle
from newPCV.imagesearch import imagesearch
from newPCV.Localdescriptors import sift
import sqlite3
from newPCV.tools.imtools import get_imlist
# 獲取圖像列表
# imlist = get_imlist('E:/Python37_course/test7/first1000/')
imlist = get_imlist('D:\\python\\RRJ\\pycharmproject\\Bag_of_words\\Imgtrain\\BOW_train\\')
nbr_images = len(imlist)
# 獲取特征列表
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]
# load vocabulary
# 載入詞匯
with open('../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
# 遍歷所有的圖像,並將它們的特征投影到詞匯上(比如我的是180張圖片)
for i in range(nbr_images)[:179]:
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())

運行結果:

2.4 在數據庫中搜素圖像

利用索引獲取候選圖像 + 用一幅圖像進行查詢 + 確定對比基准並繪制結果
建立好圖像的索引,就可以在數據庫中搜索相似的圖像了。這裡,使用BOW(詞袋模型)來表示整個圖像,這是通用的,可以應用於尋找相似的物體、相似的臉、相似的顏色等,它完全取決於圖像及所用的描述子。為了實現搜索,在Imagesearch.py中有Searcher類:

Searcher 類:
class Searcher(object):
def __init__(self,db,voc):
""" Initialize with the name of the database. """
self.con = sqlite3.connect(db)
self.voc = voc
def __del__(self):
self.con.close()
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(s[0])
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]
def candidates_from_histogram(self,imwords):
""" Get list of images with similar words. """
# get the word ids
words = imwords.nonzero()[0]
# find candidates
candidates = []
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(key=cmp_to_key(lambda x,y:operator.gt(x[1],y[1])))
tmp.reverse()
# return sorted list, best matches first
return [w[0] for w in tmp]
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 name
cand_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 ids
matchscores.sort()
return matchscores
def get_filename(self,imid):
""" Return the filename for an image id. """
s = self.con.execute(
"select filename from imlist where rowid='%d'" % imid).fetchone()
return s[0]
def tf_idf_dist(voc,v1,v2):
v1 /= sum(v1)
v2 /= sum(v2)
return sqrt( sum( voc.idf*(v1-v2)**2 ) )
def compute_ukbench_score(src,imlist):
""" Returns the average number of correct
images on the top four results of queries. """
nbr_images = len(imlist)
pos = zeros((nbr_images,4))
# get first four results for each image
for i in range(nbr_images):
pos[i] = [w[1]-1 for w in src.query(imlist[i])[:4]]
# compute score and return average
score = array([ (pos[i]//4)==(i//4) for i in range(nbr_images)])*1.0
return sum(score) / (nbr_images)

使用幾何特性對結果排序
這是一種是常用BOW模型改進檢索結果的常用方法。BOW模型的一個主要 缺點是在用視覺單詞表示圖像時 不包含圖像特征的位置信息,這是為了 獲取速度和可伸縮性而付出的 代價。最常用的方法是在查詢圖像與靠前圖像的特征位置間擬合單應性。

# -*- coding: utf-8 -*-
"""
@author: RRJ
@software: PyCharm
@file: searchImg.py
@time: 2022/6/13 0:43
"""
import pickle
from newPCV.Localdescriptors import sift
from newPCV.imagesearch import imagesearch
from newPCV.geometry import homography
from newPCV.tools.imtools import get_imlist
# load image list and vocabulary
# 載入圖像列表
imlist = get_imlist('D:\\python\\RRJ\\pycharmproject\\Bag_of_words\\Imgtrain\\BOW_train\\') # 存放數據集的路徑
nbr_images = len(imlist)
# 載入特征列表
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]
# 載入詞匯
with open('../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 = 18
nbr_results = 5
# 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
# 獲取匹配數 # 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)
# 將字典排序,以首先獲取最內層的內點數
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]) # 重排後的結果

查詢索引為18的圖像,運行結果:

查詢圖像在最左邊,後面都是按圖像列表檢索的前5幅圖像。
對輸出的結果,首先是載入圖像列表特征列表及詞匯。然後創建一個Searcher對象執行定期查詢,並將結果保存在res_reg列表中,然後載入res_reg列表中每一幅圖像特征,並和查詢的圖像進行匹配。通過計算匹配數和計數內點數得到,最終可通過減少內點數目對包含圖像索引和內點數的字典進行排序。最後可視化檢索靠前的匹配圖像結果。

查詢索引為100的圖像,運行結果:

 可見在此處搜素出現了錯誤圖像,而且明顯發現兩類圖像差別明顯,猜測錯誤原因是數據集太小或者K太大的緣故。

2.5 遇到的問題

(1)ModuleNotFoundError: No module named 'pysqlite2'

解決方法:網上查閱,python3已經不支持pysqlite2這個庫了,找自己 imagesearch.py 文件的所在的路徑,把紅線區域的修改為如圖示,並且保證自己的python已經成功安裝了 pysqlite3 包。

(2)TypeError: 'cmp' is an invalid keyword argument for sort()

 解決方法:找到目標文件按下圖修改

 (3)TypeError: a bytes-like object is required, not 'str'

 解決方法:


參考文章

(1)計算機視覺——圖像檢索與識別_Nikki_du的博客-CSDN博客_圖像識別和圖像檢索

(2)python計算機視覺-圖像檢索和識別_我超愛Debug的博客-CSDN博客_python視覺識別


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved