1.原理
1.噪声抑制:输入图像经过高斯平滑滤波,减少噪声的影响。
2.计算梯度:在平滑后的图像上应用Sobel算子计算梯度幅值和方向。
3.非极大值抑制:在梯度方向上,比较每个像素的梯度幅值与其相邻两个像素沿着梯度方向的梯度幅值。如果当前像素的梯度幅值最大,保留该像素,否则抑制(设为0)。
4.双阈值处理:设定高阈值和低阈值,对梯度幅值进行分类。梯度幅值高于高阈值的像素被视为强边缘,低于低阈值的像素被视为弱边缘,位于两者之间的像素被视为可能的边缘。A ⾼于阈值 maxVal 所以是真正的边界点,C 虽然低于 maxVal 但⾼于 minVal 并且与 A 相连,所以也被认为是可能的边界点。⽽ B 就会被抛弃
5.边缘连接:对强边缘像素进行连接,形成连续的边缘线条。
通过跟随弱边缘像素,并且与强边缘像素连接的方式来连接边缘,也就是根据弱边缘像素的位置,并且寻找与之相邻的强边缘像素来连接边缘。
2.代码实现
import cv2
import numpy as np
# 读取图像并转换为灰度图
image = cv2.imread('./base/lena.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 1. 噪声抑制
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 2. 计算梯度
gradient_x = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3)
gradient_y = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3)
gradient_magnitude = np.sqrt(gradient_x**2 + gradient_y**2)
gradient_direction = np.arctan2(gradient_y, gradient_x)
# 3. 非极大值抑制
rows, cols = gradient_magnitude.shape #去除行和列的像素值
non_maximum_suppression = np.zeros((rows, cols), dtype=np.float32)
for i in range(1, rows - 1):
for j in range(1, cols - 1):
angle = gradient_direction[i, j] * 180 / np.pi #(将弧度转换为角度)
angle = angle % 180 #取(0-180度)
if (0 <= angle < 22.5) or (157.5 <= angle <= 180):
#0°到22.5°或者157.5°到180°之间,表示该像素点的梯度方向为水平方向
neighbor_pixels = [gradient_magnitude[i, j - 1], gradient_magnitude[i, j + 1]]
elif 22.5 <= angle < 67.5:
#梯度方向落在 22.5°到67.5°之间,表示该像素点的梯度方向为主对角线方向
neighbor_pixels = [gradient_magnitude[i - 1, j - 1], gradient_magnitude[i + 1, j + 1]]
elif 67.5 <= angle < 112.5:
#梯度方向落在 67.5°到112.5°之间,表示该像素点的梯度方向为垂直方向
neighbor_pixels = [gradient_magnitude[i - 1, j], gradient_magnitude[i + 1, j]]
else:
#梯度方向落在 112.5°到157.5°之间,表示该像素点的梯度方向为副对角线方向
neighbor_pixels = [gradient_magnitude[i - 1, j + 1], gradient_magnitude[i + 1, j - 1]]
if gradient_magnitude[i, j] >= max(neighbor_pixels):
non_maximum_suppression[i, j] = gradient_magnitude[i, j]
# 4. 双阈值处理
high_threshold = np.max(non_maximum_suppression) * 0.7 #可以自己更改
#梯度值最大值的一定比例(如0.7)
low_threshold = high_threshold * 0.3 #也可以自己更改
#低阈值设为高阈值的较低比例(如0.3)
strong_edges = (non_maximum_suppression >= high_threshold)
weak_edges = (non_maximum_suppression > low_threshold) & (non_maximum_suppression < high_threshold)
# 5. 边缘连接
edges = np.zeros_like(image)
edges[strong_edges] = [255, 0, 0] # Strong edges in blue color
#strong_edges 是一个二值图像,其中像素值为 True 表示该位置为强边缘。设置为蓝色
edges[weak_edges] = [0, 0, 255] # Weak edges in red color
#weak_edges 也是一个二值图像,其中像素值为 True 表示该位置为弱边缘,设为红色
edges_gray = cv2.cvtColor(edges, cv2.COLOR_BGR2GRAY)
# 显示结果
cv2.imshow('Input', image)
cv2.imshow('Canny Edges (Grayscale)', edges_gray)
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果: