SoFunction
Updated on 2024-11-16

python implementation of canny edge detection

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!