python opencv入門 SURF演算法(34)
內容來自OpenCV-Python Tutorials 自己翻譯整理
目標:
SURF演算法基礎
opencv總SURF演算法的使用
原理:
上節課使用了SIFT演算法,當時這種演算法效率不高,需要更快速的演算法。在06年有人提出了SURF演算法“加速穩定特徵”,從名字上來看,他是SIFT演算法的加速版本。
(原文)
在SIFT演算法當中使用高斯差分方程(Difference of Gaussian)對高斯拉普拉斯方程( Laplacian of Gaussian)進行近似。然而,SURF使用盒子濾波器進行近似,下面的圖片顯示了這種近似的方法。在進行卷積計算的時候可以利用積分影象
SURF演算法計算關鍵點的尺度和位置資訊使用Hessian矩陣實現。
(解釋)
文中的高斯拉普拉斯方程(運算元)是檢測影象中斑點的一種十分常用的方法。以一維高斯函式來檢測一維訊號中的斑點為例。有一維訊號f,高斯函式的一階導數,訊號與高斯函式的一階導數卷積後,會在邊緣處出現極值。如圖:
上面圖片是在一維情況下,使用高斯函式的一階導數的情況,另一種方法是使用高斯函式的二階導數與訊號進行卷積,高斯函式的二階導數也叫做拉普拉斯變換。
但是,在一維訊號斑點檢測的實際情況當中,一個斑點可以考慮成是兩個相鄰的跳突組成,如下圖。
類似於在影象當中,一個輪胎可以當成一個斑點,一個蒼蠅也可以當成一個斑點。但是在使用高斯函式的二階導數來檢測斑點的時候,使用不同的高斯核(就是方差)運算不同大小的斑點時,計算出來的極值,即響應值會出現衰減。
此時,需要將高斯函式的二階導數進行正規化,去除方差值不同導致響應值出現的衰減。
以上,是一維高斯函式檢測一維訊號的原理。二維的影象訊號,使用二維高斯函式來檢測斑點原理基本相同,此處的二維高斯函式的二階導數,就叫做高斯拉普拉斯運算元也就是LOG,通過改變不同的方差值,可以檢測不同尺寸的二維斑點,如圖。
文中的高斯差分方程是SIFT演算法當中,發明者想要利用兩個相鄰高斯尺度空間的影象相減來得到一個LOG的近似,因為這樣做可以節省時間,而且可以控制精度變化,類似於高等數學當中泰勒公式那玩意-_- 。關於SIFT原理可以看上一篇部落格
文中提到的積分影象實際上原理非常簡單,類似遞推方程。積分影象的目的是想建立一個函式,能夠快速得到一個矩形影象區域當中所有畫素值的和是多少。那麼,設表示從點到點的所有畫素的和是多少,儲存在這個數組裡面,如果想要獲得W區域的畫素和是多少,如圖,只要計算即可。
如何求得? 遞推公式為,,這裡面表示畫素點處的畫素值。
文中提到的Hessian矩陣,學過數學分析、最優化、機器學習之類的人肯定對這玩意非常熟悉,實際上黑塞矩陣就是一個多元函式的二階偏導數構成的方陣,它的行列式值(Determinant of Hessian )可以反映的區域性結構資訊,簡稱DOH。與LOG類似,DOH可以使用不同方差生成高斯函式對各個元的二階偏導模板,以此來對影象進行卷積運算。 同樣,DOH也會在卷積後的函式中,得到對影象訊號斑點極值的響應。如圖
在SURF演算法當中,黑塞矩陣中的L,即為二維高斯函式與影象的卷積,求得黑塞矩陣後,會得到如圖。
將上面得到的模板與影象的卷積轉換為盒子濾波器,這裡使用原文中的影象,如圖。
得到三個不同的盒子濾波器以後,對其進行近似和簡化操作,並用其表示影象中某點的斑點響應值,遍歷影象當中的所有畫素,就得到了在某一尺度下斑點檢測的響應影象。然後,利用不同的模板尺寸,獲取多尺度斑點響應金字塔,在金字塔中搜索極值點,下面的操作就和SIFT演算法類似了。
(原文)
為了給找到的特徵點賦予方向,以特徵點為中心,6s為半徑獲取水平和垂直小波響應運算結果,這裡s是特徵點尺度,同時使用高斯加權的方法。然後,他們會被繪製在如下圖當中。其中,特徵點的主方向估計運算是有一個弧度為60的扇形視窗,在滑動的過程中不斷計算其中的響應值之和。有趣的是,小波響應值在任意尺度下使用積分影象很容易被獲取。但是在多數情況下,旋轉不變性不是必須的,可以程式碼當中將這一步取消,這樣還能夠提高演算法計算速度,而且在+-15度的情況也保持穩定,此時該方法稱作 U-SURF。使用者可以設定upright引數,當引數為0計算方向,引數為1不計算方向。
對於特徵點描述的建立,SURF再一次使用Haar小波響應,同時使用積分影象使操作變得簡單。在一個矩形區域當中,以特徵點為中心,劃取周圍20s×20s區域的大小,以特徵點為原點,主方向為橫軸,分成四個子區域,每個子區域使用2s的Haar小波響應,對於每個子區域,獲取一個向量,記錄垂直、水平方向上的小波響應值,如圖。
這個特徵描述符的長度使64,降低維度可以加速計算,又可以區分特徵。為了更好的區分特徵點,SURF還使用了長度為128特徵描述符。當dy小於0或者大於0時,計算dx或|dx|的和。同樣,根據dx的符號計算不同的dy和。因此能夠獲得雙倍的特徵。計算複雜度也不會增加。opencv當中的extended引數為0或1時分別對應64和128的特徵。
另外一個重要的改善是對潛在的興趣點使用了拉普拉斯運算元符號(黑塞矩陣的跡)。由於之前的計算已經完成對黑塞矩陣的構造,所以這步不會增加複雜度。
拉普拉斯符號在不同明暗背景下區分不同亮度的斑點,在匹配階段,我們只需要比較擁有相同對比度的特徵是否匹配即可,這樣加快了計算速度,如圖。
SURF演算法的速度是SIFT速度的3倍,善於處理模糊和旋轉的影象,但是不善於處理視角變化和關照變化。
(解釋)
文中的小波響應運算,全稱是haar小波運算。這裡使用haar小波目的是為了獲取影象梯度,使用之前計算好的影象積分結果,這樣能夠提高計算速度。與SIFT演算法類似,在對每個特徵點獲取主方向時,使用原文中提到的一個π/3大小的扇形視窗,同時以0.2弧度為步長旋轉滑動此視窗,在每個視窗當中對的haar響應值的水平方向,垂直方向進行累加。由於時使用一個圓形區域,轉換成類似極座標向量的方式來表示,每個視窗中的結果,如圖。
主方向最大Haar響應值累加對應的方向。其中,如果除了主方向,還有其它方向的響應累加值較大,演算法當中還會額外新增一個特徵點,並賦予另外一個次大方向。
文中建立的特徵描述符顧名思義,就是描述一個特徵點的一組向量,裡面唯一確定了一個特徵。SURF獲取主方向後,需要獲取特徵點描述子。以特徵點為原點,主方向為橫軸建立一個二維座標系,區域大小是20s×20s,分成是個之塊,每個子塊利用2s的haar模板進行響應計算。然後統計,每個20s的視窗分成4×4的子視窗,每個子視窗中又5s×5s個像元。如圖
又4×4個子塊,每個子塊裡面記錄四個值,所以描述子一共又4×4×4=64個特徵。
最後將沿著主方向的小波響應值扭轉過來,原理就是簡單的旋轉矩陣。
程式碼部分
opencv裡面提供的SURF演算法和SIFT差不多,這兩個玩意都是受到版權保護的,如果你是用pip 一條命令安裝的opencv,那麼恭喜你用不了SURF和SIFT演算法,印象中只有2.4.9版本的opencv庫才可以使用。
不過,辦法還是有的,再控制檯當中輸入pip install opencv-contrib-python 就可以用了。
如果還是無法安裝,可以直接網站早opencv-contrib-python的輪子,然後放到對應的檔案下安裝就行了。
import cv2
import numpy as np
img = cv2.imread('feng.jpg')
#引數為hessian矩陣的閾值
surf = cv2.xfeatures2d.SURF_create(400)
#找到關鍵點和描述符
key_query,desc_query = surf.detectAndCompute(img,None)
#把特徵點標記到圖片上
img=cv2.drawKeypoints(img,key_query,img)
cv2.imshow('sp',img)
cv2.waitKey(0)
鳳的嘴上特徵點佔了這麼多,辨識度還是蠻高的~ -_-|||
下面是設定方向,和輸出一些值的方法
import cv2
import numpy as np
img = cv2.imread('feng.jpg')
#引數為hessian矩陣的閾值
surf = cv2.xfeatures2d.SURF_create(4000)
#設定是否要檢測方向
surf.setUpright(True)
#輸出設定值
print(surf.getUpright())
#找到關鍵點和描述符
key_query,desc_query = surf.detectAndCompute(img,None)
img=cv2.drawKeypoints(img,key_query,img)
#輸出描述符的個數
print(surf.descriptorSize())
cv2.imshow('sp',img)
cv2.waitKey(0)