Harris角點檢測

特徵檢測中一般都會涉及角點、邊、斑點,而Harris是一種常用的角點檢測的方法。

角點是什麼呢?下面看一幅圖來理解:
在這裏插入圖片描述

上圖中標紅的點即角點,可以很清楚的看出,物體拐角處的點就是角點(字面上的理解)。

Harris角點檢測的思想:使用一個固定滑動窗口在圖像上的任意方向進行滑動,比較窗口中的像素灰度變化程度,如果在任意方向上都有較大灰度值變化,那麼可以認爲該窗口中存在角點。

上面的基本思想可能有點抽象,下面看一組圖輔助理解:
在這裏插入圖片描述

根據上圖來看:
(1)對於左圖,滑動窗口往任意方向滑動,都不會有較大的灰度值變化,因此滑動窗口對應的區域爲平坦區域;
(2)對於中圖,滑動窗口沿着邊線移動,無灰度變化;而其他方向可以有灰度變化,因此屬於邊緣部分;
(3)對於右圖,滑動窗口無論往哪個方向移動,對會帶來較大的灰度變化,因此滑窗對應的區域有角點。

下面是Harris角點檢測的數學原理

1.灰度變化描述

當窗口發生[u,v]移動時,那麼滑動前與滑動後對應的窗口中的像素點灰度變化描述如下:

E(u,v)=x,yWw(x,y)[I(x+u,y+v)I(x,y)]2E(u,v)=\sum_{x,y\in W}w(x,y)[I(x+u,y+v)-I(x,y)]^2

其中[u,v][u,v]是窗口WW的偏移量,(x,y)(x,y)是窗口所對應的像素點位置,I(x,y)I(x,y)是對應位置的像素值,w(x,y)w(x,y)這裏一般選擇的是高斯
加權函數,即離中心點近的梯度貢獻更大,距離中心遠的點貢獻小。

通過上式可以看出,在平坦區域的灰度變化不大,E(u,v)E(u,v)很小;而在紋理豐富的區域,E(u,v)E(u,v)會比較大。

2. E(u,v)化簡

首先對原始的E(u,v)E(u,v)進行泰勒展開(省略掉無窮小項),得到:

E(u,v)=x,yWw(x,y)[I(x,y)+uIx+vIyI(x,y)]2E(u,v)=\sum_{x,y\in W}w(x,y)[I(x,y)+uI_{x}+vI_{y}-I(x,y)]^2

=x,yWw(x,y)(u2Ix2+2uvIxIy+v2Iy2)\quad\quad\quad=\sum_{x,y\in W}w(x,y)(u^2I_{x}^2+2uvI_{x}I_{y}+v^2I_{y}^2)

=x,yWw(x,y)[uv][Ix2IxIyIxIyIy2][uv]\quad\quad\quad=\sum_{x,y\in W}w(x,y)\left[\begin{matrix}u\\v\end{matrix}\right]\left[\begin{matrix}I_{x}^2&I_{x}I_{y}\\I_{x}I_{y}&I_{y}^2\end{matrix}\right]\left[\begin{matrix}u&v\end{matrix}\right]

=[uv](x,yWw(x,y)[Ix2IxIyIxIyIy2])[uv]\quad\quad\quad=\left[\begin{matrix}u\\v\end{matrix}\right]\left(\sum_{x,y\in W}w(x,y)\left[\begin{matrix}I_{x}^2&I_{x}I_{y}\\I_{x}I_{y}&I_{y}^2\end{matrix}\right]\right)\left[\begin{matrix}u&v\end{matrix}\right]

因此,E(u,v)E(u,v)可以更新爲:

E(u,v)=[uv]M[uv]\quad\quad\quad E(u,v)=\left[\begin{matrix}u\\v\end{matrix}\right]M\left[\begin{matrix}u&v\end{matrix}\right]

其中M=x,yWw(x,y)[Ix2IxIyIxIyIy2]\quad\quad\quad M=\sum_{x,y\in W}w(x,y)\left[\begin{matrix}I_{x}^2&I_{x}I_{y}\\I_{x}I_{y}&I_{y}^2\end{matrix}\right]

IxI_{x}IyI_{y}是滑動窗口內的像素點(x,y)(x,y)在x和y方向上的梯度值。

3. 關鍵的矩陣M

Harris角點檢測並沒有直接使用E(u,v)E(u,v)來確定角點。而是以x和y方向上的梯度爲座標着,對窗口內每個點的梯度進行統計分析。最終將M的特徵值轉化爲橢圓的兩個軸,更詳細的原理可以參考這篇博客: https://www.cnblogs.com/zyly/p/9508131.html

得到的結論如下圖:

在這裏插入圖片描述

對於矩陣M的特徵值λ1λ2\lambda_{1}和\lambda_{2}

(1)當λ1λ2\lambda_{1}和\lambda_{2}都較小時,對應的區域爲平坦區域;

(2)當λ1λ2\lambda_{1}或\lambda_{2}有一個較大,而另一個較小時,對應的區域存在邊緣;

(3)當λ1λ2\lambda_{1}和\lambda_{2}都較大時,對應的區域存在角點。

4. 角點響應的度量

對於每一個滑動窗口計算一個得分R,如果R大於某一個閾值,那麼可以認爲該滑動窗口對應的區域內存在角點。

R=det(M)k(trace(M))2R=det(M)-k(trace(M))^2

其中:

det(M)=λ1λ2det(M)=\lambda_{1}\lambda_{2}

trace(M)=λ1+λ2trace(M)=\lambda_{1}+\lambda_{2}

其中λ1λ2\lambda_{1}和\lambda_{2}是矩陣M的兩個特徵值,k是一個超參數,一般設置爲0.04-0.06比較好,其存在意義是調節函數的形狀。

對於R來說:

(1)當滑窗內存在角點是,R爲正數且不會太小;

(2)當滑窗內爲平坦區域,R是一個很小的值;

(3)當滑窗內存在邊緣,R值爲負。

因此可以設定一個合理的閾值,當R大於該閾值時,認爲滑窗內存在角點。

Harris角點檢測實例

下面的一個例子是檢測一副RGB圖像的角點。

(1)將RGB三通道圖像轉化爲灰度圖;

(2)利用Sobel算子計算灰度圖在x和y方向上的梯度矩陣;

(3)對得到的兩個梯度矩陣進行高斯濾波,得到新的梯度矩陣;

(4)計算梯度矩陣上每個點的R值(每個點對應之前的一個滑窗),如果R值大於一個設定的閾值,則該點是角點。

該例子的實現源代碼如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt


# Harris corner detection
def Harris_corner(img):

	## Grayscale
	def BGR2GRAY(img):
		gray = 0.2126 * img[..., 2] + 0.7152 * img[..., 1] + 0.0722 * img[..., 0]
		gray = gray.astype(np.uint8)
		return gray

	## Sobel
	def Sobel_filtering(gray):
		# get shape
		H, W = gray.shape

		# sobel kernel
		sobely = np.array(((1, 2, 1),
						(0, 0, 0),
						(-1, -2, -1)), dtype=np.float32)

		sobelx = np.array(((1, 0, -1),
						(2, 0, -2),
						(1, 0, -1)), dtype=np.float32)

		# padding
		tmp = np.pad(gray, (1, 1), 'edge')

		# prepare
		Ix = np.zeros_like(gray, dtype=np.float32)
		Iy = np.zeros_like(gray, dtype=np.float32)

		# get differential
		for y in range(H):
			for x in range(W):
				Ix[y, x] = np.mean(tmp[y : y  + 3, x : x + 3] * sobelx)
				Iy[y, x] = np.mean(tmp[y : y + 3, x : x + 3] * sobely)
			
		Ix2 = Ix ** 2
		Iy2 = Iy ** 2
		Ixy = Ix * Iy

		return Ix2, Iy2, Ixy


	# gaussian filtering
	def gaussian_filtering(I, K_size=3, sigma=3):
		# get shape
		H, W = I.shape

		## gaussian
		I_t = np.pad(I, (K_size // 2, K_size // 2), 'edge')

		# gaussian kernel
		K = np.zeros((K_size, K_size), dtype=np.float)
		for x in range(K_size):
			for y in range(K_size):
				_x = x - K_size // 2
				_y = y - K_size // 2
				K[y, x] = np.exp( -(_x ** 2 + _y ** 2) / (2 * (sigma ** 2)))
		K /= (sigma * np.sqrt(2 * np.pi))
		K /= K.sum()

		# filtering
		for y in range(H):
			for x in range(W):
				I[y,x] = np.sum(I_t[y : y + K_size, x : x + K_size] * K)
				
		return I

	# corner detect
	def corner_detect(gray, Ix2, Iy2, Ixy, k=0.04, th=0.1):
		# prepare output image
		out = np.array((gray, gray, gray))
		out = np.transpose(out, (1,2,0))

		# get R
		R = (Ix2 * Iy2 - Ixy ** 2) - k * ((Ix2 + Iy2) ** 2)

		# detect corner
		out[R >= np.max(R) * th] = [0, 0, 255]

		out = out.astype(np.uint8)

		return out

	
	# 1. grayscale
	gray = BGR2GRAY(img)

	# 2. get difference image
	Ix2, Iy2, Ixy = Sobel_filtering(gray)

	# 3. gaussian filtering
	Ix2 = gaussian_filtering(Ix2, K_size=3, sigma=3)
	Iy2 = gaussian_filtering(Iy2, K_size=3, sigma=3)
	Ixy = gaussian_filtering(Ixy, K_size=3, sigma=3)

	# 4. corner detect
	out = corner_detect(gray, Ix2, Iy2, Ixy)

	return out


# Read image
img = cv2.imread("thorino.jpg").astype(np.float32)

# Harris corner detection
out = Harris_corner(img)

cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)

代碼來自: https://github.com/gzr2017/ImageProcessing100Wen/blob/master/Question_81_90/README.md

裏面的圖片資源可在上面的鏈接中下載。

發佈了38 篇原創文章 · 獲贊 87 · 訪問量 7301
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章