OpenCV特徵提取與影象檢索實現(附程式碼)
翻譯 | AI科技大本營(ID:rgznai100)
參與 | 張蔚敏
審校 | reason_W
“拍立淘”“一鍵識花”“街景匹配”……不知道大家在使用這些神奇的功能的時候,有沒有好奇過它們背後的技術原理?其實這些技術都離不開最基本的影象檢索技術。本篇文章我們就將對這一技術的原理進行介紹,並通過一個簡單的Python指令碼來實現一個最基本的影象檢索demo。
▌影象特徵
首先我們需要明白影象特徵是什麼以及它的使用方法。
影象特徵是一種簡單的影象模式,基於這種模式我們可以描述我們在影象上所看到的內容。 例如,在一張跟貓有關的圖片中,貓咪的眼睛就可以作為這幅影象的特徵。特徵在(包括但不限於)計算機視覺中的主要作用是將視覺資訊轉換為向量空間表示。這種向量空間表示讓我們可以利用數學運算對其進行處理,例如通過計算尋找相似向量(這可以用來尋找相似影象或影象中的相似目標)。
▌如何從影象中獲取特徵?
從影象中獲取特徵的方法有兩種,第一種是通過提取影象描述符實現(白盒演算法);第二種通過基於神經網路的方法實現(黑盒演算法)。本文主要介紹第一種方法。
特徵提取的演算法有很多,最常用的有:SURF、ORB、SIFT、BRIEF等。這些演算法大多是基於影象梯度的。為了簡化安裝需求,本教程使用的是KAZE描述符,因為其他描述符在python的基礎OpenCV庫中沒有提供。
下面是特徵提取器的實現程式碼:
import cv2
import numpy as np
import scipy
from scipy.misc import imread
import
import random
import os
import matplotlib.pyplot as plt
# Feature extractor
# 特徵提取器
def extract_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=lambda x: -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)
if dsc.size < needed_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)])
except cv2.error as e:
print 'Error: ', e
return None
return dsc
def batch_extractor(images_path, pickled_db_path="features.pck"):
files = [os.path.join(images_path, p) for p in sorted(os.listdir(images_path))]
result = {}
for f in files:
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 檔案
with open(pickled_db_path, 'w') as fp:
pickle.dump(result, fp)
OpenCV中的大多數特徵提取演算法的python介面都相同,所以如果你想要使用SIFT特徵,只需要用SIFT_create替換KAZE_create就行。
首先,程式會用extract_features檢測影象上的關鍵點(區域性模式的中心點)。 因為關鍵點數量隨影象的不同有所不同,因此我們需要新增一些規則,以確保所得到的特徵向量大小始終相同(這是因為在計算時,我們無法對維度不同的向量進行比較,所以必須保證相同的大小)。
然後是根據關鍵點構建向量描述符,每個描述符的大小為64,我們有32個這樣的描述符,所以我們的特徵向量是2048維。
batch_extractor是在所有的影象中批量執行特徵提取器,並將特徵向量儲存在pickled檔案中以供後續使用。
現在我們來建立類Matcher,它會將待搜尋影象和資料庫中的影象進行匹配。
class Matcher(object):
def __init__(self, pickled_db_path="features.pck"):
with open(pickled_db_path) as fp:
self.data = pickle.load(fp)
self.names = []
self.matrix = []
for k, v in self.data.iteritems():
self.names.append(k)
self.matrix.append(v)
self.matrix = np.array(self.matrix)
self.names = np.array(self.names)
def cos_cdist(self, vector):
# getting cosine distance between search image and images database
#計算待搜尋影象與資料庫影象的餘弦距離
v = vector.reshape(1, -1)
return scipy.spatial.distance.cdist(self.matrix, v, 'cosine').reshape(-1)
def match(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()
return nearest_img_paths, img_distances[nearest_ids].tolist()
這裡要載入前一步得到的特徵向量,並從它們中建立一個大矩陣,然後計算待搜尋影象的特徵向量和特徵向量資料庫之間的餘弦距離,然後輸出最近的前N個結果。
當然,這僅僅是一個demo,在實際計算中,還可以用一些演算法來快速計算數百萬影象間的餘弦距離。你可以使用簡單且執行速度相當快的Annoy Index(在1M影象中搜索約需2ms)。
現在把它們放在一起執行一下:
def show_img(path):
img = imread(path, mode="RGB")
plt.imshow(img)
plt.show()
def run():
images_path = 'resources/images/'
files = [os.path.join(images_path, p) for p in sorted(os.listdir(images_path))]
# getting 3 random images
# 隨機獲取3張圖
sample = random.sample(files, 3)
batch_extractor(images_path)
ma = Matcher('features.pck')
for s in sample:
print 'Query image =========================================='
show_img(s)
names, match = ma.match(s, topn=3)
print 'Result images ========================================'
for i in range(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
原文連結:https://towardsdatascience.com/feature-extraction-and-similar-image-search-with-opencv-for-newbies-3c59796bf774
相關推薦
OpenCV特徵提取與影象檢索實現(附程式碼)
翻譯 | AI科技大本營(ID:rgznai100) 參與 | 張蔚敏 審校 | reason_W “拍立淘”“一鍵識花”“街景匹配”……不知道大家在使用這些神奇的功能的時候,有沒有好奇過它們背後的技術原理?其實這些技術都離不開最基本的影象檢索技術。本篇文
安卓識別身份證,自動提取身份證資訊功能實現(附原始碼)
原始碼下載地址:注:原始碼裡沒有騰訊優圖的賬號需要填寫自己的 下載地址 先講幾下.首先我們需要去騰訊優圖申請一個賬號,因為身份證識別需要用到第三方介面如圖所示 我申請的是掃描身份證,當然還有其他的功能,比如掃描銀行卡,營業執照,車牌等等 ,大家可以去研究一下
keras面向小資料集的影象分類(VGG-16基礎上fine-tune)實現(附程式碼)
參考譯文地址:http://keras-cn.readthedocs.io/en/latest/blog/image_classification_using_very_little_data/ 本文作者:Francois Chollet 概述 在本文中,將使用VGG-16模型提供一種面向小資料集(幾百
淺談常見的七種加密演算法及實現(附程式碼)
1. 前言 數字簽名、資訊加密 是前後端開發都經常需要使用到的技術,應用場景包括了使用者登入、交易、資訊通訊、oauth 等等,不同的應用場景也會需要使用到不同的簽名加密演算法,或者需要搭配不一樣的 簽名加密演算法來達到業務目標。這裡簡單的給大家介紹幾種常見的簽
APIX_身份證影象識別技術(附程式碼)
什麼是身份證影象識別 身份證識別應用要求文字清晰的大陸二代身份證,文字朝向為正向,影象清晰。同時,身份證號碼要求滿足國標編碼規範,否則不通過驗證。API支援支援圖片Base64編碼和圖片URL兩種方法,支援的影象格式有jpg、jpeg、gif、png和bmp格式,檔案大小在
Android系統截圖的實現(附程式碼)
1.背景 寫部落格快兩年了,寫了100+的文章,最火的文章也是大家最關注的就是如何實現android系統截圖。其實我們google android_screen_shot就會找到很對
基於FFmpeg的YUV多影象拼接方法(附程式碼)
本文章針對的YUV資料為YUV420p,基於FFmpeg解碼後轉換Frame->data為YUV420p資料進行操作,若非此種格式請先將資料轉為此格式或查詢其他資料; 若想知其所以然請先自行搜尋YUV420p資料儲存格式,在這裡將不再贅述,推薦文章地址: http:
oauth1.0 java的實現(附程式碼)
最近再做畢業設計,需要用到OAuth 1.0協議。 首先,簡單說一下什麼是OAuth 協議。 OAuth 是一個開放授權協議,允許第三方應用訪問服務提供方中註冊的終端使用者的某些資源,且不會把帳號和密碼提供給第三方。 OAuth 允許通過服務提供商授予的一個臨時令牌而不是
在python中使用opencv將RGB影象轉換為HSV及YCrCb影象(附程式碼)
【時間】2018.11.01 【題目】在python中使用opencv將RGB影象轉換為HSV及YCrCb影象(附程式碼) 目錄 概述 一、程式碼實現 二、執行結果 三、關於HSV及YCrCb的一點補充 3.1HSV顏色空間 3.2 YCRCBA顏色空間
資料結構BFS與DFS的實現(鄰接矩陣)
#include<bits/stdc++.h> #define MaxInt 2e9 #define MVNum 100 #define OK 1 #define ERROR 0 using namespace std; typedef char VerTexType; typedef
模擬退火演算法與C語言實現(TSP問題)
1簡介: 模擬退火來自冶金學的專有名詞退火。退火是將材料加熱後再經特定速率冷卻,目的是增大晶粒的體積,並且減少晶格中的缺陷。材料中的原子原來會停留在使內能有區域性最小值的位置,加熱使能量變大,原子會離開原來位置,而隨機在其他位置中移動。退火冷卻時速度較慢,使得原子有較多可能
斐波那契數列的迭代實現與遞迴實現(c語言)
遞迴實現 #include<stdio.h> int Fib(int n){ // 自定義函式 if(n<0) return -1; else if(n==0) return 0; else if(n==1)
Spark 2.0 機器學習 ML 庫:特徵提取、轉化、選取(Scala 版)
一、前言 二、程式碼 1.TF-IDF(詞頻-逆向文件頻率) TF(詞頻Term Frequency):HashingTF不CountVectorizer用於生成詞頻TF向量。 HashingTF是一個特徵詞集的轉換器(Tr
P2P之UDP穿透NAT的原理與實現(附原始碼)
原文連結 關於UDP穿透NAT的中文資料在網路上是很少的,僅有<<P2P之UDP穿透NAT的原理與實現(shootingstars)>>這篇文章有實際的參考價值。 本人近兩年來也一直從事P2P方面的開發工作,比較有代表性的是個人開發的BitTorr
基於stm32的自定義HID裝置開發與上位機通訊實現(附原始碼)
現在主流的安卓手機資料連線線,Mini-usb、Micro-usb,Type-c,產品追隨主流,非聯網裝置,摒棄ST-LINK、JLINK,直接用usb資料傳輸升級。主要實現與HID裝置的通訊即人機互動。本文主要介紹了HID裝置的下位機通訊連線與上位機裝置識別。 下位機:
半邊資料結構與網格細分演算法Loop subdivision(附程式碼)
網格細分的原理其實並不難理解,它的難點主要在於如何實現。在看過無數有原理無程式碼的部落格後,終於決定寫一寫我的實現方法,並附上程式碼供大家參考。c++寫的可能比較笨拙,望見諒。 1.半邊資料結構 很好理解,就是把網格的每一條邊分成兩個半邊,半邊是有方向的同一條邊的兩個半邊方向相反。並且一條邊
如何在python中實現整數的二進位制迴圈移位(附程式碼)
【時間】2018.11.03 【題目】如何在python中實現整數的二進位制迴圈移位(附程式碼) 概述 在python中,可以通過<<以及>>運算子實現二進位制的左移位以及右移位,然而並沒有實現迴圈移位的運算子,暫時也找不到可以實現迴圈移位的函式,所以在本文中,主
一個模型搞定所有風格轉換,直接在瀏覽器實現(demo+程式碼)
用一個模型就能實現所有型別的風格轉換!一個名為Arbitrary Image Stylization in the Browser的專案最近火起來。 作者是日本小哥Reiichiro Nakano,他用TensorFlow.js在瀏覽器中構建了一個使用任意影象進行風格化的demo。 不像以前
CS231n-KNN實現(附坑)
從效果看來,KNN並不適合影象識別,它的識別更多基於背景,而不是圖片的語義主體。所以在實際應用中我們一般不適用KNN識別影象,但是在學習過程中,通過KNN演算法我們可以學習到影象識別的整個流程,還是有些許幫助的 影象識別流程 無論是哪種分類演算法,影象識別的流程主要為以下流程
二值影象的腐蝕膨脹原理(附程式碼)
程式碼: #include <iostream> #include<vector> #include<iomanip> using namespace std; #define picX 6 #define picY 6 type