在學習了Harris角點檢測算法之後,讓我們趁熱打鐵快點來實踐一下。在這篇博文裏,我選擇了相機標定中最經典的棋盤圖爲對象,用python語言,通過 調用OpenCV中的connerHarris()函數 和 根據算法一步步自主計算 兩種方式實現了Harris角點的提取。
OpenCV實現
OpenCV中定義了connerHarris() 函數,參數如下:
• img - 數據類型爲 float32 的輸入圖像。
• blockSize - 掃描時的窗口大小。
• ksize - Sobel算子使用時的值。
• k - Harris 角點檢測方程中的自由參數,取值範圍爲 [0.04,0.06]。
'''
調用cv2.cornerHarris()實現Harris角點檢測
'''
import numpy as np
import cv2 as cv
filename = 'chessboard.jpg'
img = cv.imread(filename)
#轉爲灰度圖
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 輸入圖像必須是 float32
gray = np.float32(gray)
#角點檢測
dst = cv.cornerHarris(gray,2,3,0.04)
#擴大角點標記,可有可無
dst = cv.dilate(dst,None)
#二值化harris角的檢測結果
img[dst>0.01*dst.max()]=[0,0,255]
#顯示結果
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
運行結果:
自主實現
首先,我們來回顧一下,Harris角點檢測的步驟:
實現代碼:
'''
自主實現Harris角點檢測
'''
import cv2
import numpy as np
def harris_detect(img, ksize=3,k0=0.04):
'''
params:
img:灰度圖片
ksize:Sobel算子窗口大小
k0:響應函數
return:
corner:與源圖像一樣大小,角點處像素值設置爲255
'''
k = k0 # 響應函數k
threshold = 0.01 # 設定閾值
WITH_NMS = False # 是否非極大值抑制
# 1、使用Sobel計算像素點x,y方向的梯度
h, w = img.shape[:2] #提取圖片寬、高
grad = np.zeros((h, w, 2), dtype=np.float32) #創建梯度矩陣
grad[:, :, 0] = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=3) #取x方向的梯度
grad[:, :, 1] = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=3) #取y方向的梯度
# 2、計算Ix^2,Iy^2,Ix*Iy
m = np.zeros((h, w, 3), dtype=np.float32)
m[:, :, 0] = grad[:, :, 0] ** 2
m[:, :, 1] = grad[:, :, 1] ** 2
m[:, :, 2] = grad[:, :, 0] * grad[:, :, 1]
# 3、利用高斯函數對Ix^2,Iy^2,Ix*Iy進行濾波,得到矩陣m
m[:, :, 0] = cv2.GaussianBlur(m[:, :, 0], ksize=(ksize, ksize), sigmaX=2)
m[:, :, 1] = cv2.GaussianBlur(m[:, :, 1], ksize=(ksize, ksize), sigmaX=2)
m[:, :, 2] = cv2.GaussianBlur(m[:, :, 2], ksize=(ksize, ksize), sigmaX=2)
m = [np.array([[m[i, j, 0], m[i, j, 2]], [m[i, j, 2], m[i, j, 1]]]) for i in range(h) for j in range(w)]
# 4、計算局部特徵結果矩陣M的特徵值和響應函數R(i,j)=det(M)-k(trace(M))^2 0.04<=k<=0.06
D, T = list(map(np.linalg.det, m)), list(map(np.trace, m))
R = np.array([d - k * t ** 2 for d, t in zip(D, T)])
# 5、將計算出響應函數的值R進行非極大值抑制,濾除一些不是角點的點,同時要滿足大於設定的閾值
# 獲取最大的R值
R_max = np.max(R)
# print(R_max)
# print(np.min(R))
R = R.reshape(h, w)
corner = np.zeros_like(R, dtype=np.uint8)
for i in range(h):
for j in range(w):
if WITH_NMS:
# 除了進行進行閾值檢測 還對3x3鄰域內非極大值進行抑制(導致角點很小,會看不清)
if R[i, j] > R_max * threshold and R[i, j] == np.max(
R[max(0, i - 1):min(i + 2, h - 1), max(0, j - 1):min(j + 2, w - 1)]):
corner[i, j] = 255
else:
# 只進行閾值檢測
if R[i, j] > R_max * threshold:
corner[i, j] = 255
return corner
if __name__ == '__main__':
img = cv2.imread('chessboard.jpg')
# 轉換爲灰度圖像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
dst = harris_detect(gray)
img[dst > 0.01 * dst.max()] = [0, 0, 255]
cv2.imwrite('FindCorner.jpg',img)
cv2.imshow('FindCorner', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
運行結果:
附上自己寫的另一組代碼,可以輸出角點數量和位置:
'''
自主實現Harris角點檢測
'''
import cv2
import numpy as np
img0 = cv2.imread('chessboard.jpg')
# 轉換爲灰度圖像
img = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
ksize=3 #Sobel算子窗口大小
k=0.04 #響應函數
threshold = 0.01 # 設定閾值
#WITH_NMS = False # 是否非極大值抑制
# 1、使用Sobel計算像素點x,y方向的梯度
h, w = img.shape[:2] #提取圖片寬、高
#print(h,w)
grad = np.zeros((h, w, 2), dtype=np.float32) #創建梯度矩陣
grad[:, :, 0] = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=3) #取x方向的梯度
grad[:, :, 1] = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=3) #取y方向的梯度
# 2、計算Ix^2,Iy^2,Ix*Iy
M = np.zeros((h, w, 3), dtype=np.float32)
M[:, :, 0] = grad[:, :, 0] ** 2
M[:, :, 1] = grad[:, :, 1] ** 2
M[:, :, 2] = grad[:, :, 0] * grad[:, :, 1]
# 3、利用高斯函數對Ix^2,Iy^2,Ix*Iy進行濾波
M[:, :, 0] = cv2.GaussianBlur(M[:, :, 0], ksize=(ksize, ksize), sigmaX=2)
M[:, :, 1] = cv2.GaussianBlur(M[:, :, 1], ksize=(ksize, ksize), sigmaX=2)
M[:, :, 2] = cv2.GaussianBlur(M[:, :, 2], ksize=(ksize, ksize), sigmaX=2)
# 4、計算局部特徵結果矩陣M的特徵值和響應函數R(i,j)=det(M)-k(trace(M))^2 0.04<=k<=0.06
R = np.zeros((h,w),dtype=np.float32)
for i in range(h):
for j in range(w):
m=([[M[i,j,0],M[i,j,2]],[M[i,j,2],M[i,j,1]]])
R[i,j]=np.linalg.det(m)- k*np.trace(m)**2
#print(R)
# 5、將計算出響應函數的值R進行非極大值抑制,濾除一些不是角點的點,同時要滿足大於設定的閾值
# 獲取最大的R值
R_max = np.max(R)
#print(R_max)
#print(np.min(R))
corner_list=[] #用於保存角點
corner = np.zeros_like(R, dtype=np.uint8)
for i in range(h):
for j in range(w):
if R[i, j] > R_max * threshold and R[i, j] == np.max(R[max(0, i - 1):min(i + 2, h - 1), max(0, j - 1):min(j + 2, w - 1)]):
corner[i, j] = 255
corner_list.append([i,j])
corner = cv2.dilate(corner,None)
print("共找到角點個數:",corner_list.__len__())
print("角點座標:",corner_list)
cv2.imwrite('Corner.jpg',corner)
cv2.imshow('Corner', corner)
cv2.waitKey(0)
cv2.destroyAllWindows()
結果:
參考:
https://www.cnblogs.com/zyly/p/9508131.html
https://docs.opencv.org/3.4.6/dc/d0d/tutorial_py_features_harris.html