2019-07-10 14:41:05 +00:00
|
|
|
|
import cv2
|
|
|
|
|
import numpy as np
|
2020-07-06 07:44:19 +00:00
|
|
|
|
|
2019-07-10 14:41:05 +00:00
|
|
|
|
from digital_image_processing.filters.convolve import img_convolve
|
|
|
|
|
from digital_image_processing.filters.sobel_filter import sobel_filter
|
|
|
|
|
|
|
|
|
|
PI = 180
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def gen_gaussian_kernel(k_size, sigma):
|
|
|
|
|
center = k_size // 2
|
2019-10-05 05:14:13 +00:00
|
|
|
|
x, y = np.mgrid[0 - center : k_size - center, 0 - center : k_size - center]
|
|
|
|
|
g = (
|
|
|
|
|
1
|
|
|
|
|
/ (2 * np.pi * sigma)
|
|
|
|
|
* np.exp(-(np.square(x) + np.square(y)) / (2 * np.square(sigma)))
|
|
|
|
|
)
|
2019-07-10 14:41:05 +00:00
|
|
|
|
return g
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def canny(image, threshold_low=15, threshold_high=30, weak=128, strong=255):
|
|
|
|
|
image_row, image_col = image.shape[0], image.shape[1]
|
|
|
|
|
# gaussian_filter
|
|
|
|
|
gaussian_out = img_convolve(image, gen_gaussian_kernel(9, sigma=1.4))
|
|
|
|
|
# get the gradient and degree by sobel_filter
|
|
|
|
|
sobel_grad, sobel_theta = sobel_filter(gaussian_out)
|
|
|
|
|
gradient_direction = np.rad2deg(sobel_theta)
|
|
|
|
|
gradient_direction += PI
|
|
|
|
|
|
|
|
|
|
dst = np.zeros((image_row, image_col))
|
|
|
|
|
|
|
|
|
|
"""
|
2020-06-16 08:09:19 +00:00
|
|
|
|
Non-maximum suppression. If the edge strength of the current pixel is the largest
|
|
|
|
|
compared to the other pixels in the mask with the same direction, the value will be
|
|
|
|
|
preserved. Otherwise, the value will be suppressed.
|
2019-07-10 14:41:05 +00:00
|
|
|
|
"""
|
|
|
|
|
for row in range(1, image_row - 1):
|
|
|
|
|
for col in range(1, image_col - 1):
|
|
|
|
|
direction = gradient_direction[row, col]
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
0 <= direction < 22.5
|
2019-10-05 05:14:13 +00:00
|
|
|
|
or 15 * PI / 8 <= direction <= 2 * PI
|
|
|
|
|
or 7 * PI / 8 <= direction <= 9 * PI / 8
|
2019-07-10 14:41:05 +00:00
|
|
|
|
):
|
|
|
|
|
W = sobel_grad[row, col - 1]
|
|
|
|
|
E = sobel_grad[row, col + 1]
|
|
|
|
|
if sobel_grad[row, col] >= W and sobel_grad[row, col] >= E:
|
|
|
|
|
dst[row, col] = sobel_grad[row, col]
|
|
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
|
elif (PI / 8 <= direction < 3 * PI / 8) or (
|
|
|
|
|
9 * PI / 8 <= direction < 11 * PI / 8
|
|
|
|
|
):
|
2019-07-10 14:41:05 +00:00
|
|
|
|
SW = sobel_grad[row + 1, col - 1]
|
|
|
|
|
NE = sobel_grad[row - 1, col + 1]
|
|
|
|
|
if sobel_grad[row, col] >= SW and sobel_grad[row, col] >= NE:
|
|
|
|
|
dst[row, col] = sobel_grad[row, col]
|
|
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
|
elif (3 * PI / 8 <= direction < 5 * PI / 8) or (
|
|
|
|
|
11 * PI / 8 <= direction < 13 * PI / 8
|
|
|
|
|
):
|
2019-07-10 14:41:05 +00:00
|
|
|
|
N = sobel_grad[row - 1, col]
|
|
|
|
|
S = sobel_grad[row + 1, col]
|
|
|
|
|
if sobel_grad[row, col] >= N and sobel_grad[row, col] >= S:
|
|
|
|
|
dst[row, col] = sobel_grad[row, col]
|
|
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
|
elif (5 * PI / 8 <= direction < 7 * PI / 8) or (
|
|
|
|
|
13 * PI / 8 <= direction < 15 * PI / 8
|
|
|
|
|
):
|
2019-07-10 14:41:05 +00:00
|
|
|
|
NW = sobel_grad[row - 1, col - 1]
|
|
|
|
|
SE = sobel_grad[row + 1, col + 1]
|
|
|
|
|
if sobel_grad[row, col] >= NW and sobel_grad[row, col] >= SE:
|
|
|
|
|
dst[row, col] = sobel_grad[row, col]
|
|
|
|
|
|
|
|
|
|
"""
|
2020-06-16 08:09:19 +00:00
|
|
|
|
High-Low threshold detection. If an edge pixel’s gradient value is higher
|
|
|
|
|
than the high threshold value, it is marked as a strong edge pixel. If an
|
|
|
|
|
edge pixel’s gradient value is smaller than the high threshold value and
|
|
|
|
|
larger than the low threshold value, it is marked as a weak edge pixel. If
|
|
|
|
|
an edge pixel's value is smaller than the low threshold value, it will be
|
|
|
|
|
suppressed.
|
2019-07-10 14:41:05 +00:00
|
|
|
|
"""
|
|
|
|
|
if dst[row, col] >= threshold_high:
|
|
|
|
|
dst[row, col] = strong
|
|
|
|
|
elif dst[row, col] <= threshold_low:
|
|
|
|
|
dst[row, col] = 0
|
|
|
|
|
else:
|
|
|
|
|
dst[row, col] = weak
|
|
|
|
|
|
|
|
|
|
"""
|
2020-06-16 08:09:19 +00:00
|
|
|
|
Edge tracking. Usually a weak edge pixel caused from true edges will be connected
|
|
|
|
|
to a strong edge pixel while noise responses are unconnected. As long as there is
|
|
|
|
|
one strong edge pixel that is involved in its 8-connected neighborhood, that weak
|
|
|
|
|
edge point can be identified as one that should be preserved.
|
2019-07-10 14:41:05 +00:00
|
|
|
|
"""
|
|
|
|
|
for row in range(1, image_row):
|
|
|
|
|
for col in range(1, image_col):
|
|
|
|
|
if dst[row, col] == weak:
|
|
|
|
|
if 255 in (
|
2019-10-05 05:14:13 +00:00
|
|
|
|
dst[row, col + 1],
|
|
|
|
|
dst[row, col - 1],
|
|
|
|
|
dst[row - 1, col],
|
|
|
|
|
dst[row + 1, col],
|
|
|
|
|
dst[row - 1, col - 1],
|
|
|
|
|
dst[row + 1, col - 1],
|
|
|
|
|
dst[row - 1, col + 1],
|
|
|
|
|
dst[row + 1, col + 1],
|
2019-07-10 14:41:05 +00:00
|
|
|
|
):
|
|
|
|
|
dst[row, col] = strong
|
|
|
|
|
else:
|
|
|
|
|
dst[row, col] = 0
|
|
|
|
|
|
|
|
|
|
return dst
|
|
|
|
|
|
|
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
|
if __name__ == "__main__":
|
2019-07-10 14:41:05 +00:00
|
|
|
|
# read original image in gray mode
|
2019-10-05 05:14:13 +00:00
|
|
|
|
lena = cv2.imread(r"../image_data/lena.jpg", 0)
|
2019-07-10 14:41:05 +00:00
|
|
|
|
# canny edge detection
|
|
|
|
|
canny_dst = canny(lena)
|
2019-10-05 05:14:13 +00:00
|
|
|
|
cv2.imshow("canny", canny_dst)
|
2019-07-10 14:41:05 +00:00
|
|
|
|
cv2.waitKey(0)
|