【MQ筆記】Harris角點檢測2:算法實現(OpenCV+自主實現)

在學習了Harris角點檢測算法之後,讓我們趁熱打鐵快點來實踐一下。在這篇博文裏,我選擇了相機標定中最經典的棋盤圖爲對象,用python語言,通過 調用OpenCV中的connerHarris()函數根據算法一步步自主計算 兩種方式實現了Harris角點的提取。

chessboard

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

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