理論
我們知道角點(diǎn)是圖像里在各個(gè)方向變化最大的區(qū)域轰传。早起嘗試找到這些角的是Chris Harris和Mike Stephens,在他們的論文A Combined Corner and Edge Detector里昌执,所以現(xiàn)在叫Harris角點(diǎn)檢測(cè)。他把這個(gè)簡單想法用數(shù)學(xué)形式表達(dá),基本上就是對(duì)(u,v)在各個(gè)方向移動(dòng)找強(qiáng)度的變化衣摩。表達(dá)式如下:
Window function是一個(gè)矩形窗口或者高斯窗口,給其下的像素權(quán)重捂敌。
我們應(yīng)該最大化這個(gè)函數(shù)E(u, v)來檢測(cè)角點(diǎn)艾扮。這表示既琴,我們應(yīng)該放大第二個(gè)條件,應(yīng)用泰勒展開上面的等式栏渺,然后經(jīng)過數(shù)學(xué)步驟,我們可以得到最終的等式:
其中
這里锐涯,Ix 和Iy 是圖像在x和y方向分別的導(dǎo)數(shù)(可以用cv2.Sobel()很容易得到)
然后是主要部分磕诊,在這個(gè)之后,它們創(chuàng)建了一個(gè)分?jǐn)?shù)纹腌,基本上市一個(gè)等式霎终,來決定一個(gè)窗口是否能包含一個(gè)角。
其中:
·det(M) = λ1λ2
·trace(M) = λ1 + λ2
·λ1和λ2是M的特征值
所以這些特征值決定了區(qū)域是否是角升薯,邊緣或者無反差的莱褒。
·當(dāng)|R|小的時(shí)候,也就是λ1和λ2小的時(shí)候涎劈,區(qū)域是無反差的
·當(dāng)R < 0广凸, 頁就是λ1 >> λ2時(shí),區(qū)域是邊緣
·當(dāng)R很大蛛枚,是λ1和λ2很大并且λ1 ~ λ2時(shí)區(qū)域是角
可以用圖片來表示:
所以Harris角點(diǎn)檢測(cè)是一個(gè)有這些分?jǐn)?shù)的灰度圖谅海,用一個(gè)合適的閾值你就可以得到圖像的角。
OpenCV里的Harris角點(diǎn)檢測(cè)
OpenCV有一個(gè)函數(shù)cv2.cornerHarris()來做這個(gè)蹦浦,參數(shù)是:
img - 輸入圖像扭吁,應(yīng)該是灰度圖和float32類型
blockSize - 做角點(diǎn)檢測(cè)的近鄰的大小
ksize - Sobel導(dǎo)數(shù)的孔徑參數(shù)
k - Harris檢測(cè)等式里的自由參數(shù)
import cv2
import numpy as npfilename = 'chessboard.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]cv2.imshow('dst',img)
if cv2.waitKey(0) & 0xff == 27:
? ? cv2.destroyAllWindows()
有時(shí)候,你可能需要更準(zhǔn)確的找到角盲镶,OpenCV用函數(shù)cv2.cornerSubPix() 通過亞像素精度精煉角點(diǎn)檢測(cè)侥袜。下面是例子,我們需要先找到harris角溉贿,然后我們把這些角的質(zhì)心傳進(jìn)去(可能有一堆點(diǎn)在角上枫吧,我們找他們的質(zhì)心)來精煉他們。Harris角點(diǎn)用紅色像素標(biāo)記宇色,精煉的角點(diǎn)用綠色像素標(biāo)記由蘑,對(duì)這個(gè)函數(shù),我們要定義什么時(shí)候停止迭代代兵。我們也需要定義來搜索角點(diǎn)的近鄰的大小尼酿。
import cv2
import numpy as npfilename = 'chessboard2.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# find Harris corners
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)
dst = cv2.dilate(dst,None)
ret, dst = cv2.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)# find centroids
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)# define the criteria to stop and refine the corners
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.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]
cv2.imwrite('subpixel5.png',img)
下面是結(jié)果,一些重要的位置放大顯示了:
END