From 34dee749a725b0d47811161b0598b9f806ce88cd Mon Sep 17 00:00:00 2001 From: Shoujue Xu Date: Wed, 10 Jul 2019 22:41:05 +0800 Subject: [PATCH] add canny edge detection algorithm and modify sobel_filter (#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 --- .../edge_detection/__init__.py | 0 .../edge_detection/canny.py | 107 ++++++++++++++++++ .../filters/sobel_filter.py | 23 ++-- 3 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 digital_image_processing/edge_detection/__init__.py create mode 100644 digital_image_processing/edge_detection/canny.py diff --git a/digital_image_processing/edge_detection/__init__.py b/digital_image_processing/edge_detection/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/digital_image_processing/edge_detection/canny.py b/digital_image_processing/edge_detection/canny.py new file mode 100644 index 000000000..7fde75a90 --- /dev/null +++ b/digital_image_processing/edge_detection/canny.py @@ -0,0 +1,107 @@ +import cv2 +import numpy as np +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 + 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))) + 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)) + + """ + 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. + """ + 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 + or 15 * PI / 8 <= direction <= 2 * PI + or 7 * PI / 8 <= direction <= 9 * PI / 8 + ): + 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] + + elif (PI / 8 <= direction < 3 * PI / 8) or (9 * PI / 8 <= direction < 11 * PI / 8): + 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] + + elif (3 * PI / 8 <= direction < 5 * PI / 8) or (11 * PI / 8 <= direction < 13 * PI / 8): + 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] + + elif (5 * PI / 8 <= direction < 7 * PI / 8) or (13 * PI / 8 <= direction < 15 * PI / 8): + 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] + + """ + 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. + """ + 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 + + """ + 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. + """ + for row in range(1, image_row): + for col in range(1, image_col): + if dst[row, col] == weak: + if 255 in ( + 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], + ): + dst[row, col] = strong + else: + dst[row, col] = 0 + + return dst + + +if __name__ == '__main__': + # read original image in gray mode + lena = cv2.imread(r'../image_data/lena.jpg', 0) + # canny edge detection + canny_dst = canny(lena) + cv2.imshow('canny', canny_dst) + cv2.waitKey(0) diff --git a/digital_image_processing/filters/sobel_filter.py b/digital_image_processing/filters/sobel_filter.py index 0c797320a..f3ef407d4 100644 --- a/digital_image_processing/filters/sobel_filter.py +++ b/digital_image_processing/filters/sobel_filter.py @@ -10,11 +10,18 @@ def sobel_filter(image): kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]) - dst_x = img_convolve(image, kernel_x) - dst_y = img_convolve(image, kernel_y) - dst = np.sqrt((np.square(dst_x)) + (np.square(dst_y))).astype(np.uint8) - degree = np.arctan2(dst_y, dst_x) - return dst, degree + dst_x = np.abs(img_convolve(image, kernel_x)) + dst_y = np.abs(img_convolve(image, kernel_y)) + # modify the pix within [0, 255] + dst_x = dst_x * 255/np.max(dst_x) + dst_y = dst_y * 255/np.max(dst_y) + + dst_xy = np.sqrt((np.square(dst_x)) + (np.square(dst_y))) + dst_xy = dst_xy * 255/np.max(dst_xy) + dst = dst_xy.astype(np.uint8) + + theta = np.arctan2(dst_y, dst_x) + return dst, theta if __name__ == '__main__': @@ -23,9 +30,9 @@ if __name__ == '__main__': # turn image in gray scale value gray = cvtColor(img, COLOR_BGR2GRAY) - sobel, d = sobel_filter(gray) + sobel_grad, sobel_theta = sobel_filter(gray) # show result images - imshow('sobel filter', sobel) - imshow('sobel degree', d) + imshow('sobel filter', sobel_grad) + imshow('sobel theta', sobel_theta) waitKey(0)