1. 程式人生 > 其它 >OpenCV-Python系列之哈里斯角檢測

OpenCV-Python系列之哈里斯角檢測

我們在上次簡述了OpenCV的特徵基礎原理,本次我們將步入實戰部分,開始進行特徵的初階檢測。

原理

在上一個教程中,我們知道了角點是影象中向任意方向發生改變時,都引起影象強烈變動的區域。Chris Harris 和 Mike Stephens 在他們1988年的論文 A Combined Corner and Edge Detector 中,做了早期的嘗試,嘗試找出這些角點。因此現在這個演算法被稱為哈里斯角點檢測。他把這個簡單的想法變成了數學的形式。它基本上求出了位移(u,v)在各個方向上的強度差,這表示如下:

視窗函式可以是一個矩形視窗,也可以是一個給與它覆蓋畫素權重的高斯視窗。

為了要做角點檢測,我們需要令這個函式 E(u,v) 最大。也就是說二元函式求極限的時間到了。將泰勒展開應用於上述方程,我們可以對下面函式進行一階展開:

那麼:

所以E(u,v)表示式可以更新為:

其中:

在此,Ix 和 Iy 分別是x 和 y方向上影象的導數。(可以被簡單用索貝爾函式算出來 cv.Sobel())。(譯者注,M其實就是梯度的協方差矩陣)

然後進入主要的正題。做完這個步驟之後,建立了一個得分函式,基本上就是一個等式,它將決定一個視窗是否包含一個角。

其中:

1、

2、

3、λ1 和 λ2 是M的特徵值

然後,這些特徵值決定了一個區域是角、邊還是平面。

a、當 |R| 較小時,是因為 λ1 和 λ2 都很小,說明這片區域是平面。

b、當 R<0時,是因為 λ1 >> λ2 或者反過來(其中一個遠大於另外一個),此時區域是邊。

c、當 R較大時,是因為 λ1 和 λ2 都較大,且 λ1∼λ2,此時這片區域應為角。

它可以用下圖來表示:

所以哈里斯角點檢測演算法的結果,是一個帶著這些得分的灰度影象,使用一個合適的閾值,就能給到我們圖中的角點。我們用一個簡單的影象來做一下實驗。

OpenCV中的哈里斯角點檢測

我們來看一下OpenCV中的函式原型:

dst=cv.cornerHarris(src,blockSize,ksize,k [,dst [,borderType]]

img - 輸入影象,它應該是灰度影象,且是 float32 型別的。

blockSize - 它是檢測拐角時所考慮的鄰域大小。

ksize - 在應用索貝爾演算法時使用的孔徑引數。

k - 方程中哈里斯檢測器的自由引數。

我們來看程式碼:

import numpy as np
import cv2 as cv
filename = 'chessboard.png'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
    cv.destroyAllWindows()

來看結果:

有時,我們可能需要找到最準確的角點。OpenCV 有一個函式 cv.cornerSubPix() ,它進一步細化檢測到的角的亞畫素精度。以下是一個示例。通常,我們還是要先找到哈里斯角。然後我們通過這些角的質心(在一個角上可能有一堆畫素,我們取它們的質心)來細化它們。哈里斯角用紅色畫素標記,而細化之後的角用綠色畫素標記。對於這個函式,我們必須定義何時停止迭代的條件。我們在指定的迭代次數之後停止它,或者達到了一定的精度,以最先發生的為準。我們還需要定義它將搜尋角落的鄰居的大小:

import numpy as np
import cv2 as cv
filename = 'chessboard2.jpg'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# find Harris corners
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
dst = cv.dilate(dst,None)
ret, dst = cv.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)
# find centroids
ret, labels, stats, centroids = cv.connectedComponentsWithStats(dst)
# define the criteria to stop and refine the corners
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv.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]
cv.imwrite('subpixel5.png',img)

以下是結果,其中一些重要的位置顯示在縮放視窗中進行視覺化:

天道酬勤 循序漸進 技壓群雄