1. 程式人生 > >(四)OpenCV中的特徵檢測之SIFT(尺度不變換特徵)

(四)OpenCV中的特徵檢測之SIFT(尺度不變換特徵)

註釋:本文翻譯自OpenCV3.0.0 document->OpenCV-Python Tutorials,包括對原文件種錯誤程式碼的糾正

1.目標

  • 學習SIFT演算法的概念
  • 學習如何找到SIFT關鍵點和描述符

2.理論

在最後幾章,我們看到了一些角落探測器,如Harris Corner等。它們是旋轉不變的,這意味著,即使影象旋轉,我們也可以找到相同的角落。這是顯而易見的,因為角落在旋轉中也是角落。但是縮放呢?如果影象縮放,角落可能不是角落。例如,請看下面的簡單圖片。放大同一個視窗時,小窗內的小影象的一個角落是平坦的。所以Harris Corner的角落是規模不變的。


因此,2004年,不列顛哥倫比亞大學的D.Lower在他的論文中提出了一種新的演算法——尺度不變特徵變換(SIFT),該演算法從尺度不變關鍵點獲取影象的特徵,提取這個關鍵點並計算其描述符。(本文很容易理解,並被認為是SIFT上最好的資料,所以這個解釋只是本文的一個簡短摘要)。

SIFT演算法主要涉及四個步驟,我們將逐一看到它們:

   2.1 尺度空間極值檢測(Scale-space Extrema Detection)

        從上圖可以看出,我們不能使用相同的視窗來檢測不同比例的關鍵點。小角落也行,但是要檢測更大的角落,我們需要更大的視窗。為此,使用縮放空間濾波。其中高斯拉普拉斯是為具有各種值的影象找到的。LoG作為一個斑點檢測器,可以檢測由於變化而產生的各種大小的斑點。簡而言之,作為一個縮放參數,例如,在上面的影象中,低的高斯核為小角賦值較高,而高斯的高斯核適合較大的角。因此,我們可以找到跨尺度和空間的區域性最大值,它給出了值的列表,這意味著尺度下(x,y)處存在潛在的關鍵點。

       但這個LoG花費代價有點大,所以SIFT演算法使用高斯差分,這是LoG的近似值。隨著這兩個不同影象的高斯模糊差異,可以得到高斯差分,讓它為。這個過程是在高斯金字塔的不同八度的影象中完成的。它在下面的影象中表示:


一旦找到這個DoG,影象就會在比例和空間上搜索到區域性極值。例如,影象中的一個畫素與其8個鄰居以及下一個尺度的9個畫素和先前尺度的9個畫素進行比較。如果它是一個區域性極值,這是一個潛在的關鍵點,它基本上意味著關鍵點最好在這個範圍內表現出來,它顯示在下面的影象中:


針對不同的引數,本文給出了一些經驗資料,可以總結為八度數(number of octaves)=4,比例級數(number of scale levels)=5,初始

等作為最優值。

   2.2 關鍵化本地點(Keypoint Localization)

         一旦找到潛在的關鍵點位置,就必須對其進行改進以獲得更準確的結果。他們使用泰勒級數展開的尺度空間來獲得更精準的極值位置,並且如果這個極值的強度小於閾值(根據紙張為0.03),它將被拒絕。該閾值在OpenCV中被稱為contrastThreshold。

         DoG對邊緣有更高的響應,所以邊緣也需要去除。為此,使用類似Harris角點檢測器的概念。他們用2*2的Hessian矩陣(H)來計算失真曲線,我們從Harris角點檢測器知道,對於邊緣,一個特徵值比另一個大。所以這裡他們使用了一個簡單的函式。

          如果此比率比閾值(在OpenCV中稱為edgeThreshold)高,則丟棄該關鍵點。在紙上給出10,因此它消除了任何低對比度關鍵點和邊緣關鍵點,剩下的就是強烈的興趣點。

   2.3 方向分配(Orientation Assignment)

        現在為每個關鍵點分配一個方向以實現影象的旋轉不變性。在關鍵點位置周圍取決於尺度,並在該區域計算梯度幅度和方向。建立一個方向直方圖,其中36個方框覆蓋360度,(用等於關鍵點尺度的1.5倍的梯度幅度和高斯加權視窗進行加權,得到直方圖中的最高峰值,並且在任何高於80%的峰值也被認為是計算方向。)建立具有相同位置和規模但方向不同的關鍵點,有助於匹配的穩定性。

   2.4 關鍵點描述符(keypoint Descriptor)

         現在建立關鍵點描述符。關鍵點周圍有一個16*16的街區(Block)。它分為16個4*4大小的字塊。對於每個字塊,建立8個方向直方圖。所以共有128bin值可用。它被表示為形成關鍵點描述符的向量。除此之外,它還採取了幾項措施來實現對光照變化、旋轉等的魯棒性。

   2.5 關鍵點匹配(Keypoint Matching)

         兩幅影象之間的關鍵點通過識別它們最近的鄰居來匹配。但在某些情況下,第二個最接近的匹配可能非常接近第一個。這可能是由於噪音或其它的原因。在那種情況下,採用最近距離與第二距離的比率,如果它大於0.8,則被拒絕。根據文章,它被排除了大約90%的錯誤匹配,而丟棄的只有5%的匹配正確。

         所以這是SIFT演算法的總結。欲瞭解更多細節和理解,強烈建議閱讀原文。記住一件事,這個演算法是獲專利的。所以這個演算法被包含在OpenCV的非自由模組中。

3.OpenCV中的SIFT

現在讓我們來看看OpenCV中提供的SIFT函式。讓我們從關鍵點檢測並開始繪製它們,首先,我們必須構建一個SIFT物件。我們可以將不同的引數傳遞給它,這些引數是可選的,並且在文件中有很好的解釋。

# -*- coding: utf-8 -*-
'''
SIFT(尺度不變特徵變換)介紹
SIFT演算法主要涉及四個步驟:
1.尺度空間極值檢測(scale-space Extrema Detection)
2.關鍵點本地化
3.方向分配
4.關鍵點描述符
5.關鍵點匹配
下面的demo是OpenCV中的SIFT函式:cv2.SIFT()
'''

import cv2

img = cv2.imread('5.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

'''
在用python使用OpenCV進行SIFT時候,編譯出現這樣的問題:
AttributeError: module 'cv2' has no attribute 'SIFT'
錯誤程式碼如下:
sift = cv2.SIFT()
原因:opencv將SIFT等演算法整合到xfeatures2d集合裡面了。變更後的寫法如下:
sift=cv2.xfeatures2d.SIFT_create()
'''
# sift = cv2.SIFT()
sift = cv2.xfeatures2d.SIFT_create()

# sift.detect()函式是在影象中查詢關鍵點。如果只想收縮影象的一部分,可以傳遞掩碼
# 每個關鍵點都是一個特殊的結構,具有許多屬性。如(x,y)座標,有意義的領域大小,指定其方向的角度,指定關鍵點強度的響應等等
kp = sift.detect(gray, None)

# drawKeypoints:第一個引數輸入影象,第二個引數關鍵點,第三個引數輸出影象
# cv2.drawKeypoints()用於在關鍵點位置繪製小圓圈。如果傳遞一個標誌cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS給它,
# 會繪製一個尺寸為關鍵點的圓,甚至會顯示它的方向。
# img = cv2.drawKeypoints(gray, kp, img)
img = cv2.drawKeypoints(gray, kp, img, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('sift_keypoints.jpg', img)

'''
現在計算描述符,OpenCV提供了兩種方法:
1.既然已經找到了關鍵點,可以使用sift.compute()計算關鍵點描述符,例如:kp,des=sift.compute(gray,kp)
2.如果沒有找到關鍵點,可使用函式sift.detectAndCompute()在一個步驟中直接找到關鍵點和描述符:如下:
'''
# 這裡找出來的kp將是一個關鍵點列表,des是形狀Number_of_Keypoints*128的一個numpy陣列
kp, des = sift.detectAndCompute(gray, None)

cv2.imshow('sift_keypoints', img)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

OpenCV還提供cv2.drawkeyPoints()函式,使用者繪製關鍵點位置上的小圓圈。如果你傳遞一個cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS給它,它會繪製一個尺寸為關鍵點的圓,它甚至會顯示它的方向。