OpenCV學習筆記-Harris角點檢測

雲net發表於2018-06-02

一、引言:關於興趣點(interest points)

在影象處理和計算機視覺領域,興趣點(interest points),或稱做關鍵點(keypoints)、特徵點(feature points)被大量用於解決物體識別,影象識別、影象匹配、視覺跟蹤、三維重建等一系列問題。我們不再觀察整幅圖,而是選擇某些特殊的點,然後對它們進行區域性有的放矢的分析。如果能檢測到足夠多的點,同時它們的區分度很高,並且可以精確定位穩定的特徵,那麼這個方法就有使用價值。

影象特徵型別可以被分為以下三種:
1、邊緣
2、角點(感興趣關鍵點)
3、斑點(Blobs)(感興趣區域)

其中,角點是個很特殊的存在。它們在影象中可以輕易定位,同時它們在人造物體中場景中,如門窗,桌子等中隨處可見。因為角點位於兩條邊緣的交點處,代表了兩個邊緣變化的方向上的點,所以它們是可以精確定位的二維特徵,甚至可以達到亞畫素的精度。且其影象梯度有很高的變化,這種變化是可以用來幫助檢測角點的。需要注意的是,角點與位於相同強度區域上的點不同,與物體輪廓上的點也不同,因為輪廓點難以在相同的其他物體上精確定位。

二、角點檢測演算法的分類

在當前的影象處理領域,角點檢測演算法可以歸納為三類:
1、基於灰度影象的角點檢測
2、基於二值影象的角點檢測
3、基於輪廓曲線的角點檢測

而基於灰度影象的角點檢測又可分為基於梯度、基於模板、基於模板梯度組合 三類方法,其中基於模板的方法主要考慮畫素領域點的灰度變化,即影象亮度的變化,將與鄰點亮度對比足夠大的點定義為角點。常見的基於模板的角點檢測演算法有Kitchen-Rosenfeld角點檢測演算法,Harris角點檢測演算法,KLT角點檢測演算法以及SUSAN角點檢測演算法。和其他角點檢測演算法相比,SUSAN角點檢測演算法具有演算法簡單、位置準確、抗噪聲能力強等特點。

三、角點的定義

‘如果某一點在任意方向的一個微小變動都會引起灰度很大的變化,那麼我們就把它稱之為角點’
關於角點的具體描述可以有幾種:

1、一階導數(即灰度的梯度)的區域性最大所對應的畫素點
2、兩條及兩條以上邊緣的點

3、影象中的梯度值和梯度方向的變化速率都很高的點
4、角點處的一階導數最大,二階導數為0,指示物體邊緣變化不連續的方向

四、Harris角點檢測原理

對於角點的檢測,Harris依據以下直觀判斷
角點:視窗移動在水平、豎直兩個方向上變化均較大的點,即Ix, Iy都比較
邊界:僅在水平、或者僅在豎直方向有較大的變化量,另一個沒有什麼變化, 即Ix和Iy中只有一個比較大
平坦地區:在水平、豎直方向的變化量比較小。即Ix,Iy都比較小
將影象視窗平移[u, v]產生灰度變化E(u, v)

                                                                    
其中,w(x, y)是視窗函式,向量[u, v]表示某個方向,以及在該方向上的位移。由上述公式可知,E(u,v)表示某個方向上影象灰度的變化。角點檢測中要是E(u, v)的值最大,就是說讓方程右側的第二項的取值最大。
,得到:
記上式最後的結果為△,則我們可以得到:

其中M是2x2矩陣可由影象的導數求得:

這裡Ix和Iy是影象在x和y方向的導數。他們根據一個用來判定視窗內是否包含角點的等式進行打分。

                                                
所以根據這些特徵我們可以判斷一個區域是否是角點,邊界或者平面:

當λ1和λ2都小時,|R|也小,這個區域就是一個平坦區域;
λ1>>λ2 或者λ1<<λ2,R小於0,這個區域是邊緣
λ1和λ2都很大是,R也很大(λ1和λ2中的最小值都大於閾值),說明這個區域是角點。
用下面的圖來表示我們的結論:

所以Harris角點檢測的結果是一個由角點分數構成的灰度影象。選取適當的閾值對結果影象進行二值化我們就檢測到影象中的角點。

五、函式及程式碼

OpenCV中的函式cv2.cornerHarris()可以用來進行角點檢測。

cornerHarris(src, blockSize, ksize, k, dst=None, borderType=None)

src:  輸入影象,單通道8位或float32
blockSize: 角點檢測中要考慮的鄰域大小
ksize:Sobel求導中使用的視窗大小
k: Harris角點檢測方程中的自由引數,取值引數為[0.04, 0.06]

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('img/blox.jpg')
# img = cv.resize(img, (int(img.shape[1]*0.2), int(img.shape[0]*0.2)))
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow('gray', gray)
gray = np.float32(gray)
#輸入影象必須是單通道, 最後一個引數的取值範圍在[0.04, 0.06]
dst = cv.cornerHarris(gray, 2, 3, 0.04)
cv.imshow('harris', dst)
#膨脹操作,讓檢測到的角點更加清晰
dst = cv.dilate(dst, None)
cv.imshow('dilate', dst)
# 閾值的最佳值
img[dst > 0.01*dst.max()] = [0, 0, 255]

cv.namedWindow('img',cv.WINDOW_AUTOSIZE)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

效果:

參考文章:
https://blog.csdn.net/qq_37059483/article/details/77836239
https://blog.csdn.net/crzy_sparrow/article/details/7391511
https://blog.csdn.net/poem_qianmo/article/details/29356187
https://blog.csdn.net/xiaowei_cqu/article/details/7805206
https://blog.csdn.net/u010103202/article/details/73250003

相關文章