【翻譯:OpenCV-Python教程】哈里斯角點檢測

⚠️由於自己的拖延症,3.4.3翻到一半,OpenCV發佈了4.0.1了正式版,所以接下來是按照4.0.1翻譯的。

⚠️除了版本之外,其他還是照舊,Harris Corner Detection,原文

目標

在本章,

理論

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

E(u,v) = \sum_{x,y} \underbrace{w(x,y)}_\text{window function} \, [\underbrace{I(x+u,y+v)}_\text{shifted intensity}-\underbrace{I(x,y)}_\text{intensity}]^2

窗口函數可以是一個矩形窗口,也可以是一個給與它覆蓋像素權重的高斯窗口。

爲了要做角點檢測,我們需要令這個函數 E(u,v) 最大。也就是說二元函數求極限的時間到了。將泰勒展開應用於上述方程,並使用一些數學步驟(請參閱任何您喜歡的標準教科書以獲得完整的推導過程),我們得到最終的方程爲:
E(u,v) \approx \begin{bmatrix} u & v \end{bmatrix} M \begin{bmatrix} u \\ v \end{bmatrix}

其中
M = \sum_{x,y} w(x,y) \begin{bmatrix}I_x I_x & I_x I_y \\ I_x I_y & I_y I_y \end{bmatrix}

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

然後進入主要的正題。做完這個步驟之後,他創建了一個得分函數,基本上就是一個等式,它將決定一個窗口是否包含一個角。
R = det(M) - k(trace(M))^2

其中

  • det(M) = \lambda_1 \lambda_2
  • trace(M) = \lambda_1 + \lambda_2
  • λ1 和 λ2 是M的特徵值(譯者注:複習一下特徵值)

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

  • 當 |R| 較小時,是因爲 λ1 和 λ2 都很小,說明這片區域是平面。
  • 當 R<0時,是因爲 λ1 >> λ2 或者反過來(其中一個遠大於另外一個),此時區域是邊。
  • 當 R較大時,是因爲 λ1 和 λ2 都較大,且 λ1∼λ2,此時這片區域應爲角。

它可以用下圖來表示:

harris_region.jpg

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

OpenCV裏的哈里斯角點檢測

OpenCV有針對它這個函數 cv.cornerHarris() ,它的參數是:

  • 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()

下面是三個結果:

harris_result.jpg

具有亞像素精度的角

有時,您可能需要找到最準確的角點。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)

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

subpixel3.png

額外資源

練習


上篇:【翻譯:OpenCV-Python教程】理解特徵

下篇:【翻譯:OpenCV-Python教程】史-托馬斯角點檢測&用於追蹤的好特徵

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章