基於特徵點檢測和匹配的圖像識別

特徵點
一個圖像的特徵點由兩部分組成:關鍵點(keypoint)和描述子(Descriptor)。關鍵點指的是該特徵點在圖像中的位置,有些還具有方向、尺度等信息。描述子通常是一個向量,按照人爲的設計方式,描述關鍵點周圍像素的信息。通常描述子是按照外觀相似的特徵應該有相似的描述子設計的。因此,在匹配的時候,只要兩個特徵點的描述子在向量空間的距離相近,就可以認爲它們是同一個特徵點。
特徵點的匹配通常需要三步:
(1)提取圖像中的關鍵點–查找圖像中具有某些特徵的像素
(2)根據得到的關鍵點位置,計算特徵點的描述子
(3)根據特徵點的描述子,進行匹配
特徵檢測
在圖片中找到感興趣區域的過程就叫做特徵檢測。opencv中提供了多個特徵檢測算法:

  • Harris corner detection:角點是在所有方向像素變化劇烈的點,Harris和Stephens提出了檢測這樣區域的快速方法。opencv:cv2:cornerHarris
  • Shi-Tomasi corner detection:通常比Harris更優,它們查找N個最強的角點。opencv:cv2:goodFeaturesToTrack
  • Scale-Invariant Feature Transform(SIFT):在圖像大小改變時角點檢測的效果就不好了,Lowe提出了一個描述圖像裏與角度大小無關的關鍵點的方法。在opencv3中,SIFT在contrib模塊裏。opencv3:cv2:xfeatures2d.SIFT_create()
  • Speed-Up Robust Features(SURF):SURF將SIFT中的Laplacian of a Gaussisan(LOG)用一個方框濾波代替(box filter)代替。opencv3:cv2:xfeatures2d.SURF_create()

一般而言,一個物體的角點最能股代表物體的特徵,所以特徵檢測又叫角點(Corner) 檢測。
harris是最早的特徵提取算法,opencv實現如下:

dst = cv2.conrnerHarris(gray,blockSize=2,ksize=5,k=0.04)
# blockSize - 檢測的臨點數
# ksize -sobel邊緣檢測的核
# k -目標函數的一個參數(一般取值較小)

harris corner算法能夠解決旋轉不變性問題,但不能解決尺度變化問題,由此提出了SIFT算法

SIFT

SIFT(ScaleInvariant Feature Transform,尺度不變特徵變換)是一種尺度不變特徵檢測法。SIFT特徵對旋轉、尺度旋轉、亮度變化等保持不變性,是一種非常穩定的局部特徵。
SIFT算法主要有以下幾個步驟:

  • 高斯差分金字塔的構建
    使用組和層的結構構建一個具有線性關係的金字塔(尺度空間),這樣可以在連續的高斯核尺度上查找圖像的特徵點。另外,它使用一階的高斯差分來近似高斯的拉普拉斯核,大大的減少了運算量。
  • 尺度空間的極值檢測及特徵點的定位
    搜索上一步建立的高斯尺度空間,通過高斯差分來識別潛在的對尺度和旋轉不變的特徵點。但是,在離散空間中,局部極值點可能並不是真正意義的極值點,真正的極值點有可能落在離散點的間隙中,SIFT通過尺度空間DoG函數進行曲線擬合尋找極值點。
  • 特徵方向賦值
    基於圖像局部的梯度方向,分配給每個關鍵點位置一個或多個方向,後續的所有操作都是對於關鍵點的方向、尺度和位置進行變換,從而提供這些特徵的不變性。
  • 特徵描述子的生成
    通過上面的步驟已經找到的SIFT特徵點的位置、方向、尺度信息,最後使用一組向量來描述特徵點及其周圍鄰域
    opencv實現:
sift = cv2.xfeatures2d.SIFT_create()
kp,des = sfit.detectAndCompute(gray, None) #計算描述子
#kp指關鍵點, des表示關鍵點的特徵描述

SIFT算法詳解

SURF

SURF(speed up robust features,加速穩健特徵),是一種穩健的局部特徵點檢測和描述算法。
SURF執行效率上的兩大法寶:一個是積分圖在Hessian上的使用,一個是降維的特徵描述子的使用。
SURF是對SIFT算法的改進,改進了特徵的提取和描述方式,用一種更爲高效地方式完成特徵的提取和描述,具體實現流程如下:

  1. 構建Hessian矩陣,生成所有的興趣點,用於特徵的提取
  2. 構建尺度空間
  3. 特徵點定位
  4. 特徵點主方向分配
  5. 生成特徵點描述子
  6. 特徵點匹配
    SURF算法詳解
    SURF算法
    opencv實現:
surf = cv2.xfeatures2d.SURF_create('閾值')
#閾值越大能檢測的特徵就越少
kp, des = surf.detectAndCompute(gray,None)

ORB

orb的全稱是Oriented Fast and Rotated Brief,是brief算法的改進版,orb算法結合了fast算法與brief算法的優點,是目前最快的目標檢測算法。

  • ORB算法是brief算法的改進,brief的優點在於速度,其缺點是:不具備旋轉不變性、對噪聲敏感、不具備尺度不變性
  • 使用非最大值抑制,在一定區域內僅僅保留響應極大值的角點,避免FAST提取到的角點過於集中。
  • FAST提取到的角點數量過多且不是很穩定,ORB中可以指定需要提取到的角點的數量N,然後對FAST提取到的角點分別計算Harris響應值,選擇前N個具有最大響應值的角點作爲最終提取到的特徵點集合。
  • FAST提取到的角點不具有尺度信息,在ORB中使用圖像金字塔,並且在每一層金字塔上檢測角點,以此來保持尺度的不變性。
  • FAST提取到的角點不具有方向信息,在ORB中使用灰度質心法(Intensity Centroid)來保持特徵的旋轉不變性。
  • fast算法是特徵檢測的再提速(能夠滿足實時檢測關鍵點的要求),brief是特徵描述子計算的簡化與提速,sift與surf算法每個關鍵的特徵描述子128維都是單精度,所以每個描述子佔128*4=512bytes的內存,breif每個特徵描述子32bytes,大大減少內存,且運算快(利於嵌入式開發)
    ORB特徵提取與匹配
    orb的opencv實現:
orb = cv2.ORB_create()
kp,des = orb.detectAndCompute(img, None)

特徵匹配

opencv提供了兩種matching方式:

  • Brute-force matcher(cv::BFMatcher)暴力匹配
  • Flann-based matcher (cv::FlannBasedMatcher)快速最近鄰搜索算法

BFmatch(暴力匹配)
計算匹配圖層的一個特徵描述子與待匹配圖層的所有特徵描述子的距離返回最近距離。
Brute-force matcher是用暴力方法找到點集1中每個descriptor在點集2中距離最近的descriptor,找到的距離最小就認爲匹配。在第一幅圖像中選取一個關鍵點然後依次與第二幅圖像的每個關鍵點進行(描述符)距離測試,最後返回距離最近的關鍵點。
一般把點集一稱爲 train set (訓練集),對應模板圖像。點集二稱爲 query set(查詢集),對應查找模板圖的目標圖像。浮點描述子–歐氏距離,二進制描述符–漢明距離

opencv實現:

import cv2 
img1 = cv2.imread(' ')
img2 = cv2.imread(' ')
orb = cv.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2 ,des2 = orb.detectAndCompute(img2, None)

#針對ORB算法,NORM_HAMMING計算特徵距離,TRUE判斷交叉驗證
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck = True)
#特徵描述子匹配
matches = bf.match(des1, des2)

matches = sorted(matches, key=lambda x:x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=2)

FlannBasedMatcher
Fast Library for Approximate Nearest Neighbors–最近鄰搜索,它是一個對大數據集和高維特徵進行最近鄰搜索的算法的集和,並且這些算法已經被優化過了,在面對大數據集時它的效果要好於BFMatcher,是目前最快的特徵匹配算法(最近鄰搜索)

使用FLANN匹配,需要傳入兩個字典作爲參數:
第一個是indexParams, 配置要使用的算法:隨機kd樹、優先搜索k-means樹算法、層次聚類樹;

對於SIFT和SURF,可以傳入的參數index_params=dict(algorithm=FLANN_INDEX_KDTREE, tress=5); 
對於ORB, 可以傳入參數index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12, multi_probe_level=1)

第二個是SearchParams,可以傳入參數search_params=dict(checks=100), 它用來指定遞歸遍歷的次數,值越高結果越準確,但是消耗的時間也越多。

opencv實現:

import cv2
img1 = cv2.imread(' ')
img2 = cv2.imread(' ')

sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2 ,des2 = sift.detectAndCompute(img2, None)

#FLANN parameters
FLANN_INDEX_KDTEE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)

flann = cv2.FlannBasedMatcher(index_params, search_params)

matches = flann.knnMatch(des1, des2, k=2)

#Need to draw only goos matches, so create a mask
matchesMask = [[0,0] for i in range(len(matches))]

#ratio test
for i, (m,n) in enumerate(matches):
	if m.distance < 0.7*n.distance:
		matchesMask[i] = [1,0]
draw_params = dict(matchColor = (0,255,0), singlePointColor=(255,0,0),
								matchesMask=matchesMask, flags=0)

img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None, **draw_params)

Flann-based matcher使用快速近似最近鄰搜索算法尋找(用快速的第三方庫近似最近鄰搜索算法)

爲了提高檢測速度,你可以調用matching函數前,先訓練一個matcher。訓練過程可以首先使用cv::FlannBasedMatcher來優化,爲descriptor建立索引樹,這種操作將在匹配大量數據時發揮巨大作用(比如在上百幅圖像的數據集中查找匹配圖像)。而Brute-force matcher在這個過程並不進行操作,它只是將train descriptors保存在內存中。

文章參考之:
https://blog.csdn.net/z704630835/article/details/85005525
https://blog.csdn.net/weixin_37565420/article/details/79090644
https://www.jianshu.com/p/1f6195352b26?nomobile=yes

好文收藏:
https://blog.csdn.net/zhuisui_woxin/article/details/84400439

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章