第十三节、SURF特征提取算法
上一節(jié)我們已經(jīng)介紹了SIFT算法,SIFT算法對(duì)旋轉(zhuǎn)、尺度縮放、亮度變化等保持不變性,對(duì)視角變換、仿射變化、噪聲也保持一定程度的穩(wěn)定性,是一種非常優(yōu)秀的局部特征描述算法。但是其實(shí)時(shí)性相對(duì)不高。
SURF(Speeded Up Robust Features)算法改進(jìn)了特征了提取和描述方式,用一種更為高效的方式完成特征點(diǎn)的提取和描述。
一 使用快速Hessian算法和SURF來(lái)提取和檢測(cè)特征
我們先用OpenCV庫(kù)函數(shù)演示一下快速Hessian算法和SURF來(lái)提取的效果,然后再來(lái)講述一下SURF算法的原理。
SURF特征檢測(cè)算法由Herbert Lowe于2006年發(fā)表,該算法比SIFT算法快好幾倍,它吸收了SIFT算法的思想。
SURF算法采用快速Hessian算法檢測(cè)關(guān)鍵點(diǎn),而SURF算子會(huì)通過(guò)一個(gè)特征向量來(lái)描述關(guān)鍵點(diǎn)周圍區(qū)域的情況。這和SIFT算法很像,SIFT算法分別采用DoG和SIFT算子來(lái)檢測(cè)關(guān)鍵點(diǎn)和提取關(guān)鍵點(diǎn)的描述符。下面我們來(lái)演示一個(gè)例子:
# -*- coding: utf-8 -*- """ Created on Fri Aug 24 20:09:32 2018@author: lenovo """# -*- coding: utf-8 -*- """ Created on Wed Aug 22 16:53:16 2018@author: lenovo """''' SURF算法 ''' import cv2 import numpy as npimg = cv2.imread('./image/cali.bmp') img = cv2.resize(img,dsize=(600,400)) #轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #創(chuàng)建一個(gè)SURF對(duì)象 surf = cv2.xfeatures2d.SURF_create(20000) #SIFT對(duì)象會(huì)使用Hessian算法檢測(cè)關(guān)鍵點(diǎn),并且對(duì)每個(gè)關(guān)鍵點(diǎn)周圍的區(qū)域計(jì)算特征向量。該函數(shù)返回關(guān)鍵點(diǎn)的信息和描述符 keypoints,descriptor = surf.detectAndCompute(gray,None) print(type(keypoints),len(keypoints),keypoints[0]) print(descriptor.shape) #在圖像上繪制關(guān)鍵點(diǎn) img = cv2.drawKeypoints(image=img,keypoints = keypoints,outImage=img,color=(255,0,255),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) #顯示圖像 cv2.imshow('surf_keypoints',img) cv2.waitKey(0) cv2.destroyAllWindows()我們把Hessian閾值設(shè)置為20000,閾值越高,能識(shí)別的特征就越少,因此可以采用試探法來(lái)得到最優(yōu)檢測(cè)。
二 SURF算法原理
1、SURF特征檢測(cè)的步驟
2、構(gòu)建Hessian(黑塞矩陣)
構(gòu)建Hessian矩陣的目的是為了生成圖像穩(wěn)定的邊緣點(diǎn)(突變點(diǎn)),跟Canny、拉普拉斯邊緣檢測(cè)的作用類似,為特征提取做準(zhǔn)備。構(gòu)建Hessian矩陣的過(guò)程對(duì)應(yīng)著SIFT算法中的DoG過(guò)程。
黑塞矩陣(Hessian Matrix)是由一個(gè)多元函數(shù)的二階偏導(dǎo)數(shù)構(gòu)成的方陣,描述了函數(shù)的局部曲率。由德國(guó)數(shù)學(xué)家Ludwin Otto Hessian于19世紀(jì)提出。
對(duì)于一個(gè)圖像$I(x,y)$,其Hessian矩陣如下:
$$H(I(x,y))=\begin{bmatrix} \frac{\partial^2I}{\partial{x^2}} & \frac{\partial^2I}{\partial{x}\partial{y}}? \\ \frac{\partial^2I}{\partial{x}\partial{y}} &?\frac{\partial^2I}{\partial{y^2}} \end{bmatrix}$$
H矩陣的判別式是:
$$Det(H)=\frac{\partial^2I}{\partial{x^2}}*\frac{\partial^2I}{\partial{y^2}}-\frac{\partial^2I}{\partial{x}\partial{y}} * \frac{\partial^2I}{\partial{x}\partial{y}}$$
在構(gòu)建Hessian矩陣前需要對(duì)圖像進(jìn)行高斯濾波,經(jīng)過(guò)濾波后的Hessian矩陣表達(dá)式為:
$$H(x,y,\sigma)=\begin{bmatrix} L_{xx}(x,y,\sigma) & L_{xy}(x,y,\sigma) \\ L_{xy}(x,y,\sigma) & L_{yy}(x,y,\sigma) \end{bmatrix}$$
其中$(x,y)$為像素位置,$L(x,y,\sigma)=G(\sigma)*I(x,y)$,代表著圖像的高斯尺度空間,是由圖像和不同的高斯卷積得到。
我們知道在離散數(shù)學(xué)圖像中,一階導(dǎo)數(shù)是相鄰像素的灰度差:
$$L_x=L(x+1,y)-L(x,y)$$
二階導(dǎo)數(shù)是對(duì)一階導(dǎo)數(shù)的再次求導(dǎo):
$$L_{xx}=[L(x+1,y)-L(x,y)]-[L(x,y)-L(x-1,y)]$$
$$=L(x+1,y)+L(x-1,y)-2L(x,y)$$
反過(guò)來(lái)看Hessian矩陣的判別式,其實(shí)就是當(dāng)前點(diǎn)對(duì)水平方向二階偏導(dǎo)數(shù)乘以垂直方向二階偏導(dǎo)數(shù)再減去當(dāng)前水平、垂直二階偏導(dǎo)的二次方:
$$Det(H)=L_{xx}*L_{yy}-L_{xy}*L_{xy}$$
通過(guò)這種方法可以為圖像中每個(gè)像素計(jì)算出其H行列式的決定值,并用這個(gè)值來(lái)判別圖像局部特征點(diǎn)。Hession矩陣判別式中的$L(x,y)$是原始圖像的高斯卷積,由于高斯核服從正太分布,從中心點(diǎn)往外,系數(shù)越來(lái)越小,為了提高運(yùn)算速度,SURF算法使用了盒式濾波器來(lái)替代高斯濾波器$L$,所以在$L_{xy}$上乘了一個(gè)加權(quán)系數(shù)0.9,目的是為了平衡因使用盒式濾波器近似所帶來(lái)的誤差,則H矩陣判別式可表示為:
$$Det(H)=L_{xx}*L_{yy}-(0.9*L_{xy})^2$$
盒式濾波器和高斯濾波器的示意圖如下:
上面兩幅圖是$9×9$高斯濾波器模板分別在圖像垂直方向上二階導(dǎo)數(shù)$L_{yy}$和$L_{xy}$對(duì)應(yīng)的值,下邊兩幅圖是使用盒式濾波器對(duì)其近似,灰色部分的像素值為0,黑色為-2,白色為1.
那么為什么盒式濾波器可以提高運(yùn)算速度呢?這就涉及到積分圖的使用,盒式濾波器對(duì)圖像的濾波轉(zhuǎn)化成計(jì)算圖像上不同區(qū)域間像素的加減運(yùn)算問(wèn)題,這正是積分圖的強(qiáng)項(xiàng),只需要簡(jiǎn)單積分查找積分圖就可以完成。
3、構(gòu)造尺度空間
同SIFT算法一樣,SURF算法的尺度空間由$O$組$S$層組成,不同的是,SIFT算法下一組圖像的長(zhǎng)寬均是上一組的一半,同一組不同層圖像之間尺寸一樣,但是所使用的尺度空間因子(高斯模糊系數(shù)$\sigma$)逐漸增大;而在SURF算法中,不同組間圖像的尺寸都是一致的,不同的是不同組間使用的盒式濾波器的模板尺寸逐漸增大,同一組不同層圖像使用相同尺寸的濾波器,但是濾波器的尺度空間因子逐漸增大。如下圖所示:
4、特征點(diǎn)過(guò)濾并進(jìn)行精確定位
SURF特征點(diǎn)的定位過(guò)程和SIFT算法一致,將經(jīng)過(guò)Hessian矩陣處理的每個(gè)像素點(diǎn)(即獲得每個(gè)像素點(diǎn)Hessian矩陣的判別式值)與其圖像域(相同大小的圖像)和尺度域(相鄰的尺度空間)的所有相鄰點(diǎn)進(jìn)行比較,當(dāng)其大于(或者小于)所有相鄰點(diǎn)時(shí),該點(diǎn)就是極值點(diǎn)。如圖所示,中間的檢測(cè)點(diǎn)要和其所在圖像的$3×3$鄰域8個(gè)像素點(diǎn),以及其相鄰的上下兩層$3×3$鄰域18個(gè)像素點(diǎn),共26個(gè)像素點(diǎn)進(jìn)行比較。
初步定位出特征點(diǎn)后,再經(jīng)過(guò)濾除能量比較弱的關(guān)鍵點(diǎn)以及錯(cuò)誤定位的關(guān)鍵點(diǎn),篩選出最終的穩(wěn)定的特征點(diǎn)。
?
5、計(jì)算特征點(diǎn)主方向
SIFT算法特征點(diǎn)的主方向是采用在特征點(diǎn)鄰域內(nèi)統(tǒng)計(jì)其梯度直方圖,橫軸是梯度方向的角度,縱軸是梯度方向?qū)?yīng)梯度幅值的累加,取直方圖bin最大的以及超過(guò)最大80%的那些方向作為特征點(diǎn)的主方向。
而在SURF算法中,采用的是統(tǒng)計(jì)特征點(diǎn)圓形鄰域內(nèi)的Harr小波特征,即在特征點(diǎn)的圓形鄰域內(nèi),統(tǒng)計(jì)60度扇形內(nèi)所有點(diǎn)的水平、垂直Harr小波特征總和,然后扇形以0.2弧度大小的間隔進(jìn)行旋轉(zhuǎn)并再次統(tǒng)計(jì)該區(qū)域內(nèi)Harr小波特征值之后,最后將值最大的那個(gè)扇形的方向作為該特征點(diǎn)的主方向。該過(guò)程示意圖如下:
Harr特征的具體內(nèi)容可以參考第九節(jié)、人臉檢測(cè)之Haar分類器。
6、生成特征描述
在SIFT算法中,為了保證特征矢量的旋轉(zhuǎn)不變性,先以特征點(diǎn)為中心,在附近鄰域內(nèi)將坐標(biāo)軸旋轉(zhuǎn)$\theta$(特征點(diǎn)的主方向)角度,然后提取特征點(diǎn)周圍$4×4$個(gè)區(qū)域塊,統(tǒng)計(jì)每小塊內(nèi)8個(gè)梯度方向,這樣一個(gè)關(guān)鍵點(diǎn)就可以產(chǎn)生128維的SIFT特征向量。
SURF算法中,也是提取特征點(diǎn)周圍$4×4$個(gè)矩形區(qū)域塊,但是所取得矩形區(qū)域方向是沿著特征點(diǎn)的主方向,而不是像SIFT算法一樣,經(jīng)過(guò)旋轉(zhuǎn)$\theta$角度。每個(gè)子區(qū)域統(tǒng)計(jì)25個(gè)像素點(diǎn)水平方向和垂直方向的Haar小波特征,這里的水平和垂直方向都是相對(duì)主方向而言的。該Harr小波特征為水平方向值之和、垂直方向值之和、水平方向值絕對(duì)值之和以及垂直方向絕對(duì)之和4個(gè)方向。該過(guò)程示意圖如下:
把這4個(gè)值作為每個(gè)子塊區(qū)域的特征向量,所以一共有$4×4×4$=64維向量作為SURF特征的描述子,比SIFT特征的描述子減少了一半。
三 特征點(diǎn)匹配
與SIFT特征點(diǎn)匹配類似,SURF也是通過(guò)計(jì)算兩個(gè)特征點(diǎn)間特征向量的歐氏距離來(lái)確定匹配度,歐式距離越短,代表兩個(gè)特征點(diǎn)的匹配度越好。不同的是SURF還加入了Hessian矩陣跡(矩陣特征值的和)的判斷,如果兩個(gè)特征點(diǎn)的矩陣跡正負(fù)號(hào)相同,代表著兩個(gè)特征點(diǎn)具有相同方向上的對(duì)比度變化,如果不同,說(shuō)明這兩個(gè)特征點(diǎn)的對(duì)比度方向是相反的,即使歐氏距離為0,也直接剔除。
# -*- coding: utf-8 -*- """ Created on Fri Aug 24 20:09:32 2018@author: lenovo """# -*- coding: utf-8 -*- """ Created on Wed Aug 22 16:53:16 2018@author: lenovo """''' SURF算法 ''' import cv2 import numpy as np'''1、加載圖片''' img1 = cv2.imread('./image/cali1.bmp',cv2.IMREAD_GRAYSCALE) img1 = cv2.resize(img1,dsize=(600,400)) img2 = cv2.imread('./image/cali2.bmp',cv2.IMREAD_GRAYSCALE) img2 = cv2.resize(img2,dsize=(600,400)) image1 = img1.copy() image2 = img2.copy()'''2、提取特征點(diǎn)''' #創(chuàng)建一個(gè)SURF對(duì)象 surf = cv2.xfeatures2d.SURF_create(25000) #SIFT對(duì)象會(huì)使用Hessian算法檢測(cè)關(guān)鍵點(diǎn),并且對(duì)每個(gè)關(guān)鍵點(diǎn)周圍的區(qū)域計(jì)算特征向量。該函數(shù)返回關(guān)鍵點(diǎn)的信息和描述符 keypoints1,descriptor1 = surf.detectAndCompute(image1,None) keypoints2,descriptor2 = surf.detectAndCompute(image2,None) print('descriptor1:',descriptor1.shape,'descriptor2',descriptor2.shape) #在圖像上繪制關(guān)鍵點(diǎn) 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('surf_keypoints1',image1) cv2.imshow('surf_keypoints2',image2) cv2.waitKey(20)'''3、特征點(diǎn)匹配''' matcher = cv2.FlannBasedMatcher() matchePoints = matcher.match(descriptor1,descriptor2) print(type(matchePoints),len(matchePoints),matchePoints[0])#提取強(qiáng)匹配特征點(diǎn) minMatch = 1 maxMatch = 0 for i in range(len(matchePoints)):if minMatch > matchePoints[i].distance:minMatch = matchePoints[i].distanceif maxMatch < matchePoints[i].distance:maxMatch = matchePoints[i].distance print('最佳匹配值是:',minMatch) print('最差匹配值是:',maxMatch) #獲取排雷在前邊的幾個(gè)最優(yōu)匹配結(jié)果 goodMatchePoints = [] for i in range(len(matchePoints)):if matchePoints[i].distance < minMatch + (maxMatch-minMatch)/16:goodMatchePoints.append(matchePoints[i])#繪制最優(yōu)匹配點(diǎn) 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()我們來(lái)看一看特征點(diǎn)匹配效果,可以看到好多點(diǎn)都匹配錯(cuò)誤,這主要與我選擇的圖片有關(guān),由于我選擇的圖片是用來(lái)做相機(jī)標(biāo)點(diǎn)的,而當(dāng)我們使用SURF算法提取特征點(diǎn),圖片上大部分特征點(diǎn)都具有相同的性質(zhì),特征向量也近似相等,因此在匹配時(shí)會(huì)出現(xiàn)很大的誤差。
下面我們更換兩張圖片,再次進(jìn)行特征點(diǎn)匹配:
我們可以看到這個(gè)匹配效果比剛才好了不少,而且我對(duì)Hessian閾值也進(jìn)行了修改,這個(gè)值需要自己不斷的調(diào)整,以達(dá)到自己的期望。但是總體上來(lái)看,我們選擇的這兩幅圖片亮度和對(duì)比度差異都是很大的,而且拍攝所使用的相機(jī)也是不同的,左側(cè)是我自己用手機(jī)拍攝到的,右側(cè)是從網(wǎng)上下載的,匹配能有這樣的效果也已經(jīng)不錯(cuò)了。但是如果我們想達(dá)到更高的匹配度,我們應(yīng)該盡量選擇兩張更為相似的圖片。下面是我稍微修改后的代碼:
# -*- coding: utf-8 -*- """ Created on Fri Aug 24 20:09:32 2018@author: lenovo """# -*- coding: utf-8 -*- """ Created on Wed Aug 22 16:53:16 2018@author: lenovo """''' SURF算法 ''' 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、提取特征點(diǎn)''' #創(chuàng)建一個(gè)SURF對(duì)象 surf = cv2.xfeatures2d.SURF_create(10000) #SIFT對(duì)象會(huì)使用Hessian算法檢測(cè)關(guān)鍵點(diǎn),并且對(duì)每個(gè)關(guān)鍵點(diǎn)周圍的區(qū)域計(jì)算特征向量。該函數(shù)返回關(guān)鍵點(diǎn)的信息和描述符 keypoints1,descriptor1 = surf.detectAndCompute(image1,None) keypoints2,descriptor2 = surf.detectAndCompute(image2,None) print('descriptor1:',descriptor1.shape,'descriptor2',descriptor2.shape) #在圖像上繪制關(guān)鍵點(diǎn) 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('surf_keypoints1',image1) cv2.imshow('surf_keypoints2',image2) cv2.waitKey(20)'''3、特征點(diǎn)匹配''' matcher = cv2.FlannBasedMatcher() matchePoints = matcher.match(descriptor1,descriptor2) print(type(matchePoints),len(matchePoints),matchePoints[0])#提取強(qiáng)匹配特征點(diǎn) minMatch = 1 maxMatch = 0 for i in range(len(matchePoints)):if minMatch > matchePoints[i].distance:minMatch = matchePoints[i].distanceif maxMatch < matchePoints[i].distance:maxMatch = matchePoints[i].distance print('最佳匹配值是:',minMatch) print('最差匹配值是:',maxMatch) #獲取排雷在前邊的幾個(gè)最優(yōu)匹配結(jié)果 goodMatchePoints = [] for i in range(len(matchePoints)):if matchePoints[i].distance < minMatch + (maxMatch-minMatch)/4:goodMatchePoints.append(matchePoints[i])#繪制最優(yōu)匹配點(diǎn) 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()參考文章:
[1]圖像識(shí)別基本算法之SURF
[2]SURF算法
[3]SURF算法與源碼分析、上
[4]SURF算法與源碼分析、下
[5]SURF原理及源碼解析(C++)
轉(zhuǎn)載于:https://www.cnblogs.com/zyly/p/9531907.html
總結(jié)
以上是生活随笔為你收集整理的第十三节、SURF特征提取算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux 中 awk命令应用
- 下一篇: tp5.0初入