理論
前幾節(jié)照雁,我們看到了Harris角點檢測蚕愤。他們是跟旋轉(zhuǎn)無關的,也就是說即使圖像旋轉(zhuǎn)了,我們還是可以找到相同的角萍诱。這是因為在旋轉(zhuǎn)的圖像里角還是角悬嗓。但是如果尺度發(fā)生變化呢?一個角可能就不是角了裕坊,比如包竹,下面的圖像,當一個角在小圖的窗口里是角籍凝,可是放大以后周瞎,在同樣窗口里就是平的了。所以Harris角不是尺度不變的饵蒂。
所以在2004年声诸,D.Lowe,提出了新的算法退盯,SIFT双絮,尺度不變特征變換,分解出關鍵點得问。
在SIFT算法里主要有四步:
1.尺度空間極值檢測
從上面的圖像可以知道我們不能用同樣的窗口在不同尺度下去檢測關鍵點囤攀。對于小角是可以的,但是要檢測大角我們需要大的窗口宫纬。我們使用尺度空間過濾焚挠。用多個σ值來找LoG(高斯-拉普拉斯算子),LoG由于σ的變換來在不同大小下檢測點漓骚,σ相當于尺度參數(shù)蝌衔。比如在上面的圖像里,σ小的高斯核給小角的值高而高σ的高斯核和大角符合的好蝌蹂。所以噩斟,我們可以找到尺度和空間的局部最大值,由(x, y, σ)列表給出孤个,表示在σ尺度下的潛在關鍵點(x剃允,y)。
但是這個LoG有點成本齐鲤,所以SIFT算法使用了不一樣的高斯算子斥废,用來近似LoG。不同的高斯算子是圖像兩個不同σ的不同高斯模糊的出來的给郊,這兩個σ 一個是σ一個是kσ牡肉。這個過程是在圖像金字塔的不同級別做的∠牛看下圖:
當找到了這個DoG统锤,會在尺度和空間里找圖像的局部最大值毛俏。比如,圖像里的一個像素和他周圍的8個像素比較饲窿,同樣還有上一層和下一層的9個像素拧抖,如果它是局部最大值,它就是個潛在關鍵點免绿,這就表明這個關鍵點最好被顯示在這一尺度唧席。
至于不同的參數(shù),論文里給出了經(jīng)驗數(shù)據(jù)嘲驾,層數(shù)是4淌哟,尺度級別5,初始化σ=1.6辽故, k=√2?
2. 關鍵點本地化
當潛在關鍵點位置被找到了徒仓,需要被精化得到更準確的結(jié)果,他們使用了泰勒展開得到極值更準確的位置誊垢,如果這個極值的強度比閾值械舫凇(論文里是0.03),就被拋棄喂走,這個閾值在OpenCV里被叫做對比度閾值
DoG對于邊緣有更高的響應殃饿,所以邊緣得被去掉,這里用了一個和Harris角點檢測類似的方法芋肠。他們使用2x2的Hessian矩陣來計算主要的彎曲乎芳,我們在Harris角點檢測里知道對于邊緣,一個特征值比另一個大帖池,所以這里他們用了簡單的函數(shù)
如果這個比率比閾值大奈惑,(在OpenCV里叫做邊緣閾值,)關鍵點就被拋棄睡汹,這個值在論文里給的是10.
所以它會消除掉任何低對比度的關鍵點和邊緣關鍵點肴甸,剩下的就是強點。
3.方向分配
現(xiàn)在給每個關鍵點分配一個方向以達到圖像旋轉(zhuǎn)不變囚巴。根據(jù)尺度取關鍵點位置周圍的一個鄰居原在,在這個區(qū)域內(nèi)計算梯度和方向。創(chuàng)建一個覆蓋306度的有30個bin的方向直方圖文兢。(用梯度幅值和關鍵點的尺度乘以1.5的σ值的圓形窗口高斯權(quán)重計算出權(quán)重)晤斩,取直方圖里的最高值,任何高于它的80%的值都用來計算方向姆坚,這創(chuàng)建同樣位置和尺度而不同方向的關鍵點。
4.關鍵點描述
現(xiàn)在創(chuàng)建了關鍵點描述实愚,取一個關鍵點周圍16x16的鄰居兼呵,它被分成16個4x4的塊兔辅。對于每個小塊,創(chuàng)建8bin的方向直方圖击喂,所以一個總共128bin的值就得到了维苔。除此之外,還要對一些光線變化懂昂,旋轉(zhuǎn)等因素的考慮來達到健壯性。
5.關鍵點匹配
兩個圖像之間的關鍵點匹配是識別他們周圍的鄰居凌彬,但有些情況下,第二近的可能和第一近的特別靠近铲敛,可能是由于噪點或者別的原因?qū)е隆_@種情況下工三,取最近的和第二近的比率先鱼。如果他們的比率大于0.8,就被拒絕段审,這會消除90%的錯誤匹配,只會丟掉5%的正確匹配闹蒜。
所以這就是SIFT算法了寺枉,記住,這個算法是有專利的绷落,所以這個算法在OpenCV里是在非免費模塊里姥闪。
OpenCV里的SIFT
所以現(xiàn)在讓我們看看OpenCV里的SIFT。讓我們開始關鍵點檢測并畫出他們砌烁,首先我們有一個SIFT對象的結(jié)構(gòu)筐喳,我們可以把不同參數(shù)傳給它。
import cv2
import numpy as npimg = 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()函數(shù)找到圖像的關鍵點函喉,你可以傳一個掩圖給它避归,如果你只想在圖像的一個部分內(nèi)搜索的話。每個關鍵點是一個特殊的結(jié)構(gòu)管呵,這些結(jié)構(gòu)有很多屬性梳毙,比如(x,y)坐標,有意義的鄰居的大小捐下,指定它們方向的角度账锹,指定關鍵點力量的響應等萌业。
OpenCV也提供了cv2.drawKeyPoints()函數(shù)來在關鍵點的位置畫上小圓圈,如果你傳入一個標志位奸柬,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)
現(xiàn)在計算描述抱婉,OpenCV提供了兩個方法:
1.由于你已經(jīng)找到關鍵點了,你可以調(diào)用sift.computer()來計算我們找到的關鍵點的描述蒸绩,比如: kp, des = sift.compute(gray, kp)
2.如果你沒找關鍵點侵贵,直接用sift.detectAndCompute()一次直接找到關鍵點和描述。
sift=cv2.SIFT()
kp,des=sift.detectAndCompute(gray,None)
這里kp是關鍵點的列表漱抓,des是形狀數(shù)組