import numpy as np
import cv2
import matplotlib.pyplot as plt
from latex import latexify
= 2)
latexify(columns %matplotlib inline
%config InlineBackend.figure_format = 'retina'
= cv2.imread('./images/B/board.jpg', 1)
image print(image.shape)
(512, 512, 3)
=(6, 6))
plt.figure(figsize
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))'off')
plt.axis( plt.show()
Harris Corner Detection
Reference: https://medium.com/jun94-devpblog/cv-10-local-feature-descriptors-harris-and-hessian-corner-detector-7d524888abfd
from scipy.ndimage import convolve
def sobel_filter(image):
= np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype = np.float32)
Gx = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]], dtype = np.float32)
Gy
= image.astype(np.float32)
img = cv2.GaussianBlur(img, (5, 5), 1.4)
img
= convolve(img, Gx, mode='constant', cval=0.0)
Ix = convolve(img, Gy, mode='constant', cval=0.0)
Iy
= np.hypot(Ix, Iy)
G = G / G.max() * 255
G = G.astype(np.uint8)
G
return np.clip(Ix, 0, 255).astype(np.uint8), np.clip(Iy, 0, 255).astype(np.uint8), G
= cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
im = sobel_filter(im)
Ix, Iy, G
= Ix.astype(np.float32)
Ix_ = Iy.astype(np.float32)
Iy_ = Ix_**2
Ixx = Iy_**2
Iyy = Ix_ * Iy_
Ixy
= plt.subplots(4, 2, figsize=(8, 10))
fig, axes 0, 0].imshow(cv2.cvtColor(im, cv2.COLOR_BGR2RGB))
axes[0, 0].axis('off')
axes[0, 0].set_title('Original Image')
axes[
0, 1].imshow(cv2.cvtColor(G, cv2.COLOR_BGR2RGB))
axes[0, 1].axis('off')
axes[0, 1].set_title(r'$\sqrt{I_x^2 + I_y^2}$')
axes[
1, 0].imshow(cv2.cvtColor(Ix, cv2.COLOR_BGR2RGB))
axes[1, 0].axis('off')
axes[1, 0].set_title(r'Sobel Filter - $I_x$')
axes[
1, 1].imshow(cv2.cvtColor(Iy, cv2.COLOR_BGR2RGB))
axes[1, 1].axis('off')
axes[1, 1].set_title(r'Sobel Filter - $I_y$')
axes[
2, 0].imshow(cv2.cvtColor(Ixx, cv2.COLOR_BGR2RGB))
axes[2, 0].axis('off')
axes[2, 0].set_title(r'Sobel Filter - $I_{xx}$')
axes[
2, 1].imshow(cv2.cvtColor(Iyy, cv2.COLOR_BGR2RGB))
axes[2, 1].axis('off')
axes[2, 1].set_title(r'Sobel Filter - $I_{yy}$')
axes[
3, 0].imshow(cv2.cvtColor(Ixy, cv2.COLOR_BGR2RGB))
axes[3, 0].axis('off')
axes[3, 0].set_title(r'Sobel Filter - $I_{xy}$')
axes[
3, 1].axis('off')
axes[
plt.tight_layout() plt.show()
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Harris Corner Detector
The Harris corner detection method is based on the structure tensor (or second-moment matrix) \(M\), which is computed from image gradients. The gradients \(I_x\) and \(I_y\) can be obtained using Sobel operators:
\[ G_x = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix}, \quad G_y = \begin{bmatrix} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{bmatrix} \]
Where: - \(G_x\) is the Sobel filter for calculating the horizontal gradient. - \(G_y\) is the Sobel filter for calculating the vertical gradient.
The image gradients \(I_x\) and \(I_y\) are computed as:
\[ I_x = G_x \ast I, \quad I_y = G_y \ast I \]
The structure tensor \(M\) is constructed using these gradients:
\[ M = \begin{bmatrix} I_x^2 & I_x I_y \\ I_x I_y & I_y^2 \end{bmatrix} \]
The Harris response function \(R\) is calculated as:
\[ R = \det(M) - k \cdot \text{trace}(M)^2 \]
Where: - \(\det(M) = I_x^2 I_y^2 - (I_x I_y)^2\) is the determinant of the matrix. - \(\text{trace}(M) = I_x^2 + I_y^2\) is the trace of the matrix. - \(k\) is a constant, typically \(0.04\).
A corner is detected if \(R\) exceeds a threshold, indicating significant changes in intensity in both gradient directions.
def detect_harris_corner(image, k=0.04, window_size=3, threshold=1e-4):
if len(image.shape) == 3:
= cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray_image else:
= image
gray_image
= sobel_filter(gray_image)
Ix, Iy, _
= Ix.astype(np.float32)
Ix = Iy.astype(np.float32)
Iy
= Ix ** 2
Ixx = Iy ** 2
Iyy = Ix * Iy
Ixy
= cv2.GaussianBlur(Ixx, (window_size, window_size), 1)
Ixx = cv2.GaussianBlur(Iyy, (window_size, window_size), 1)
Iyy = cv2.GaussianBlur(Ixy, (window_size, window_size), 1)
Ixy
# Harris response R = det(M) - k(trace(M))^2
= Ixx * Iyy - Ixy ** 2
detM = Ixx + Iyy
traceM = detM - k * traceM ** 2
harris_response
= harris_response / harris_response.max()
harris_response
= np.zeros_like(harris_response)
corners > threshold] = 255
corners[harris_response
return corners.astype(np.uint8), harris_response
def draw_corners_on_image(image, corners, radius=5, color=(0, 255, 0)):
= image.copy()
corner_img
= np.argwhere(corners == 255)
corner_points
for point in corner_points:
= point
y, x 1)
cv2.circle(corner_img, (x, y), radius, color,
return corner_img
= detect_harris_corner(image, k=0.04, window_size=3, threshold=0.01)
corners, harris_response
= draw_corners_on_image(image, corners, color = (255, 0, 0))
corners_image
= plt.subplots(1, 2, figsize=(10, 5))
fig, axes 'Harris Corner Detection')
fig.suptitle(0].imshow(corners, cmap='gray')
axes[0].axis('off')
axes[0].set_title('Corners')
axes[
1].imshow(corners_image)
axes[1].axis('off')
axes[1].set_title('Corners on Image')
axes[
plt.tight_layout() plt.show()
Hessian Corner Detector
The Hessian corner detection method uses the Hessian matrix, which is computed from second-order derivatives of the image intensity. The second-order derivatives can be obtained using the following filters for the \(x\) - and \(y\) -directions:
\[ \frac{\partial^2}{\partial x^2} = \begin{bmatrix} 1 & -2 & 1 \\ 2 & -4 & 2 \\ 1 & -2 & 1 \end{bmatrix}, \quad \frac{\partial^2}{\partial y^2} = \begin{bmatrix} 1 & 2 & 1 \\ -2 & -4 & -2 \\ 1 & 2 & 1 \end{bmatrix}, \quad \frac{\partial^2}{\partial x \partial y} = \begin{bmatrix} -1 & 0 & 1 \\ 0 & 0 & 0 \\ 1 & 0 & -1 \end{bmatrix} \]
The second-order derivatives are computed as: \[ I_{xx} = \frac{\partial^2}{\partial x^2} \ast I, \quad I_{yy} = \frac{\partial^2}{\partial y^2} \ast I, \quad I_{xy} = \frac{\partial^2}{\partial x \partial y} \ast I \]
The Hessian matrix \(H\) is constructed using these derivatives:
\[ H = \begin{bmatrix} I_{xx} & I_{xy} \\ I_{xy} & I_{yy} \end{bmatrix} \]
The response function for corner detection is the Hessian determinant:
\[ R_H = \det(H) = I_{xx} I_{yy} - I_{xy}^2 \]
Corners are detected where \(R_H\) is large, indicating significant curvature in both directions.
def sobel_second_order(image):
= np.array([[1, -2, 1], [2, -4, 2], [1, -2, 1]], dtype=np.float32)
Dxx = Dxx.T
Dyy = np.array([[1, 0, -1], [0, 0, 0], [-1, 0, 1]], dtype=np.float32)
Dxy
= image.astype(np.float32)
img = cv2.GaussianBlur(img, (5, 5), 1.4)
img
= convolve(img, Dxx, mode='constant', cval=0.0)
Ixx = convolve(img, Dyy, mode='constant', cval=0.0)
Iyy = convolve(img, Dxy, mode='constant', cval=0.0)
Ixy
return Ixx, Iyy, Ixy
def detect_hessian_corner(image, threshold=1e-4):
if len(image.shape) == 3:
= cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray_image else:
= image
gray_image
= sobel_second_order(gray_image)
Ixx, Iyy, Ixy
= Ixx.astype(np.float32)
Ixx = Iyy.astype(np.float32)
Iyy = Ixy.astype(np.float32)
Ixy
# Hessian response R = det(H)
= Ixx * Iyy - Ixy ** 2
detH
# Normalize the response for better visualization
= detH / np.max(detH)
detH
# Apply threshold to get corner points
= np.zeros_like(detH)
corners > threshold] = 255
corners[detH
return corners.astype(np.uint8), detH
= detect_hessian_corner(image, threshold=0.01)
corners, hessian_response = draw_corners_on_image(image, corners, radius=5, color=(0, 255, 0))
corner_image
= plt.subplots(1, 2, figsize=(10, 5))
fig, axes 'Hessian Corner Detection')
fig.suptitle(0].imshow(corners, cmap='gray')
axes[0].axis('off')
axes[0].set_title('Corners')
axes[
1].imshow(corners_image)
axes[1].axis('off')
axes[1].set_title('Corners on Image')
axes[
plt.tight_layout() plt.show()