I have an image that contains several straight lines at various angles (e.g. horizontal, vertical, diagonal, cross-diagonal). I have a code that can filter the lines in a desired direction since I know how to set a kernel matrix for horizontal, vertical, diagonal, and cross-diagonal directions. However, for lines at an arbitrary angle, I have no clue how to do it. See my code below.
import cv2
import numpy as np
%matplotlib inline
from matplotlib import pyplot as plt
# Define a image display function to work on Jupyter notebook
def showimage(title,myimage):
# Reverse OpenCV's BGR order to RGB for colored images (ndim>2)
if (myimage.ndim>2):
myimage = myimage[:,:,::-1]
fig, ax = plt.subplots(figsize=(6,6))
ax.imshow(myimage, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([]) # hide tick values on X and Y axes
plt.title(title)
plt.show()
# Load the image
imfile = 'multilines.jpg' # working image file
image = cv2.imread(imfile)
showimage('Original',image)
# Convert the image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply Canny edge detection
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
### Erosion:
# Create a horizontal kernel for erosion
kernel_h = np.array([[0, 0, 0],
[1, 1, 1],
[0, 0, 0]], dtype=np.uint8)
# Create a vertical kernel for erosion
kernel_v = np.array([[0, 1, 0],
[0, 1, 0],
[0, 1, 0]], dtype=np.uint8)
# Create a diagonal kernel for erosion (45 degrees)
kernel_d = np.array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]], dtype=np.uint8)
# Create a cross-diagonal kernel for erosion (45 degrees)
kernel_crossd = np.array([[0, 0, 1],
[0, 1, 0],
[1, 0, 0]], dtype=np.uint8)
kernel = kernel_h; tag = 'Horizontal'
kernel = kernel_v; tag = 'Vertical'
kernel = kernel_d; tag = 'Diagonal'
kernel = kernel_crossd; tag = 'Cross-diagonal'
# TO DO begins ----------------------
# Create a generic angle dependent kernel
theta = 90
theta = np.deg2rad(theta)
## kernel_angle = ? <- Here to uncomment and modify
## kernel = kernel_angle ; tag = 'Generic angle-based' # Uncomment once modify above
# TO DO ends ------------------------
print(f'{tag} erosion applied.')
# Apply erosion to isolate desired lines
erosion_lines = cv2.erode(edges, kernel, iterations=1)
showimage('After erosion',erosion_lines)
# Invert the colors (convert to negative to get white background)
result = cv2.bitwise_not(erosion_lines)
# Show final image
showimage('Final',result)
How can I create a generic kernel that creates filters for any arbitrarily angled line? This will also reduce the tedious task of assigning the kernel of each type of line (e.g. horizontal, vertical, diagonal, etc).
The "TO DO" part needs to get modified. Any solution?
I using this image (filename: 'multilines.jpg') :


You can apply the sobel filter manually in order get access to the (local) orientation of an edge as described on Wikipedia about Sobel filter.
The resulting array of angles can then be filtered with respect to the angle(s) of interest.