部分 V
圖像特征提取與描述
29 理解圖像特征
目標
本節我會試着幫你理解什么是圖像特征,為什么圖像特征很重要,為什么角點很重要等。
29.1 解釋
我相信你們大多數人都玩過拼圖游戲吧。首先你們拿到一張圖片的一堆碎片,要做的就是把這些碎片以正確的方式排列起來從而重建這幅圖像。問題是,你怎樣做到的呢?如果把你做游戲的原理寫成計算機程序,那計算機就也會玩拼圖游戲了。如果計算機可以玩拼圖,我們就可以給計算機一大堆自然圖片,然后就可以讓計算機把它拼成一張大圖了。如果計算機可以自動拼接自然圖片,那我們是不是可以給計算機關於一個建築的的大量圖片,然后讓計算機給我們創建一個 3D 的的模型呢?
問題和聯想可以無邊無際。但是所有的這些問題都是建立在一個基礎問題之上的。這個問題就是:我們是如何玩拼圖的?我們是如何把一堆碎片拼在一起的?我們有時如何把一個個自然場景拼接成一個單獨圖像的?
答案就是:我們要尋找一些唯一的特征,這些特征要適於被跟蹤,容易被比較。如果我們要定義這樣一種特征,雖然我們知道它是什么但很難用語言來描述。如果讓你找出一個可以在不同圖片之間相互比較的好的特征,你肯定能搞定。這就是為什么小孩子也會玩拼圖的原因。我們在一副圖像中搜索這樣的特征,我們能找到它們,而且也能在其他圖像中找到這些特征,然后再把它們拼接到一塊。(在拼圖游戲中,我們更注重的是圖片之間的連續性)。我們的這些能力都是天生的。
所以我們的一個問題現在擴展成了幾個,但是更加確切了。這些特征是什么呢?(我們的答案必須也能被計算機理解)。
好吧,很難說人是怎樣找出這些特征的。這些能力已經刻在我們的大腦中了。但是如果我們深入的觀察一些圖像並搜索不同的 pattern,我們會發現一些有趣的事。一下圖為例:

圖像很簡單。在圖像的上方給出了六個小圖。你要做的就是找到這些小圖在原始圖像中的位置。你能找到多少正確結果呢?
A 和 B 是平面,而且它們的圖像中很多地方都存在。很難找到這些小圖的准確位置。
C 和 D 更簡單。它們是建築的邊緣。你可以找到它們的近似位置,但是准確位置還是很難找到。這是因為:沿着邊緣,所有的地方都一樣。所以邊緣是比平面更好的特征,但是還不夠好(在拼圖游戲中要找連續的邊緣)。
最后 E 和 F 是建築的一些角點。它們能很容易的被找到。因為在角點的地方,無論你向哪個方向移動小圖,結果都會有很大的不同。所以可以把它們當成一個好的特征。為了更好的理解這個概念我們舉個更簡單的例子。

如上圖所示,藍色框中的區域是一個平面很難被找到和跟蹤。無論你向那個方向移動藍色框,長的都一樣。對於黑色框中的區域,它是一個邊緣。如果你沿垂直方向移動,它會改變。但是如果沿水平方向移動就不會改變。而紅色框中的角點,無論你向那個方向移動,得到的結果都不同,這說明它是唯一的。
所以,基本上來說角點是一個好的圖像特征。(不僅僅是角點,有些情況斑點也是好的圖像特征)。
現在我們終於回答了前面的問題了,“這些特征是什么?”。但是下一個問題又來了。我們怎樣找到它們?或者說我們怎樣找到角點?我們也已經用一種直觀的方式做了回答,比如在圖像中找一些區域,無論你想那個方向移動這些區域變化都很大。在下一節中我們會用計算機語言來實現這個想法。所以找到圖像特征的技術被稱為 特征檢測。
現在我們找到了圖像特征(假設你已經搞定)。在找到這些之后,你應該在其他圖像中也找到同樣的特征。我們應該怎么做呢?我們選擇特征周圍的一個區域,然后用我們自己的語言來描述它,比如“上邊是藍天,下邊是建築,在建築上有很多玻璃等”,你就可以在其他圖片中搜索相同的區域了。基本上看來,你是在描述特征。同樣,計算機也要對特征周圍的區域進行描述,這樣它才能在其他圖像中找到相同的特征。我們把這種描述稱為 特征描述。當你有了特征很它們的描述后,你就可以在所有的圖像中找這個相同的特征了,找到之后你就可以做任何你想做的了。
本章我們就是要使用 OpenCV 中的各種算法來查找圖像的特征,然后描述它們,對它們進行匹配等。
更多資源
練習
30 Harris 角點檢測
目標
• 理解 Harris 角點檢測的概念
• 學習函數:cv2.cornerHarris(),cv2.cornerSubPix()
原理
在上一節我們已經知道了角點的一個特性:向任何方向移動變化都很大。Chris_Harris 和 Mike_Stephens 早在 1988 年的文章《A CombinedCorner and Edge Detector》中就已經提出了焦點檢測的方法,被稱為Harris 角點檢測。他把這個簡單的想法轉換成了數學形式。將窗口向各個方向移動(u,v)然后計算所有差異的總和。表達式如下:
![E(u,v) = \sum_{x,y} \underbrace{w(x,y)}_\text{window function} \, [\underbrace{I(x+u,y+v)}_\text{shifted intensity}-\underbrace{I(x,y)}_\text{intensity}]^2](/image/aHR0cDovL29wZW5jdi1weXRob24tdHV0cm9hbHMucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L19pbWFnZXMvbWF0aC8xOTQ4ZjI4NTBjOTEyY2UzMzk0YjYxYmE5OWI1NDZmMWJmN2E2YWRjLnBuZw==.png)
窗口函數可以是正常的矩形窗口也可以是對每一個像素給予不同權重的高斯窗口
角點檢測中要使 E (µ,ν) 的值最大。這就是說必須使方程右側的第二項的取值最大。對上面的等式進行泰勒級數展開然后再通過幾步數學換算(可以參考其他標准教材),我們得到下面的等式:

其中

這里 I x 和 I y 是圖像在 x 和 y 方向的導數。(可以使用函數 cv2.Sobel()
計算得到)。
然后就是主要部分了。他們根據一個用來判定窗口內是否包含角點的等式進行打分。

其中
• λ 1 和 λ 2 是矩陣 M 的特征值所以根據這些特征中我們可以判斷一個區域是否是角點,邊界或者是平面。
• 當 λ 1 和 λ 2 都小時,|R| 也小,這個區域就是一個平坦區域。
• 當 λ 1 ≫ λ 2 或者 λ 1 ≪ λ 2 ,時 R 小於 0,這個區域是邊緣
• 當 λ 1 和 λ 2 都很大,並且 λ 1 ~λ 2 中的時,R 也很大,(λ 1 和 λ 2 中的最小值都大於閾值)說明這個區域是角點。
可以用下圖來表示我們的結論:

所以 Harris 角點檢測的結果是一個由角點分數構成的灰度圖像。選取適當的閾值對結果圖像進行二值化我們就檢測到了圖像中的角點。我們將用一個簡單的圖片來演示一下。
30.1 OpenCV 中的 Harris 角點檢測
Open 中的函數 cv2.cornerHarris() 可以用來進行角點檢測。參數如下:
• img - 數據類型為 float32 的輸入圖像。
• blockSize - 角點檢測中要考慮的領域大小。
• ksize - Sobel 求導中使用的窗口大小
• k - Harris 角點檢測方程中的自由參數,取值參數為 [0,04,0.06].
例子如下:
import cv2 import numpy as np filename = 'chessboard.jpg' img = cv2.imread(filename) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) gray = np.float32(gray) dst = cv2.cornerHarris(gray,2,3,0.04) #result is dilated for marking the corners, not important dst = cv2.dilate(dst,None) # Threshold for an optimal value, it may vary depending on the image. img[dst>0.01*dst.max()]=[0,0,255] cv2.imshow('dst',img) if cv2.waitKey(0) & 0xff == 27: cv2.destroyAllWindows()
結果如下:

30.2 亞像素級精確度的角點
有時我們需要最大精度的角點檢測。OpenCV 為我們提供了函數 cv2.cornerSubPix(),它可以提供亞像素級別的角點檢測。下面是一個例子。首先我們要找到 Harris角點,然后將角點的重心傳給這個函數進行修正。Harris 角點用紅色像素標出,綠色像素是修正后的像素。在使用這個函數是我們要定義一個迭代停止條件。當迭代次數達到或者精度條件滿足后迭代就會停止。我們同樣需要定義進行角點搜索的鄰域大小。
import cv2 import numpy as np filename = 'chessboard2.jpg' img = cv2.imread(filename) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # find Harris corners gray = np.float32(gray) dst = cv2.cornerHarris(gray,2,3,0.04) dst = cv2.dilate(dst,None) ret, dst = cv2.threshold(dst,0.01*dst.max(),255,0) dst = np.uint8(dst) # find centroids ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst) # define the criteria to stop and refine the corners criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001) corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria) # Now draw them res = np.hstack((centroids,corners)) res = np.int0(res) img[res[:,1],res[:,0]]=[0,0,255] img[res[:,3],res[:,2]] = [0,255,0] cv2.imwrite('subpixel5.png',img)
結果如下,為了方便查看我們對角點的部分進行了放大:

31 Shi-Tomasi 角點檢測 & 適合於跟蹤的圖像特征
目標
本節我們將要學習:
• 另外一個角點檢測技術:Shi-Tomasi 焦點檢測
• 函數:cv2.goodFeatureToTrack()
原理
上一節我們學習了 Harris 角點檢測,后來 1994 年,J.Shi 和 C.Tomasi在他們的文章《Good_Features_to_Track》中對這個算法做了一個小小的修改,並得到了更好的結果。我們知道 Harris 角點檢測的打分公式為:

但 Shi-Tomasi 使用的打分函數為:

如果打分超過閾值,我們就認為它是一個角點。我們可以把它繪制到 λ 1 ~λ 2 空間中,就會得到下圖:

從這幅圖中,我們可以看出來只有當 λ 1 和 λ 2 都大於最小值時,才被認為是角點(綠色區域)。
31.1 代碼
OpenCV 提供了函數:cv2.goodFeaturesToTrack()。這個函數可以幫我們使用 Shi-Tomasi 方法獲取圖像中 N 個最好的角點(如果你願意的話,187
www.linuxidc.com
也可以通過改變參數來使用 Harris 角點檢測算法)。通常情況下,輸入的應該是灰度圖像。然后確定你想要檢測到的角點數目。再設置角點的質量水平,0到 1 之間。它代表了角點的最低質量,低於這個數的所有角點都會被忽略。最后在設置兩個角點之間的最短歐式距離。
根據這些信息,函數就能在圖像上找到角點。所有低於質量水平的角點都會被忽略。然后再把合格角點按角點質量進行降序排列。函數會采用角點質量最高的那個角點(排序后的第一個),然后將它附近(最小距離之內)的角點都刪掉。按着這樣的方式最后返回 N 個最佳角點。
在下面的例子中,我們試着找出 25 個最佳角點:
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('simple.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) corners = cv2.goodFeaturesToTrack(gray,25,0.01,10) corners = np.int0(corners) for i in corners: x,y = i.ravel() cv2.circle(img,(x,y),3,255,-1) plt.imshow(img),plt.show()
結果如下:

我們以后會發現這個函數很適合在目標跟蹤中使用。
32 介紹 SIFT(Scale-Invariant Feature Trans-form)
目標
• 學習 SIFT 算法的概念
• 學習在圖像中查找 SIFT 關鍵點和描述符
原理
在前面兩節我們學習了一些角點檢測技術,比如 Harris 等。它們具有旋轉不變特性,即使圖片發生了旋轉,我們也能找到同樣的角點。很明顯即使圖像發生旋轉之后角點還是角點。那如果我們對圖像進行縮放呢?角點可能就不再是角點了。以下圖為例,在一副小圖中使用一個小的窗口可以檢測到一個角點,但是如果圖像被放大,再使用同樣的窗口就檢測不到角點了。

所以在 2004 年,D.Lowe 提出了一個新的算法:尺度不變特征變換(SIFT),這個算法可以幫助我們提取圖像中的關鍵點並計算它們的描述符。
SIFT 算法主要由四步構成。我們來逐步進行學習。
尺度空間極值檢測
從上圖我們可以很明顯的看出來在不同的尺度空間不能使用相同的窗口檢測極值點。對小的角點要用小的窗口,對大的角點只能使用大的窗口。為了達到這個目的我們要使用尺度空間濾波器。(尺度空間濾波器可以使用一些列具有不同方差 σ 的高斯卷積核構成)。使用具有不同方差值 σ 的高斯拉普拉斯算子(LoG)對圖像進行卷積,LoG 由於具有不同的方差值 σ 所以可以用來檢測不同大小的斑點(當 LoG 的方差 σ 與斑點直徑相等時能夠使斑點完全平滑)。簡單來說方差 σ 就是一個尺度變換因子。例如,上圖中使用一個小方差 σ 的高斯卷積核是可以很好的檢測出小的角點,而使用大方差 σ 的高斯卷積核時可以很好的檢測除大的角點。所以我們可以在尺度空間和二維平面中檢測到局部最大值,如(x,y,σ), 這表示在 σ 尺度中(x,y)點可能是一個關鍵點。(高斯方差的大小與窗口的大小存在一個倍數關系:窗口大小等於 6 倍方差加 1,所以方差的大小也決定了窗口大小)但是這個 LoG 的計算量非常大,所以 SIFT 算法使用高斯差分算子(DoG)來對 LoG 做近似。這里需要再解釋一下圖像金字塔,我們可以通過減少采樣(如只取奇數行或奇數列)來構成一組圖像尺寸(1,0.5,0.25 等)不同的金字塔,然后對這一組圖像中的每一張圖像使用具有不同方差 σ 的高斯卷積核構建出具有不同分辨率的圖像金字塔(不同的尺度空間)。DoG 就是這組具有不同分辨率的圖像金字塔中相鄰的兩層之間的差值。如下圖所示:

在 DoG 搞定之后,就可以在不同的尺度空間和 2D 平面中搜索局部最大值了。對於圖像中的一個像素點而言,它需要與自己周圍的 8 鄰域,以及尺度空間中上下兩層中的相鄰的 18(2x9)個點相比。如果是局部最大值,它就可能是一個關鍵點。基本上來說關鍵點是圖像在相應尺度空間中的最好代表。如下圖所示:

該算法的作者在文章中給出了 SIFT 參數的經驗值:octaves=4(通過降低采樣從而減小圖像尺寸,構成尺寸減小的圖像金字塔(4 層)?),尺度空間為 5,也就是每個尺寸使用 5 個不同方差的高斯核進行卷積,初始方差是 1.6,
,
等。
關鍵點(極值點)定位
一旦找到關鍵點,我們就要對它們進行修正從而得到更准確的結果。
作者使用尺度空間的泰勒級數展開來獲得極值的准確位置,如果極值點的灰度值小於閾值(0.03)就會被忽略掉。在 OpenCV 中這種閾值被稱為contrastThreshold。
DoG 算法對邊界非常敏感,所以我們必須要把邊界去除。前面我們講的Harris 算法除了可以用於角點檢測之外還可以用於檢測邊界。作者就是使用了同樣的思路。作者使用 2x2 的 Hessian 矩陣計算主曲率。從 Harris 角點檢測的算法中,我們知道當一個特征值遠遠大於另外一個特征值時檢測到的是邊界。所以他們使用了一個簡單的函數,如果比例高於閾值(OpenCV 中稱為邊界閾值),這個關鍵點就會被忽略。文章中給出的邊界閾值為 10。
所以低對比度的關鍵點和邊界關鍵點都會被去除掉,剩下的就是我們感興趣的關鍵點了。
為關鍵點(極值點)指定方向參數
現在我們要為每一個關鍵點賦予一個反向參數,這樣它才會具有旋轉不變性。獲取關鍵點(所在尺度空間)的鄰域,然后計算這個區域的梯度級和方向。根據計算得到的結果創建一個含有 36 個 bins(每 10 度一個 bin)的方向直方圖。(使用當前尺度空間 σ 值的 1.5 倍為方差的圓形高斯窗口和梯度級做權重)。直方圖中的峰值為主方向參數,如果其他的任何柱子的高度高於峰值的80% 被認為是輔方向。這就會在相同的尺度空間相同的位置構建除具有不同方向的關鍵點。這對於匹配的穩定性會有所幫助。
關鍵點描述符
新的關鍵點描述符被創建了。選取與關鍵點周圍一個 16x16 的鄰域,把它分成 16 個 4x4 的小方塊,為每個小方塊創建一個具有 8 個 bin 的方向直方圖。總共加起來有 128 個 bin。由此組成長為 128 的向量就構成了關鍵點描述符。除此之外還要進行幾個測量以達到對光照變化,旋轉等的穩定性。
關鍵點匹配
下一步就可以采用關鍵點特征向量的歐式距離來作為兩幅圖像中關鍵點的相似性判定度量。取第一個圖的某個關鍵點,通過遍歷找到第二幅圖像中的距離最近的那個關鍵點。但有些情況下,第二個距離最近的關鍵點與第一個距離最近的關鍵點靠的太近。這可能是由於噪聲等引起的。此時要計算最近距離與第二近距離的比值。如果比值大於 0.8,就忽略掉。這會去除 90% 的錯誤匹配,同時只去除 5% 的正確匹配。如文章所說。
這就是 SIFT 算法的摘要。非常推薦你閱讀原始文獻,這會加深你對算法的理解。請記住這個算法是受專利保護的。所以這個算法包含在 OpenCV 中的收費模塊中。
OpenCV 中的 SIFT
現在讓我們來看看 OpenCV 中關於 SIFT 的函數吧。讓我們從關鍵點檢測和繪制開始吧。首先我們要創建對象。我們可以使用不同的參數,這並不是必須的,關於參數的解釋可以查看文檔。
import cv2 import numpy as np img = cv2.imread('home.jpg') gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) sift = cv2.SIFT() kp = sift.detect(gray,None) img=cv2.drawKeypoints(gray,kp) cv2.imwrite('sift_keypoints.jpg',img)
函數 sift.detect() 可以在圖像中找到關鍵點。如果你只想在圖像中的一個區域搜索的話,也可以創建一個掩模圖像作為參數使用。返回的關鍵點是一個帶有很多不同屬性的特殊結構體,這些屬性中包含它的坐標(x,y),有意義的鄰域大小,確定其方向的角度等。
OpenCV 也提供了繪制關鍵點的函數:cv2.drawKeyPoints(),它可以在關鍵點的部位繪制一個小圓圈。如果你設置參數為 cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
就會繪制代表關鍵點大小的圓圈甚至可以繪制除關鍵點的方向。
img=cv2.drawKeypoints(gray,kp,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()一步到位直接找到關鍵點並計算出其描述符。
這里我們來看看第二個方法:
sift = cv2.SIFT()
kp, des = sift.detectAndCompute(gray,None)
這里 kp 是一個關鍵點列表。des 是一個 Numpy 數組,其大小是關鍵點數目乘以 128。
所以我們得到了關鍵點和描述符等。現在我們想看看如何在不同圖像之間進行關鍵點匹配,這就是我們在接下來的章節將要學習的內容。
33 介紹 SURF(Speeded-Up Robust Features)
目標
本節我們將要學習:
• SUFR 的基礎是什么?
• OpenCV 中的 SURF
原理
在上一節中我們學習了使用 SIFT 算法進行關鍵點檢測和描述。但是這種算法的執行速度比較慢,人們需要速度更快的算法。在 2006 年Bay,H.,Tuytelaars,T. 和 Van Gool,L 共同提出了 SURF(加速穩健特征)算法。跟它的名字一樣,這是個算法是加速版的 SIFT。
在 SIFT 中,Lowe 在構建尺度空間時使用 DoG 對 LoG 進行近似。SURF使用盒子濾波器(box_filter)對 LoG 進行近似。下圖顯示了這種近似。在進行卷積計算時可以利用積分圖像(積分圖像的一大特點是:計算圖像中某個窗口內所有像素和時,計算量的大小與窗口大小無關),是盒子濾波器的一大優點。而且這種計算可以在不同尺度空間同時進行。同樣 SURF 算法計算關鍵點的尺度和位置是也是依賴與 Hessian 矩陣行列式的。

為了保證特征矢量具有選裝不變形,需要對於每一個特征點分配一個主要方向。需要以特征點為中心,以 6s(s 為特征點的尺度)為半徑的圓形區域內,對圖像進行 Harr 小波相應運算。這樣做實際就是對圖像進行梯度運算,但是利用積分圖像,可以提高計算圖像梯度的效率,為了求取主方向值,需喲啊設計一個以方向為中心,張角為 60 度的扇形滑動窗口,以步長為 0.2 弧度左右旋轉這個滑動窗口,並對窗口內的圖像 Haar 小波的響應值進行累加。主方向為最大的 Haar 響應累加值對應的方向。在很多應用中根本就不需要旋轉不變性,所以沒有必要確定它們的方向,如果不計算方向的話,又可以使算法提速。SURF 提供了成為 U-SURF 的功能,它具有更快的速度,同時保持了對 +/-15 度旋轉的穩定性。OpenCV 對這兩種模式同樣支持,只需要對參數upright 進行設置,當 upright 為 0 時計算方向,為 1 時不計算方向,同時速度更快。

生成特征點的特征矢量需要計算圖像的 Haar 小波響應。在一個矩形的區域內,以特征點為中心,沿主方向將 20s*20s 的圖像划分成 4*4 個子塊,每個子塊利用尺寸 2s 的 Haar 小波模版進行響應計算,然后對響應值進行統計,組成向量
。這個描述符的長度為 64。降低的
維度可以加速計算和匹配,但又能提供更容易區分的特征。
為了增加特征點的獨特性,SURF 還提供了一個加強版 128 維的特征描述符。當 d y 大於 0 和小於 0 時分別對 d x 和 |d x | 的和進行計算,計算 d y和 |d y | 時也進行區分,這樣獲得特征就會加倍,但又不會增加計算的復雜度。
For more distinctiveness, SURF feature descriptor has an extended 128 dimension version. The sums of
and
are computed separately for
and
. Similarly, the sums of
and
are split up according to the sign of
, thereby doubling the number of features.
OpenCV 同樣提供了這種功能,當參數 extended 設置為 1 時為 128 維,當參數為 0 時為 64 維,默認情況為 128 維。
在檢測特征點的過程中計算了 Hessian 矩陣的行列式,與此同時,計算得到了 Hessian 矩陣的跡,矩陣的跡為對角元素之和。
按照亮度的不同,可以將特征點分為兩種,第一種為特征點跡其周圍小鄰域的亮度比背景區域要亮,Hessian 矩陣的跡為正;另外一種為特征點跡其周圍小鄰域的亮度比背景區域要暗,Hessian 矩陣為負值。根據這個特性,首先對兩個特征點的 Hessian 的跡進行比較。如果同號,說明兩個特征點具有相同的對比度;如果異號的話,說明兩個特征點的對比度不同,放棄特征點之間的后續的相似性度量。
對於兩個特征點描述子的相似性度量,我們采用歐式距離進行計算。

簡單來說 SURF 算法采用了很多方法來對每一步進行優化從而提高速度。
分析顯示在結果效果相當的情況下 SURF 的速度是 SIFT 的 3 倍。SURF 善於處理具有模糊和旋轉的圖像,但是不善於處理視角變化和關照變化。
33.1 OpenCV 中的 SURF
與 SIFT 相同 OpenCV 也提供了 SURF 的相關函數。首先我們要初始化一個 SURF 對象,同時設置好可選參數:64/128 維描述符,Upright/Normal 模式等。所有的細節都已經在文檔中解釋的很明白了。就像我們在SIFT 中一樣,我們可以使用函數 SURF.detect(),SURF.compute() 等來進行關鍵點攙着和描述。
首先從查找描述繪制關鍵點開始。由於和 SIFT 一樣所以我們的示例都在Python 終端中演示。
>>> img = cv2.imread('fly.png',0) # Create SURF object. You can specify params here or later. # Here I set Hessian Threshold to 400 >>> surf = cv2.SURF(400) # Find keypoints and descriptors directly >>> kp, des = surf.detectAndCompute(img,None) >>> len(kp) 699
在一幅圖像中顯示 699 個關鍵點太多了。我們把它縮減到 50 個再繪制到圖片上。在匹配時,我們可能需要所有的這些特征,不過現在還不需要。所以我們現在提高 Hessian 的閾值。
# Check present Hessian threshold >>> print surf.hessianThreshold 400.0 # We set it to some 50000. Remember, it is just for representing in picture. # In actual cases, it is better to have a value 300-500 >>> surf.hessianThreshold = 50000 # Again compute keypoints and check its number. >>> kp, des = surf.detectAndCompute(img,None) >>> print len(kp) 47
現在低於 50 了,把它們繪制到圖像中吧。
>>> img2 = cv2.drawKeypoints(img,kp,None,(255,0,0),4)
>>> plt.imshow(img2),plt.show()
結果如下。你會發現 SURF 很像一個斑點檢測器。它可以檢測到蝴蝶翅膀上的白班。你可以在其他圖片中測試一下。

現在我們試一下 U-SURF,它不會檢測關鍵點的方向。
# Check upright flag, if it False, set it to True >>> print surf.upright False >>> surf.upright = True # Recompute the feature points and draw it >>> kp = surf.detect(img,None) >>> img2 = cv2.drawKeypoints(img,kp,None,(255,0,0),4) >>> plt.imshow(img2),plt.show()
結果如下。所有的關鍵點的朝向都是一致的。它比前面的快很多。如果你的工作對關鍵點的朝向沒有特別的要求(如全景圖拼接)等,這種方法會更快。

最后我們再看看關鍵點描述符的大小,如果是 64 維的就改成 128 維。
# Find size of descriptor
# Find size of descriptor >>> print surf.descriptorSize() 64 # That means flag, "extended" is False. >>> surf.extended False # So we make it to True to get 128-dim descriptors. >>> surf.extended = True >>> kp, des = surf.detectAndCompute(img,None) >>> print surf.descriptorSize() 128 >>> print des.shape (47, 128)
接下來要做的就是匹配了,我們會在后面討論。


