import gradio as gr import cv2 import matplotlib.pyplot as plt from scipy import ndimage from scipy.ndimage.filters import convolve import numpy as np def hysteresis(img, weak = 75, strong=255): M, N = img.shape for i in range(1, M-1): for j in range(1, N-1): if (img[i,j] == weak): try: if ((img[i+1, j-1] == strong) or (img[i+1, j] == strong) or (img[i+1, j+1] == strong) or (img[i, j-1] == strong) or (img[i, j+1] == strong) or (img[i-1, j-1] == strong) or (img[i-1, j] == strong) or (img[i-1, j+1] == strong)): img[i, j] = strong else: img[i, j] = 0 except IndexError as e: pass return img def threshold(img, lowThresholdRatio=0.05, highThresholdRatio=0.09): highThreshold = img.max() * highThresholdRatio; lowThreshold = highThreshold * lowThresholdRatio; M, N = img.shape res = np.zeros((M,N), dtype=np.int32) weak = np.int32(25) strong = np.int32(255) strong_i, strong_j = np.where(img >= highThreshold) zeros_i, zeros_j = np.where(img < lowThreshold) weak_i, weak_j = np.where((img <= highThreshold) & (img >= lowThreshold)) res[strong_i, strong_j] = strong res[weak_i, weak_j] = weak return (res) def non_max_suppression(img, D): M, N = img.shape Z = np.zeros((M,N), dtype=np.int32) angle = D * 180. / np.pi angle[angle < 0] += 180 for i in range(1,M-1): for j in range(1,N-1): try: q = 255 r = 255 #angle 0 if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180): q = img[i, j+1] r = img[i, j-1] #angle 45 elif (22.5 <= angle[i,j] < 67.5): q = img[i+1, j-1] r = img[i-1, j+1] #angle 90 elif (67.5 <= angle[i,j] < 112.5): q = img[i+1, j] r = img[i-1, j] #angle 135 elif (112.5 <= angle[i,j] < 157.5): q = img[i-1, j-1] r = img[i+1, j+1] if (img[i,j] >= q) and (img[i,j] >= r): Z[i,j] = img[i,j] else: Z[i,j] = 0 except IndexError as e: pass return Z def gaussian_kernel(size, sigma=1): size = int(size) // 2 x, y = np.mgrid[-size:size+1, -size:size+1] normal = 1 / (2.0 * np.pi * sigma**2) g = np.exp(-((x**2 + y**2) / (2.0*sigma**2))) * normal return g def sobel_filters(img): Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32) Ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]], np.float32) Ix = ndimage.filters.convolve(img, Kx) Iy = ndimage.filters.convolve(img, Ky) G = np.hypot(Ix, Iy) G = G / G.max() * 255 theta = np.arctan2(Iy, Ix) return (G, theta) def canny(img, kernel, sigma): img_color = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img_gaussian = convolve(img_gray, gaussian_kernel(kernel, sigma)) G, theta = sobel_filters(img_gaussian) img_nonmax = non_max_suppression(G, theta) img_threshold = threshold(img_nonmax) img_final = hysteresis(img_threshold) return img_final interface = gr.Interface( title = "Canny Edge Detector 🤖", description = "

The Canny edge detector is an edge detection operator that uses a multi-stage algorithm to detect a wide range of edges in images.


Select an image 🖼", article='Step-by-step on GitHub notebook
~ Ivanrs', allow_flagging = "never", fn = canny, inputs = [ gr.Image(), gr.Slider(1, 9, step = 1, value=3, label = "Kernel Size"), gr.Slider(1, 20, step = 5, value=10, label = "Sigma"), ], outputs = "image" ) interface.launch(share = False)