canny edge detection principle
There are 5 parts of canny edge detection, which I will describe below.
1 Gaussian fuzzy (omitted)
2 Calculate the gradient magnitude and direction.
Optional templates: soble operators, Prewitt operators, Roberts templates, etc.; the
The soble operator is generally used, as is OpenCV, to compute dx, dy using the soble horizontal and vertical operators convolved with the input image:
Further the magnitude of the image gradient can be obtained:
To simplify the calculation, the magnitude can also be approximated as follows:
Angle for:
The following figure represents the gradient vector, azimuth, and edge direction of the center point (the edge at any point is orthogonal to the gradient vector) :
θ = θm = arctan(dy/dx) (edge direction)
α = θ + 90= arctan(dy/dx) + 90 (gradient direction)
3、According to the angle of the amplitude of the non-extreme value suppression
Highlight: it is the non-maximum suppression of the magnitude along the gradient direction, not the edge direction, which is easily confused by beginners here.
For example, in a 3*3 region, the edges can be divided into four directions: vertical, horizontal, 45°, 135°, and similarly, the gradient reversal is also four directions (orthogonal to the edge direction). Therefore, in order to perform non-extremely large values, all possible directions are quantized into 4 directions as follows:
That is, the gradient directions are respectively
α = 90
α = 45
α = 0
α = -45
Non-extremely large value suppression means comparing the magnitude of the corresponding neighborhood values within the 3*3 neighborhood along the gradient directions of the above four types:
At each point, the center x of the domain is compared to two pixels along its corresponding gradient direction, and if the center pixel is the maximum, it is kept, otherwise the center is set to 0. This suppresses non-extremely large values and retains the point with the largest local gradient to get a refined edge.
4. detecting and connecting edges with a double thresholding algorithm
1 Selection of the coefficients TH and TL in a ratio of 2:1 or 3:1. (Generally TH = 0.3 or 0.2, TL = 0.1);
2 Points less than the low threshold are discarded and assigned 0; points greater than the high threshold are immediately labeled (these are defined edge points) and assigned 1 or 255;
3 will be less than the high threshold and greater than the low threshold will be determined using the 8-connected region (i.e.: only accepted as an edge point if connected to a TH pixel, assigned 1 or 255)
python implementation
import cv2 import numpy as np m1 = ([[1, 0, -1], [2, 0, -2], [1, 0, -1]]) m2 = ([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]) from matplotlib import pyplot as plt # Step 1: Complete Gaussian smoothing filtering img = ("", 0) sobel = (img, 50, 100) ('5', 0) ("5", 640, 480) ("5", sobel) # Angular value grayscale map img = (img, (3, 3), 2) # Step 2: Complete the first-order finite-difference calculations to compute the gradient magnitude and direction at each point img1 = (, dtype="uint8") # Same size as the original theta = (, dtype="float") # Direction matrix original image size img = (img, 1, 1, 1, 1, borderType=cv2.BORDER_REPLICATE) rows, cols = for i in range(1, rows - 1): for j in range(1, cols - 1): Gy = [(m2 * img[i - 1:i + 2, j - 1:j + 2])] #Gy = ((([1, 1, 1]), (m2 * img[i - 1:i + 2, j - 1:j + 2]))).dot(([[1], [1], [1]])) Gx = [(m1 * img[i - 1:i + 2, j - 1:j + 2])] #Gx = ((([1, 1, 1]), (m1 * img[i - 1:i + 2, j - 1:j + 2]))).dot(([[1], [1], [1]])) if Gx[0] == 0: theta[i - 1, j - 1] = 90 continue else: temp = ((np.arctan2(Gy[0], Gx[0])) * 180 / )+90 if Gx[0] * Gy[0] > 0: if Gx[0] > 0: # First quadrant theta[i - 1, j - 1] = (temp) else: # Third quadrant theta[i - 1, j - 1] = ((temp) - 180) if Gx[0] * Gy[0] < 0: if Gx[0] > 0: # Fourth quadrant theta[i - 1, j - 1] = (-1) * (temp) else: # Second quadrant theta[i - 1, j - 1] = 180 - (temp) img1[i - 1, j - 1] = ((Gx[0] ** 2 + Gy[0] ** 2)) for i in range(1, rows - 2): for j in range(1, cols - 2): if (((theta[i, j] >= -22.5) and (theta[i, j] < 22.5)) or ((theta[i, j] <= -157.5) and (theta[i, j] >= -180)) or ((theta[i, j] >= 157.5) and (theta[i, j] < 180))): theta[i, j] = 0.0 elif (((theta[i, j] >= 22.5) and (theta[i, j] < 67.5)) or ((theta[i, j] <= -112.5) and (theta[i, j] >= -157.5))): theta[i, j] = -45.0 elif (((theta[i, j] >= 67.5) and (theta[i, j] < 112.5)) or ((theta[i, j] <= -67.5) and (theta[i, j] >= -112.5))): theta[i, j] = 90.0 elif (((theta[i, j] >= 112.5) and (theta[i, j] < 157.5)) or ((theta[i, j] <= -22.5) and (theta[i, j] >= -67.5))): theta[i, j] = 45.0 ''' for i in range(1, rows - 1): for j in range(1, cols - 1): Gy = [(m2 * img[i - 1:i + 2, j - 1:j + 2])] #Gy = ((([1, 1, 1]), (m2 * img[i - 1:i + 2, j - 1:j + 2]))).dot(([[1], [1], [1]])) Gx = [(m1 * img[i - 1:i + 2, j - 1:j + 2])] #Gx = ((([1, 1, 1]), (m1 * img[i - 1:i + 2, j - 1:j + 2]))).dot(([[1], [1], [1]])) if Gx[0] == 0: theta[i - 1, j - 1] = 90 continue else: temp = (np.arctan2(Gy[0], Gx[0])) * 180 / ) if Gx[0] * Gy[0] > 0: if Gx[0] > 0: # First quadrant theta[i - 1, j - 1] = (temp) else: # Third quadrant theta[i - 1, j - 1] = ((temp) - 180) if Gx[0] * Gy[0] < 0: if Gx[0] > 0: # Fourth quadrant theta[i - 1, j - 1] = (-1) * (temp) else: # Second quadrant theta[i - 1, j - 1] = 180 - (temp) img1[i - 1, j - 1] = ((Gx[0] ** 2 + Gy[0] ** 2)) for i in range(1, rows - 2): for j in range(1, cols - 2): if (((theta[i, j] >= -22.5) and (theta[i, j] < 22.5)) or ((theta[i, j] <= -157.5) and (theta[i, j] >= -180)) or ((theta[i, j] >= 157.5) and (theta[i, j] < 180))): theta[i, j] = 90.0 elif (((theta[i, j] >= 22.5) and (theta[i, j] < 67.5)) or ((theta[i, j] <= -112.5) and (theta[i, j] >= -157.5))): theta[i, j] = 45.0 elif (((theta[i, j] >= 67.5) and (theta[i, j] < 112.5)) or ((theta[i, j] <= -67.5) and (theta[i, j] >= -112.5))): theta[i, j] = 0.0 elif (((theta[i, j] >= 112.5) and (theta[i, j] < 157.5)) or ((theta[i, j] <= -22.5) and (theta[i, j] >= -67.5))): theta[i, j] = -45.0 ''' # Step 3: Perform non-maximum suppression calculations img2 = () # Non-maximally suppressed image matrix for i in range(1, [0] - 1): for j in range(1, [1] - 1): # 0 degrees j unchanged if (theta[i, j] == 0.0) and (img1[i, j] == ([img1[i, j], img1[i + 1, j], img1[i - 1, j]])): img2[i, j] = img1[i, j] if (theta[i, j] == -45.0) and img1[i, j] == ([img1[i, j], img1[i - 1, j - 1], img1[i + 1, j + 1]]): img2[i, j] = img1[i, j] if (theta[i, j] == 90.0) and img1[i, j] == ([img1[i, j], img1[i, j + 1], img1[i, j - 1]]): img2[i, j] = img1[i, j] if (theta[i, j] == 45.0) and img1[i, j] == ([img1[i, j], img1[i - 1, j + 1], img1[i + 1, j - 1]]): img2[i, j] = img1[i, j] # Step 4: Dual Threshold Detection and Edge Connections img3 = () # Define a dual threshold image # TL = 0.4*(img2) # TH = 0.5*(img2) TL = 50 TH = 100 # The key is in the choice of these two thresholds # for i in range(1, [0] - 1): for j in range(1, [1] - 1): if img2[i, j] < TL: img3[i, j] = 0 elif img2[i, j] > TH: img3[i, j] = 255 elif ((img2[i + 1, j] < TH) or (img2[i - 1, j] < TH) or (img2[i, j + 1] < TH) or (img2[i, j - 1] < TH) or (img2[i - 1, j - 1] < TH) or (img2[i - 1, j + 1] < TH) or (img2[i + 1, j + 1] < TH) or (img2[i + 1, j - 1] < TH)): img3[i, j] = 255 ('1', 0) ("1", 640, 480) ('2', 0) ("2", 640, 480) ('3', 0) ("3", 640, 480) ('4', 0) ("4", 640, 480) ("1", img) # Original image ("2", img1) # Gradient magnitude map ("3", img2) # Non-extremely suppressed grayscale maps ("4", img3) # Final rendering (0)
The results of the run are as follows
Above is the python implementation of canny edge detection details, more information about canny edge detection please pay attention to my other related articles!