Skip to content

Commit 34dee74

Browse files
lighttxupoyea
authored andcommitted
add canny edge detection algorithm and modify sobel_filter (TheAlgorithms#991)
* add gaussian filter algorithm and lena.jpg * add img_convolve algorithm and sobel_filter * add canny edge detection algorithm and modify sobel_filter * format to avoid the backslashes
1 parent add1aef commit 34dee74

File tree

3 files changed

+122
-8
lines changed

3 files changed

+122
-8
lines changed

digital_image_processing/edge_detection/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import cv2
2+
import numpy as np
3+
from digital_image_processing.filters.convolve import img_convolve
4+
from digital_image_processing.filters.sobel_filter import sobel_filter
5+
6+
PI = 180
7+
8+
9+
def gen_gaussian_kernel(k_size, sigma):
10+
center = k_size // 2
11+
x, y = np.mgrid[0 - center:k_size - center, 0 - center:k_size - center]
12+
g = 1 / (2 * np.pi * sigma) * np.exp(-(np.square(x) + np.square(y)) / (2 * np.square(sigma)))
13+
return g
14+
15+
16+
def canny(image, threshold_low=15, threshold_high=30, weak=128, strong=255):
17+
image_row, image_col = image.shape[0], image.shape[1]
18+
# gaussian_filter
19+
gaussian_out = img_convolve(image, gen_gaussian_kernel(9, sigma=1.4))
20+
# get the gradient and degree by sobel_filter
21+
sobel_grad, sobel_theta = sobel_filter(gaussian_out)
22+
gradient_direction = np.rad2deg(sobel_theta)
23+
gradient_direction += PI
24+
25+
dst = np.zeros((image_row, image_col))
26+
27+
"""
28+
Non-maximum suppression. If the edge strength of the current pixel is the largest compared to the other pixels
29+
in the mask with the same direction, the value will be preserved. Otherwise, the value will be suppressed.
30+
"""
31+
for row in range(1, image_row - 1):
32+
for col in range(1, image_col - 1):
33+
direction = gradient_direction[row, col]
34+
35+
if (
36+
0 <= direction < 22.5
37+
or 15 * PI / 8 <= direction <= 2 * PI
38+
or 7 * PI / 8 <= direction <= 9 * PI / 8
39+
):
40+
W = sobel_grad[row, col - 1]
41+
E = sobel_grad[row, col + 1]
42+
if sobel_grad[row, col] >= W and sobel_grad[row, col] >= E:
43+
dst[row, col] = sobel_grad[row, col]
44+
45+
elif (PI / 8 <= direction < 3 * PI / 8) or (9 * PI / 8 <= direction < 11 * PI / 8):
46+
SW = sobel_grad[row + 1, col - 1]
47+
NE = sobel_grad[row - 1, col + 1]
48+
if sobel_grad[row, col] >= SW and sobel_grad[row, col] >= NE:
49+
dst[row, col] = sobel_grad[row, col]
50+
51+
elif (3 * PI / 8 <= direction < 5 * PI / 8) or (11 * PI / 8 <= direction < 13 * PI / 8):
52+
N = sobel_grad[row - 1, col]
53+
S = sobel_grad[row + 1, col]
54+
if sobel_grad[row, col] >= N and sobel_grad[row, col] >= S:
55+
dst[row, col] = sobel_grad[row, col]
56+
57+
elif (5 * PI / 8 <= direction < 7 * PI / 8) or (13 * PI / 8 <= direction < 15 * PI / 8):
58+
NW = sobel_grad[row - 1, col - 1]
59+
SE = sobel_grad[row + 1, col + 1]
60+
if sobel_grad[row, col] >= NW and sobel_grad[row, col] >= SE:
61+
dst[row, col] = sobel_grad[row, col]
62+
63+
"""
64+
High-Low threshold detection. If an edge pixel’s gradient value is higher than the high threshold
65+
value, it is marked as a strong edge pixel. If an edge pixel’s gradient value is smaller than the high
66+
threshold value and larger than the low threshold value, it is marked as a weak edge pixel. If an edge
67+
pixel's value is smaller than the low threshold value, it will be suppressed.
68+
"""
69+
if dst[row, col] >= threshold_high:
70+
dst[row, col] = strong
71+
elif dst[row, col] <= threshold_low:
72+
dst[row, col] = 0
73+
else:
74+
dst[row, col] = weak
75+
76+
"""
77+
Edge tracking. Usually a weak edge pixel caused from true edges will be connected to a strong edge pixel while
78+
noise responses are unconnected. As long as there is one strong edge pixel that is involved in its 8-connected
79+
neighborhood, that weak edge point can be identified as one that should be preserved.
80+
"""
81+
for row in range(1, image_row):
82+
for col in range(1, image_col):
83+
if dst[row, col] == weak:
84+
if 255 in (
85+
dst[row, col + 1],
86+
dst[row, col - 1],
87+
dst[row - 1, col],
88+
dst[row + 1, col],
89+
dst[row - 1, col - 1],
90+
dst[row + 1, col - 1],
91+
dst[row - 1, col + 1],
92+
dst[row + 1, col + 1],
93+
):
94+
dst[row, col] = strong
95+
else:
96+
dst[row, col] = 0
97+
98+
return dst
99+
100+
101+
if __name__ == '__main__':
102+
# read original image in gray mode
103+
lena = cv2.imread(r'../image_data/lena.jpg', 0)
104+
# canny edge detection
105+
canny_dst = canny(lena)
106+
cv2.imshow('canny', canny_dst)
107+
cv2.waitKey(0)

digital_image_processing/filters/sobel_filter.py

+15-8
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ def sobel_filter(image):
1010
kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
1111
kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
1212

13-
dst_x = img_convolve(image, kernel_x)
14-
dst_y = img_convolve(image, kernel_y)
15-
dst = np.sqrt((np.square(dst_x)) + (np.square(dst_y))).astype(np.uint8)
16-
degree = np.arctan2(dst_y, dst_x)
17-
return dst, degree
13+
dst_x = np.abs(img_convolve(image, kernel_x))
14+
dst_y = np.abs(img_convolve(image, kernel_y))
15+
# modify the pix within [0, 255]
16+
dst_x = dst_x * 255/np.max(dst_x)
17+
dst_y = dst_y * 255/np.max(dst_y)
18+
19+
dst_xy = np.sqrt((np.square(dst_x)) + (np.square(dst_y)))
20+
dst_xy = dst_xy * 255/np.max(dst_xy)
21+
dst = dst_xy.astype(np.uint8)
22+
23+
theta = np.arctan2(dst_y, dst_x)
24+
return dst, theta
1825

1926

2027
if __name__ == '__main__':
@@ -23,9 +30,9 @@ def sobel_filter(image):
2330
# turn image in gray scale value
2431
gray = cvtColor(img, COLOR_BGR2GRAY)
2532

26-
sobel, d = sobel_filter(gray)
33+
sobel_grad, sobel_theta = sobel_filter(gray)
2734

2835
# show result images
29-
imshow('sobel filter', sobel)
30-
imshow('sobel degree', d)
36+
imshow('sobel filter', sobel_grad)
37+
imshow('sobel theta', sobel_theta)
3138
waitKey(0)

0 commit comments

Comments
 (0)