OpenCV特徵提取與圖像檢索實現
翻譯 | AI科技大本營(ID:rgznai100)
參與 | 張蔚敏
審校|reason_W
「拍立淘」「一鍵識花」「街景匹配」……不知道大家在使用這些神奇的功能的時候,有沒有好奇過它們背後的技術原理?其實這些技術都離不開最基本的圖像檢索技術。本篇文章我們就將對這一技術的原理進行介紹,並通過一個簡單的Python腳本來實現一個最基本的圖像檢索demo。
▌圖像特徵
首先我們需要明白圖像特徵是什麼以及它的使用方法。
圖像特徵是一種簡單的圖像模式,基於這種模式我們可以描述我們在圖像上所看到的內容。 例如,在一張跟貓有關的圖片中,貓咪的眼睛就可以作為這幅圖像的特徵。特徵在(包括但不限於)計算機視覺中的主要作用是將視覺信息轉換為向量空間表示。這種向量空間表示讓我們可以利用數學運算對其進行處理,例如通過計算尋找相似向量(這可以用來尋找相似圖像或圖像中的相似目標)。
▌如何從圖像中獲取特徵?
從圖像中獲取特徵的方法有兩種,第一種是通過提取圖像描述符實現(白盒演算法);第二種通過基於神經網路的方法實現(黑盒演算法)。本文主要介紹第一種方法。
特徵提取的演算法有很多,最常用的有:SURF、ORB、SIFT、BRIEF等。這些演算法大多是基於圖像梯度的。為了簡化安裝需求,本教程使用的是KAZE描述符,因為其他描述符在python的基礎OpenCV庫中沒有提供。
下面是特徵提取器的實現代碼:
importcv2
importnumpyasnp
importscipy
fromscipy.miscimportimread
importcPickleaspickle
importrandom
importos
importmatplotlib.pyplotasplt
# Feature extractor
# 特徵提取器
defextract_features(image_path, vector_size=32):
image = imread(image_path, mode="RGB")
try:
# Using KAZE, cause SIFT, ORB and other was moved to additional module
# which is adding addtional pain during install
#此處為了簡化安裝步驟,使用KAZE,因為SIFT/ORB以及其他特徵運算元需要安
#裝額外的模塊
alg = cv2.KAZE_create()
# Finding image keypoints
#尋找圖像關鍵點
kps = alg.detect(image)
# Getting first 32 of them.
#計算前32個
# Number of keypoints is varies depend on image size and color pallet
#關鍵點的數量取決於圖像大小以及彩色調色板
# Sorting them based on keypoint response value(bigger is better)
#根據關鍵點的返回值進行排序(越大越好)
kps = sorted(kps, key=lambdax: -x.response)[:vector_size]
# computing descriptors vector
#計算描述符向量
kps, dsc = alg.compute(image, kps)
# Flatten all of them in one big vector - our feature vector
# 將其放在一個大的向量中,作為我們的特徵向量
dsc = dsc.flatten()
# Making descriptor of same size
# 使描述符的大小一致
# Descriptor vector size is 64
#描述符向量的大小為64
needed_size = (vector_size *64)
ifdsc.size
# if we have less the 32 descriptors then just adding zeros
# at the end of our feature vector
#如果少於32個描述符,則在特徵向量後面補零
dsc = np.concatenate([dsc, np.zeros(needed_size - dsc.size)])
exceptcv2.errorase:
print"Error: ", e
returnNone
returndsc
defbatch_extractor(images_path, pickled_db_path="features.pck"):
files = [os.path.join(images_path, p)forpinsorted(os.listdir(images_path))]
result = {}
forfinfiles:
print"Extracting features from image %s"% f
name = f.split("/")[-1].lower()
result[name] = extract_features(f)
# saving all our feature vectors in pickled file
# 將特徵向量存於pickled 文件
withopen(pickled_db_path,"w")asfp:
pickle.dump(result, fp)
OpenCV中的大多數特徵提取演算法的python介面都相同,所以如果你想要使用SIFT特徵,只需要用SIFT_create替換KAZE_create就行。
首先,程序會用extract_features檢測圖像上的關鍵點(局部模式的中心點)。 因為關鍵點數量隨圖像的不同有所不同,因此我們需要添加一些規則,以確保所得到的特徵向量大小始終相同(這是因為在計算時,我們無法對維度不同的向量進行比較,所以必須保證相同的大小)。
然後是根據關鍵點構建向量描述符,每個描述符的大小為64,我們有32個這樣的描述符,所以我們的特徵向量是2048維。
batch_extractor是在所有的圖像中批量運行特徵提取器,並將特徵向量保存在pickled文件中以供後續使用。
現在我們來建立類Matcher,它會將待搜索圖像和資料庫中的圖像進行匹配。
classMatcher(object):
def__init__(self, pickled_db_path="features.pck"):
with open(pickled_db_path) asfp:
self.data = pickle.load(fp)
self.names = []
self.matrix = []
fork, vinself.data.iteritems():
self.names.append(k)
self.matrix.append(v)
self.matrix = np.array(self.matrix)
self.names = np.array(self.names)
defcos_cdist(self, vector):
# getting cosine distance between search image and images database
#計算待搜索圖像與資料庫圖像的餘弦距離
v = vector.reshape(1, -1)
returnscipy.spatial.distance.cdist(self.matrix, v,"cosine").reshape(-1)
defmatch(self, image_path, topn=5):
features = extract_features(image_path)
img_distances =self.cos_cdist(features)
# getting top 5 records
# 獲得前5個記錄
nearest_ids = np.argsort(img_distances)[:topn].tolist()
nearest_img_paths =self.names[nearest_ids].tolist()
returnnearest_img_paths, img_distances[nearest_ids].tolist()
這裡要載入前一步得到的特徵向量,並從它們中創建一個大矩陣,然後計算待搜索圖像的特徵向量和特徵向量資料庫之間的餘弦距離,然後輸出最近的前N個結果。
當然,這僅僅是一個demo,在實際計算中,還可以用一些演算法來快速計算數百萬圖像間的餘弦距離。你可以使用簡單且運行速度相當快的Annoy Index(在1M圖像中搜索約需2ms)。
現在把它們放在一起運行一下:
defshow_img(path):
img = imread(path, mode="RGB")
plt.imshow(img)
plt.show()
defrun():
images_path ="resources/images/"
files = [os.path.join(images_path, p)forpinsorted(os.listdir(images_path))]
# getting 3 random images
# 隨機獲取3張圖
sample = random.sample(files,3)
batch_extractor(images_path)
ma = Matcher("features.pck")
forsinsample:
print"Query image =========================================="
show_img(s)
names, match = ma.match(s, topn=3)
print"Result images ========================================"
foriinrange(3):
# we got cosine distance, less cosine distance between vectors
# more they similar, thus we subtruct it from 1 to get match value
#我們得到了餘弦距離,向量之間的餘弦距離越小表示它們越相似,因此我們從1中減去它以得到匹配值
print"Match %s"% (1-match[i])
show_img(os.path.join(images_path, names[i]))
run()
大家可以在我的 github上下載源碼,或者在Google Colab上運行(Google Colab是一種提供GPU在線計算的免費服務):
https://colab.research.google.com/drive/1BwdSConGugBlGzPLLkXHTz2ahkdzEhQ9
▌總結
在運行上述代碼的過程中,你可能會發現搜索到的相似圖像並不總能達到我們想像中的那種相似程度。這是因為我們所用的這種演算法是上下文無關(context-unaware)的,所以該演算法在尋找相同(即使是被修改過的)圖像方面表現更好,而不是在相似圖像方面。如果是要尋找上下文相關的相似圖像,那就要使用卷積神經網路了,我的下一篇文章會對這方面的知識進行詳細介紹。
作者:Andrey Nikishaev


※Yann LeCun卸任!Facebook變天,做AI不能落地是不成了
※語音入口大戰升級,Echo音箱還不是殺手鐧,等所有汽車都用上Alexa就不一樣了
TAG:AI科技大本營 |