上一節中,我們介紹了Harris角點檢測。角點在圖像旋轉的情況下也可以檢測到,但是如果減小(或者增加)圖像的大小,可能會丟失圖像的某些部分,甚至導致檢測到的角點發生改變。這樣的損失現象需要一種與圖像比例無關的角點檢測方法來解決。尺度不變特征變換(Scale-Invariant Feature Transform,SIFT)可以解決這個問題。我們使用一個變換來進行特征變換,並且該變換會對不同的圖像尺度輸出相同的結果。
到底什么是SIFT算法?通俗一點說,SIFT算法利用DoG(差分高斯)來提取關鍵點(或者說成特征點),DoG的思想是用不同的尺度空間因子(高斯正態分布的標准差$\sigma$)對圖像進行平滑,然后比較平滑后圖像的區別,差別大的像素就是特征明顯的點,即可能是特征點。對得到的所有特征點,我們剔除一些不好的,SIFT算子會把剩下的每個特征點用一個128維的特征向量進行描述。
由上,易知,一幅圖像經過SIFT算法后可以表示為一個128維的特征向量集。
SIFT具有一下特征:
- SIFT特征,對旋轉、尺度縮放、亮度變化等保持不變性,對視角變換、仿射變化、噪聲也保持一定程度的穩定性,是一種非常優秀的局部特征描述算法;
- 獨特性好,信息量豐富,適用於海量特征庫進行快速、准確的匹配;
- 多量性,即使是很少幾個物體也可以產生大量的SIFT特征;
- 高速性,經優化的SIFT匹配算法甚至可以達到實時性的要求;
- 擴展性,可以很方便的與其他的特征向量進行聯合。
一 使用DoG和SIFT進行特征提取和描述
我們先用OpenCV庫函數演示一下DoG和SIFT特征提取的效果,然后再來講述一下SIFT的原理。
我們先來介紹一下Different of Gaussians(DoG),DoG是對同一圖象使用不同高斯濾波器作差所得到的結果。DoG操作的最終結果會得到感興趣的區域(關鍵點),這將通過SIFT來進行特征描述。
我們來看看如何通過SIFT得到充滿角點和特征的圖像:
# -*- coding: utf-8 -*- """ Created on Wed Aug 22 16:53:16 2018 @author: lenovo """ ''' SIFT算法 ''' import cv2 import numpy as np img = cv2.imread('./image/cali.bmp') img = cv2.resize(img,dsize=(600,400)) #轉換為灰度圖像 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #創建一個SIFT對象 sift = cv2.xfeatures2d.SIFT_create() #SIFT對象會使用DoG檢測關鍵點,並且對每個關鍵點周圍的區域計算特征向量。該函數返回關鍵點的信息和描述符 keypoints,descriptor = sift.detectAndCompute(gray,None) print(type(keypoints),len(keypoints),keypoints[0]) print(descriptor.shape) #在圖像上繪制關鍵點 img = cv2.drawKeypoints(image=img,keypoints = keypoints,outImage=img,color=(255,0,255),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) #顯示圖像 cv2.imshow('sift_keypoints',img) cv2.waitKey(0) cv2.destroyAllWindows()
我們首先創建了一個SIFT對象,SIFT對象會使用DoG檢測關鍵點,並且對每個關鍵點周圍區域計算特征向量。detectAndCompute()函數會返回關鍵點信息(每一個元素都是一個對象,有興趣的可以看一下OpenCV源碼)和關鍵點的描述符。然后,我們在圖像上繪制關鍵點,並顯示出來。
上面我們用到了一個函數,下面來介紹一下:
cv2.drawKeypoints(image,keypoints,outImage[,color[,flags]])
參數描述:
image:原始圖像,可以使三通道或單通道圖像;
keypoints:特征點集合list,向量內每一個元素是一個KeyPoint對象,包含了特征點的各種屬性信息;
outImage:特征點繪制的畫布圖像,可以是原圖像;
color:顏色設置,繪制的特征點的顏色信息,默認繪制的是隨機彩色;
flags:特征點的繪制模式,其實就是設置特征點的哪些信息需要繪制,哪些不需要繪制,有以下幾種模式可選:
- cv2.DRAW_MATCHES_FLAGS_DEFAULT:創建輸出圖像矩陣,使用現存的輸出圖像繪制匹配對和特征點,對每一個關鍵點只繪制中間點;
- cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG:不創建輸出圖像矩陣,而是在輸出圖像上繪制匹配對;
- cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:對每一個特征點繪制帶大小和方向的關鍵點圖形;
- cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:單點的特征點不被繪制;
我們傳入了DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS標志位,表明對圖像每個關鍵點都繪制圓圈(大小)和方向。
二 SIFT算法原理
1、SIFT特征檢測的步驟
- 尺度空間的極值檢測:搜索所有高斯尺度空間上的圖像,通過高斯差分函數(DoG)來識別潛在的對尺度和選擇不變的興趣點。
- 關鍵點的定位和過濾:在每個候選的位置上,通過一個擬合精細模型來確定位置尺度,關鍵點的選取依據他們的穩定程度。
- 特征方向賦值:基於圖像局部的梯度方向,分配給每個關鍵點位置一個或多個方向,后續的所有操作都是對於關鍵點的方向、尺度和位置進行變換,從而提供這些特征的不變性。
- 特征點描述:在每個特征點周圍的鄰域內,在選定的尺度上測量圖像的局部梯度,這些梯度被變換成一種表示,這種表示允許比較大的局部形狀的變形和光照變換。
2、尺度空間
在一定的范圍內,無論物體是大還是小,人眼都可以分辨出來。然而計算機要有相同的能力卻不是那么簡單的事情,在未知的場景中,計算機視覺並不能提供物體的尺度大小,其中的一個方法就是把物體不同尺寸下的圖像都提供給機器,讓機器能夠對物體再不同的尺度下有一個統一的認知。在建立統一認知的過程中,要考慮的就是圖像在不同的尺度下都存在的特征點。
2.1 多分辨率圖像金字塔
在早期圖像的多尺度通常使用圖像金字塔表述形式。圖像金字塔是同一圖象在不同的分辨率下得到的一組結果,其生成過程一半包含兩個步驟:
1、對原始圖像進行平滑出來;
2、對處理后的圖像進行降維采樣(通常是水平、垂直方向的1/2);降維采樣后得到一系列尺寸不斷縮小的圖像。顯然,一個傳統的金字塔中,每一層的圖像是其上一層圖像長、高的各一半。多分辨率的圖像金字塔雖然生成簡單,但其本質是降維采樣,圖像的局部特征則難以保持,也就是無法保持特性的尺度不變性。
2.2 高斯尺度空間
我們還可以通過圖像的模糊程度來模擬人在距離物體遠近時物體在視網膜上的成像過程,距離物體越近其尺寸越大圖像也越清晰,這就是高斯尺度空間,使用不同的參數模糊圖像(保持分辨率不變),是尺度空間的另一種表現形式。
我們知道圖像和高斯函數進行卷積運算能夠對圖像進行模糊,使用不同的"高斯核"可得到不同模糊程度圖像。
高斯核函數$G(x,y,σ)$(二維高斯函數)的公式可以寫成:
$$G(x,y,σ)=\frac{1}{2\pi σ^2}e^{\frac{x^2+y^2}{2σ^2}}$$
$σ$稱為尺度空間因子,它是高斯正太分布的標准差,反映了圖像被模糊的程度,其值越大圖像越模糊,對應的尺度也就越大。
在二維空間中,這個公式生成的曲面的等高線是從中心開始呈正態分布的同心圓,如圖所示。分布不為零的像素組成的卷積矩陣與原始圖像做變換。每個像素的值都是周圍相鄰像素值的加權平均。原始像素的值有最大的高斯分布值,所以有最大的權重,相鄰像素隨着距離原始像素越來越遠,其權重也越來越小。這樣進行模糊處理比其它的均衡模糊濾波器更高地保留了邊緣效果。
一副圖像其高斯尺度空間可由其和不同的高斯卷積得到:
$$L(x,y,σ)=G(x,y,σ)*I(x,y)$$
$L(x,y,σ)$代表着圖像的高斯尺度空間。下圖為圖像取不同$\sigma$值得到的高斯尺度空間:
構建尺度空間的目的是為了檢測出在不同的尺度下都存在的特征點,而檢測特征點較好的算子是$\sigma^2\nabla^2G$(高斯拉普拉斯,LoG,圖像的二階導數),是由2002年Mikolajczyk在詳細的實驗比較中發現的,同其它的特征提取函數,例如:梯度,Hessian或Harris角特征比較,能夠產生最穩定的圖像特征。
而Lindeberg早在1994年就發現高斯差分函數(Difference of Gaussian ,簡稱DOG算子)與尺度歸一化的高斯拉普拉斯函數$\sigma^2\nabla^2G$非常近似。使用LoG雖然能較好的檢測到圖像中的特征點,但是其運算量過大,通常可使用DoG(差分高斯,Difference of Gaussina)來近似計算LoG。其中$D(x,y,\sigma)$和$\sigma^2\nabla^2G$的關系可以從如下公式推導得到:$$\frac{\partial{G}}{\partial{\sigma}}=\sigma^2\nabla^2G$$
利用差分近似代替微分,則有:$$\sigma^2\nabla^2G=\frac{\partial{G}}{\partial{\sigma}}≈\frac{G(x,y,k\sigma)-G(x,y,\sigma)}{k\sigma-\sigma}$$
因此有:$$G(x,y,k\sigma)-G(x,y,\sigma)≈(k-1)\sigma^2\nabla^2G$$
其中k-1是個常數,並不影響極值點位置的求取。高斯拉普拉斯和高斯差分的比較如下:
如圖所示,紅色曲線表示的是高斯差分算子,而藍色曲線表示的是高斯拉普拉斯算子。Lowe使用更高效的高斯差分算子代替拉普拉斯算子進行極值檢測,設$k$為相鄰兩個高斯尺度空間的比例因子,則DoG的定義:
$$D(x,y,σ)=[G(x,y,kσ)-G(x,y,σ)]*I(x,y)$$ $$=L(x,y,kσ)-L(x,y,σ)$$
其中,$L(x,y,σ)$是圖像的高斯尺度空間。
從上式可以知道,將相鄰的兩個高斯空間的圖像相減就得到了DoG的響應圖像。為了得到DoG圖像,先要構造高斯尺度空間,而高斯的尺度空間可以在圖像金字塔降維采樣的基礎上加上高斯濾波得到,也就是對圖像金字塔的每層圖像使用不同的尺度參數$\sigma$進行高斯模糊,使每層金字塔有多張高斯模糊過的圖像,然后我們把得到的同一尺寸大小的圖像划分為一組。
易知,高斯金字塔有多組(如上圖),每組又有多層。一組中的多個層之間的尺度是不一樣的(也就是使用的高斯參數$\sigma$是不同的),相鄰兩層之間的尺度相差一個比例因子$k$,如果每組有$S$層,則$k=2^{\frac{1}{S}}$。上一組圖像的最低層圖象是由下一組中尺度為$2\sigma$的圖像進行因子為2的降維采樣得到的(高斯金字塔先底層建立)。高斯金字塔構建完成后,將相鄰的高斯金字塔相減就得到了DoG金字塔。
高斯金字塔的組數一般是:
$$O=[log_2min(m,n)-a]$$
$O$代表高斯金字塔的組數,$m$,$n$分別是圖像的行和列。減去的系數$a$可以在$0-log_2min(m,n)$之間的任意值,和具體需要的金字塔的頂層圖像的大小有關。
高斯模糊參數$\sigma$(尺度空間),可由下面關系得到:
$$\sigma(o,s)=\sigma_0·2^{\frac{o+s}{S}}$$
其中$o$為所在的組,$s$為所在的層,$\sigma_0$為初始的尺度,$S$為每組的層數。
在Lowe論文中$\sigma_0=1.6$,$o_{min}=-1$,$S=3$,$o_{min}=-1$就是首先將原圖像的長和寬各擴展一倍(為了保留原始圖像信息,增加特征點數量)。
從上面可以得到同一組內相鄰的圖像尺度關系:
$$\sigma_{s+1}=k·\sigma_s=2^{\frac{1}{S}}·\sigma_s$$
相鄰組處於同一層之間的圖像尺度關系:
$$\sigma_{o+1}=2\sigma_o$$
下面是高斯尺度空間表示的一個例子:
2.3 高斯金字塔構建案例
以一張$512×512$的圖像$I$為例,構建高斯金字塔步驟:(從0開始計數,倒立的金字塔)
1、金字塔的組數,$log_2{512}=9$,減去因子3,構建的金字塔組數為$O=6$,取每組的層數為$S=3$,相鄰兩個高斯尺度空間的比例因子$k=2^{\frac{1}{S}}=2^{\frac{1}{3}}$;
2、構建第0組,將圖像的寬和高都增加一倍,變成$1024×1024$($I_0$)。第0層$I_0*G(x,y,\sigma_0)$,第1層$I_0*G(x,y,k\sigma_0)$,第2層$I_0*G(x,y,k^2\sigma_0)$;
3、構建第1組,對$I_0$降維采樣變成$512×512$($I_1$)。第0層$I_1*G(x,y,2\sigma_0)$,第1層$I_1*G(x,y,2k\sigma_0)$,第2層$I_1*G(x,y,2k^2\sigma_0)$;
4、......
5、構建第$o$組,第$s$層$I_o*G(x,y,2^ok^s\sigma_0)$;
高斯金字塔構建成功后,將每一組相鄰的兩層相減就可以得到DoG金字塔。
3、DoG空間極值檢測
為了尋找DoG金字塔尺度空間的極值點,每個像素點要和其圖像域(相同大小的圖像)和尺度域(相鄰的尺度空間)的所有相鄰點進行比較,當其大於(或者小於)所有相鄰點時,該點就是極值點。如圖所示,中間的檢測點要和其所在圖像的$3×3$鄰域8個像素點,以及其相鄰的上下兩層$3×3$鄰域18個像素點,共26個像素點進行比較。
從上面的描述中可以知道,不同組的圖像大小不一樣,因此每組圖像的第一層和最后一層是無法進行比較取得極值的。為了滿足尺度變換的連續性,在每一組圖像繼續使用高斯模糊生成3幅圖像,高斯金字塔每組$S+3$層圖像,DoG金字塔的每組有$S+2$層圖像.
3.1 尺度變化的連續性
設$S=3$,也就是每組有3層,則$k=2^{\frac{1}{S}}=2^{\frac{1}{3}}$。如果按照之前的計算,也就是高斯金字塔每組有3層圖像,DoG金字塔每組有2層圖像,在DoG金字塔的第一組有兩層尺度分別是$\sigma$,$k\sigma$,$k^2\sigma$,第二組有兩層尺度分別是$2\sigma$,$2k\sigma$,由於只有兩項是無法比較取得極值的(只有左右兩邊都有值才能有極值),我們必須在高斯空間繼續添加高斯模糊項,使得DoG空間尺度形成$\sigma$,$k\sigma$,$k^2\sigma$,$k^3\sigma$,$k^4\sigma$,這樣就可以選擇中間的三項$k\sigma$,$k^2\sigma$,$k^3\sigma$。對應的下一組由上一組降維采樣得到的三項是$2k\sigma$,$2k^2\sigma$,$2k^3\sigma$,其首項$2k\sigma=2·2^{\frac{1}{3}}\sigma=2^{\frac{4}{3}}\sigma$,剛好與上一組的最后一項$k^3\sigma=2^{\frac{3}{3}}\sigma$尺度連接起來。所以需要在每一組圖像繼續使用高斯模糊生成3幅圖像,高斯金字塔每組有$S+3$層圖像,DoG金字塔的每組有$S+2$層圖像。
下面是局部極值檢測的結果:
4、刪除不好的極值點(特征點)
到現在,我們已經得到了部分關鍵點,但是現在存在幾個問題,通過比較檢測得到的DoG的局部極值點是在離散的空間搜索得到的,由於離散空間是對連續空間采樣得到的結果,因此在離散空間找到的極值點不一定是真正意義上的極值點,因此要設法將不滿足條件的點剔除掉。可以通過尺度空間DoG函數進行曲線擬合尋找極值點,這一步的本質是去掉DoG局部曲率非常不對稱的點,增強匹配穩定性、提高抗噪聲能力,以達到精確定位關鍵點的目的。
利用已知的離散空間點插值得到的連續空間極值點的方法叫做子像素插值(Sub-pixelInterpolation)。
要剔除掉的不符合要求的點主要有兩種:
- 低對比度的特征點;
- 不穩定的邊緣響應點;
4.1 剔除低對比度的特征點
假設我們在尺度為$\sigma$的尺度圖像$D(x,y)$上檢測到了一個局部極值點,空間位置為$(x,y,\sigma)$,為了簡單方便我們使用$x$表示這個候選點,其極值點偏移量用$Δx$表示,為了提高關鍵點的穩定性,需要對尺度空間DoG函數進行曲線擬合。利用DoG函數在尺度空間的泰勒展開式(擬合函數)為:
$$D(x+Δx)=D(x)+\frac{\partial{D^T}}{\partial{x}}Δx+\frac{1}{2}Δx^T\frac{\partial^2D}{\partial{x^2}}Δx$$
對$Δx$求導並讓方程等於零,可以得到極值點的偏移量為:
$$Δx=-\frac{\partial^2D^{-1}}{\partial{x^2}}\frac{\partial{D(x)}}{\partial{x}}$$
然后再把求得的$Δx$帶入到$D(x)$的泰勒展開式中:
$$D(\hat{x})=D(x)+\frac{1}{2}\frac{\partial{D^T}}{\partial{x}}Δx$$
其中$\hat{x}=x+Δx$,設對比度的閾值為$T$,對比度為$D(x)$的絕對值$|D(x)|$,若$|D(\hat{x})|≥T$,則該特征點保留,否則剔除掉。
下面是刪除$D(x)$絕對值較小后的結果:
4.2 剔除不穩定的邊緣響應點
有些極值點的位置是在圖像的邊緣位置的,因為圖像的邊緣點很難定位,同時也容易受到噪聲的干擾,我們把這些點看做是不穩定的極值點,需要進行去除。
在邊緣梯度的方向上主曲率值比較大,而沿着邊緣方向則主曲率值較小。候選特征點的DoG函數$D(x)$的主曲率與$2×2$Hessian矩陣$H$的特征值成正比:
$$H=\begin{bmatrix}D_{xx} & D_{yx} \\ D_{xy} & D_{yy} \end{bmatrix}$$
其中,$D_{xx}$,$D_{xy}$,$D_{yy}$是候選點鄰域位置差分求得的。
為了避免求具體的值,可以使用$H$特征值的比例.設$\alpha=\lambda_{max}$為$H$的最大特征值,$\beta=\lambda_{min}$為$H$的最小特征值,則:
$$T_r(H)=D_{xx}+D_{yy}=\alpha+\beta$$
$$Det(H)=D_{xx}+D_{yy}-D^2_{xy}=\alpha·\beta$$
其中,$T_r(H)$為矩陣$H$的跡,$Det(H)$為矩陣$H$的行列式.
設$\gamma=\frac{\alpha}{\beta}$表示最大特征值與最小特征值的比值,則:
$$\frac{T_r(H)^2}{Det(H)}=\frac{(\alpha+\beta)^2}{\alpha\beta}=\frac{(\gamma\beta+\beta)^2}{\gamma\beta^2}=\frac{(\gamma+1)^2}{\gamma}$$
上式的結果與兩個特征值的比例有關,和具體的大小無關,當兩個特征值相等時其值最小,並且隨着$\gamma$的增大而增大。因此為了檢測主曲率是否在某個閾值$T_r$下,只需檢測:
$$\frac{T_r(H)^2}{Det(H)}>\frac{(T_{\gamma}+1)^2}{T_{\gamma}}$$
如果上式成立,則剔除該特征點,否則保留。(Lowe論文中取$T_{\gamma}=10$)
我們知道邊緣$H$矩陣一個特征值很大,一個特征值很小,因此可以通過過濾較大的$\gamma$值達到過濾邊緣的目的。具體可參見Harris角點檢測算法第十一節、Harris角點檢測原理。
下面是移除邊緣響應后的結果:
注意:代碼實現以及具體細節可以參看文章SIFT定位算法關鍵步驟的說明。
5、求取特征點的主方向
經過上面的步驟已經找到了在不同尺度下都存在的特征點,為了實現圖像旋轉不變性,需要給特征點的方向進行賦值。利用特征點鄰域像素的梯度來確定其方向參數,再利用圖像的梯度直方圖求取關鍵點局部結構的穩定方向。
對於已經檢測到的特征點,我們知道該特征點的尺度值$\sigma$,因此根據這一尺度值,也就可以得到這一尺度的高斯圖像:
$$L(x,y)=G(x,y,\sigma)*I(x,y)$$
使用有限差分,計算以特征點為中心,$3×1.5\sigma$為半徑的區域圖像的幅角和幅值,每個點$L(x,y)$的梯度的模$m(x,y)$以及方向$\theta(x,y)$可通過下面公式得到:
$$m(x,y)=\sqrt{[L(x+1,y)-L(x-1,y)]^2+[L(x,y+1)-L(x,y-1)]^2}$$
$$\theta(x,y)=arctan\frac{L(x,y+1)-L(x,y-1)}{L(x+1,y)-L(x-1,y)}$$
計算得到梯度方向后,就要使用直方圖統計特征點鄰域內像素對應的梯度方向和幅值。梯度方向的直方圖的橫軸是梯度方向的角度(梯度方向的范圍是0到360度,直方圖每36度一個柱共10個柱,或者每45度一個柱共8個柱),縱軸是梯度方向對應梯度幅值的累加,在直方圖的峰值就是特征點的主方向。
在Lowe的論文還提到了使用高斯函數對直方圖進行平滑以增強特征點近的鄰域點對關鍵點方向的作用,並減少突變的影響。為了得到更精確的方向,通常還可以對離散的梯度直方圖進行插值擬合。具體而言,關鍵點的方向可以由和主峰值最近的三個柱值通過拋物線插值得到。在梯度直方圖中,當存在一個相當於主峰值80%能量的柱值時,則可以將這個方向認為是該特征點輔助方向。所以,一個特征點可能檢測到多個方向(也可以理解為,一個特征點可能產生多個坐標、尺度相同,但是方向不同的特征點)。Lowe在論文中指出15%的關鍵點具有多方向,而且這些點對匹配的穩定性很關鍵。
得到特征點的主方向后,對於每個特征點可以得到三個信息$(x,y,\sigma,\theta)$,即位置、尺度和方向。由此可以確定一個SIFT特征區域,一個SIFT特征區域由三個值表示,中心表示特征點位置,半徑表示關鍵點的尺度,箭頭表示主方向。具有多個方向的關鍵點可以被復制成多份,然后將方向值分別賦給復制后的特征點,一個特征點就產生了多個坐標、尺度相等,但是方向不同的特征點。
下面是特征方向和尺度分配的結果(箭頭指向代表方向,長度代表尺度):
6、生成特征描述
通過以上的步驟已經找到了SIFT特征點位置、尺度和方向信息,下面就需要使用一組向量來描述關鍵點也就是生成特征點描述子,這個描述符不只包含特征點,也含有特征點周圍對其有貢獻的像素點。描述子應具有較高的獨立性,以保證匹配率。
特征描述符的生成大致有三個步驟:
- 校正旋轉主方向,確保旋轉不變性。
- 生成描述子,最終形成一個128維的特征向量
- 歸一化處理,將特征向量長度進行歸一化處理,進一步去除光照的影響。
為了保證特征矢量的旋轉不變性,要以特征點為中心,在附近鄰域內將坐標軸旋轉$\theta$(特征點的主方向)角度,即將坐標軸旋轉為特征點的主方向。
到這里,有人會問:旋轉過程中,中圖和右圖為什么每個像素點的方向不一樣?其實,你要明確一點,你所選的小區域,是關鍵點旋轉后的小區域,右圖的區域跟旋轉前的區域不一樣了,右圖是重新選取得區域,但是區域大小沒變。
旋轉后鄰域內像素的新坐標為:
$$\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} cos\theta & –sin\theta \\ sin\theta & cos\theta \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix}$$
旋轉后以主方向為中心取$3\sigma{d}\times3\sigma{d}$(下圖中$d=4$)的窗口。下圖所示,左圖的中央為當前關鍵點的位置,每個小格代表為關鍵點鄰域所在尺度空間的一個像素,求取每個像素的梯度幅值與梯度方向,箭頭方向代表該像素的梯度方向,長度代表梯度幅值,然后利用高斯窗口對其進行加權運算。最后在每個$3\sigma\times3\sigma$的小塊上(圖像中每個正方形的區域的邊長為$4$)繪制8個方向的梯度直方圖,計算每個梯度方向的累加值,即可形成一個種子點,如右圖所示。每個特征點由4個種子點組成,每個種子點有8個方向的向量信息。這種鄰域方向性信息聯合增強了算法的抗噪聲能力,同時對於含有定位誤差的特征匹配也提供了比較理性的容錯性。
注意:由於實際情況下,編程時用到雙線性差值(前面用到了旋轉,旋轉操作會用到插值處理),因此這里用到的特征點鄰域的邊長實際為$3\sigma{(d+1)}$。因此鄰域中一共有$(3\sigma{(d+1)})^2$個像素點。
與求主方向不同,此時每個種子區域的梯度直方圖在0-360之間划分為8個方向區間,每個區間為45度,即每個種子點有8個方向的梯度強度信息。
在實際的計算過程中,為了增強匹配的穩健性,Lowe建議對每個關鍵點使用$4\times4$共16個種子點來描述,這樣一個關鍵點就可以產生128維的SIFT特征向量。
通過對特征點周圍的像素進行分塊,計算塊內梯度直方圖,生成具有獨特性的向量,這個向量是該區域圖像信息的一種抽象,具有唯一性。
上面講了那么多,我們來總結一下特征點鄰域每個像素點對於整個梯度直方圖的貢獻的計算如下所述:
- 計算各個像素點的梯度幅值和梯度幅角;
- 根據該像素點距離特征點的距離進行加權(即第一次高斯加權),該像素點的幅值乘以加權值;
- 根據該像素點在所在的小正方形區域內據中心的距離進行加權(即第二次高斯加權),把2中的結果再乘以一個權值。
經過上面的計算,就可以得到128柱的直方圖$\{p_1,p_2,...,p_{128}\}$,為了去除光照的影響,需要進行歸一化處理:
$$q_i=\frac{p_i}{\sqrt{p_1^2+p_2^2+...+p_{128}^2}},i=1,2,3,...,128$$
實際上為了去除非線性光照的變化,在實現的過程中對於已經歸一化好的描述符$\{q_1,q_2,...,q_{128}\}$需要設定一個閾值,一般閾值為0.2,當$q_i$超過0.2以后,則$q_i=0.2$。
7、總結
SIFT特征以其對旋轉、尺度縮放、亮度等保持不變性,是一種非常穩定的局部特征,在圖像處理和計算機視覺領域有着很重要的作用,其本身也是非常復雜的,下面對其計算過程做一個粗略總結。
1、DoG尺度空間的極值檢測。 首先是構造DoG尺度空間,在SIFT中使用不同參數的高斯模糊來表示不同的尺度空間。而構造尺度空間是為了檢測在不同尺度下都存在的特征點,特征點的檢測比較常用的方法$\sigma^2\nabla^2G$(高斯拉普拉斯LoG),但是LoG的運算量是比較大的,Marr和Hidreth曾指出,可以使用DoG(差分高斯)來近似計算LoG,所以在DoG的尺度空間下檢測極值點。
2、刪除不穩定的極值點。主要刪除兩類:低對比度的極值點以及不穩定的邊緣響應點。
3、確定特征點的主方向。以特征點的為中心、以$3×1.5\sigma$為半徑的領域內計算各個像素點的梯度的幅角和幅值,然后使用直方圖對梯度的幅角進行統計。直方圖的橫軸是梯度的方向,縱軸為梯度方向對應梯度幅值的累加值,直方圖中最高峰所對應的方向即為特征點的方向。
4、生成特征點的描述子。 首先將坐標軸旋轉為特征點的方向,以特征點為中心的$16\times16$窗口的像素的梯度幅值和方向,將窗口內的像素分成16塊,每塊是其像素內8個方向的直方圖統計,共可形成128維的特征向量。
三 特征點匹配
既然是匹配,當然每個特征點的特征向量(描述符)要相似才能匹配到一起,這里采用的是歐式距離來衡量其相似度。比如說我有2張圖片A和B,圖片的內容相同,只是圖片的大小尺寸不同。假設A圖片尺寸比較大,且我們已經采用SIFT算法對圖片A和B都進行了特征提取,獲得了它們的特征點集合,現在我們的問題是需要把A和B中相應的特征點給對應連線起來。
對B中的特征點x,去尋找A中最相似的點y,最簡單的方法就是拿x與A中所有點進行相似度比較,距離最小的那個為匹配點。但是如果圖片中特征點數目很多的話,這樣效率會很低。所以我們需要把A中特征點向量集合用一種數據結構來描述,這種描述要有利於x在A中的搜索,即減少時間復雜度。在sift匹配中,這種數據結構采用的是kd-tree。關於kd-tree的講解,可以參考博文k-d tree算法的研究.
# -*- coding: utf-8 -*- """ Created on Wed Aug 22 16:53:16 2018 @author: lenovo """ ''' SIFT算法 ''' import cv2 '''1、加載圖片''' img1 = cv2.imread('./image/match1.jpg') img1 = cv2.resize(img1,dsize=(600,400)) gray1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY) img2 = cv2.imread('./image/match2.jpg') img2 = cv2.resize(img2,dsize=(600,400)) gray2 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY) image1 = gray1.copy() image2 = gray2.copy() '''2、提取特征點''' #創建一個SIFT對象 sift = cv2.xfeatures2d.SIFT_create(400) #SIFT對象會使用DoG檢測關鍵點,並且對每個關鍵點周圍的區域計算特征向量。該函數返回關鍵點的信息和描述符 keypoints1,descriptor1 = sift.detectAndCompute(image1,None) keypoints2,descriptor2 = sift.detectAndCompute(image2,None) print('descriptor1:',descriptor1.shape,'descriptor2',descriptor2.shape) #在圖像上繪制關鍵點 image1 = cv2.drawKeypoints(image=image1,keypoints = keypoints1,outImage=image1,color=(255,0,255),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) image2 = cv2.drawKeypoints(image=image2,keypoints = keypoints2,outImage=image2,color=(255,0,255),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) #顯示圖像 cv2.imshow('sift_keypoints1',image1) cv2.imshow('sift_keypoints2',image2) cv2.waitKey(20) '''3、特征點匹配''' matcher = cv2.FlannBasedMatcher() matchePoints = matcher.match(descriptor1,descriptor2) print(type(matchePoints),len(matchePoints),matchePoints[0]) #提取強匹配特征點 minMatch = 1 maxMatch = 0 for i in range(len(matchePoints)): if minMatch > matchePoints[i].distance: minMatch = matchePoints[i].distance if maxMatch < matchePoints[i].distance: maxMatch = matchePoints[i].distance print('最佳匹配值是:',minMatch) print('最差匹配值是:',maxMatch) #獲取排雷在前邊的幾個最優匹配結果 goodMatchePoints = [] for i in range(len(matchePoints)): if matchePoints[i].distance < minMatch + (maxMatch-minMatch)/3: goodMatchePoints.append(matchePoints[i]) #繪制最優匹配點 outImg = None outImg = cv2.drawMatches(img1,keypoints1,img2,keypoints2,goodMatchePoints,outImg,matchColor=(0,255,0),flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT) cv2.imshow('matche',outImg) cv2.waitKey(0) cv2.destroyAllWindows()
我們選擇的這兩幅圖片亮度和對比度差異都是很大的,而且拍攝所使用的相機也是不同的,拍攝出來的同一物體尺寸也是不用的,左側是我自己用手機拍攝到的,右側是從網上下載的,可以看到匹配效果還不錯。這也說明了SIFT算法的優越性:對旋轉、尺度縮放、亮度變化等保持不變性,對視角變換、仿射變化、噪聲也保持一定程度的穩定性。但是如果我們想達到更高的匹配度,我們應該盡量選擇兩張更為相似的圖片。
參考文章:
[1]斑點檢測(LoG)
[3]基於圖像配准的圖像特征檢測之sift算法----sift算法總結
[4]SIFT算法學習總結
[7]SIFT算法詳解
[8]SIFT算法詳解
[9]Distinctive Image Features from Scale-Invariant Keypoints
[10]Distinctive Image Features from Scale-Invariant Keypoints-SIFT算法譯文
[11]SIFT特征點檢測學習一(轉載)
[14]Sift特征點匹配過程
[15]尺度空間理論
[16]SIFT定位算法關鍵步驟的說明
[17]SIFT原理及源碼解析(C++)