1 什么是边缘?
图像的边缘时指图像局部区域亮度变化显著的部分
2 边缘检测
边缘检测,顾名思义,就是提取边缘。
边缘检测的原理是:在边缘部分,像素值会出现较大的变化,**在边缘部分求一阶导数,就会出现极值。而在一阶导数为极值的地方,二阶导数为0,基于这个原理,就可以进行边缘检测。**如下图所示:左边为图像,右边为对图像宽度方向求一阶导数导数的变化曲线图,图像的边缘位于黑白相接的地方,也是就是weight = 100,由右图可知,weight = 100是该处的一阶导数为最大值,因此该点就是边缘点。
常用的 方法有:
一阶导数:sobel、prewitt、Roberts等算子
二阶导数:Laplace、Canny算子
其中,Canny算子的效果最好,但是实现起来更麻烦。
3 常用算子的原理
边缘在一阶导数取最大值,二阶导数为0的地方,在图像处理中,将一阶导数最大值原理提取边缘的算子称为一阶微分边缘算子,将利用二阶导数为0的原理求边缘的算子称为二阶微分边缘算子。
3.1 一阶微分边缘算子
一阶微分边缘算子求边缘实际上就是求导数,那么在图像中导数该怎么计算?
设*f(x,y)*为点(x, y)对应的像素值大小,则图像在x方向和y方向的一阶微分算子应该计算为:
图像是离散的二维函数,∆x、∆y不能无限小,最小就是1像素。因此,上述公式就变成:
该式对应的算子是:
3.1.1 Prewitt 算子
利用上述的Gx, Gy来检测边缘还存在一个问题,就是噪声点会被误测为边缘。通常如果一个点是边缘点,那么它上面下面的点也为边缘点,而噪声却是一个孤立的点,因此,可以根据其上下点的梯度来判断该点是不是边缘点。相同时检测锚点及其上方下方点的梯度,只需将滤波器扩展为:
该算子可以用来检测边缘,但是它不是Prewitt 算子。真正的Prewitt 算子是:
利用 Prewitt 算子,计算出水平和垂直梯度的幅值,可以得到边缘的强度。对于某个像素点P,如果其水平和垂直梯度都较大,意味着其周围存在边缘,即捕捉到了上下文信息。反之,如果梯度较小,则说明该点周围没有明显的边缘。可以设定一阈值,大于阈值的为边缘,小于阈值的视为噪声点。
使用 Prewitt 算子进行边缘检测的代码如下:
import numpy as np
import cv2
import matplotlib.pyplot as plt
# 读取图像
image = cv2.imread('lenna.png', cv2.IMREAD_GRAYSCALE)
# 定义Prewitt算子模板
prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
prewitt_y = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])
# 使用Prewitt算子进行边缘检测
gradient_x = cv2.filter2D(image, -1, prewitt_x)
gradient_y = cv2.filter2D(image, -1, prewitt_y)
# 将x、y梯度相加得到边缘图像
edge_image = cv2.add(np.abs(gradient_x), np.abs(gradient_y))
# 显示图像
plt.subplot(2, 2, 1), plt.imshow(image, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 2), plt.imshow(gradient_x, cmap='gray')
plt.title('Prewitt X'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 3), plt.imshow(gradient_y, cmap='gray')
plt.title('Prewitt Y'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 4), plt.imshow(edge_image, cmap='gray')
plt.title('Prewitt Edge'), plt.xticks([]), plt.yticks([])
plt.show()
运行结果如下:
3.1.2 Sobel检测算子
Sobel检测算子与Prewitt算子原理一样,他是Prewitt算子的一种特殊情况。**Sobel检测算子对像素的位置进行了加权,距离边缘更远的像素会被赋予较小的权重,而距离边缘更近的像素会被赋予较大的权重,使得边缘像素的响应更强烈。**其模板为:
使用 Sobel算子进行边缘检测的代码如下:
def sobel_edge_detection(image):
# 对图像进行高斯模糊以减少噪声
blurred = cv2.GaussianBlur(image, (3, 3), 0)
# 计算x方向和y方向的梯度
gradient_x = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3)
gradient_y = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3)
# 取绝对值并转换为8位无符号整数
gradient_x = cv2.convertScaleAbs(gradient_x)
gradient_y = cv2.convertScaleAbs(gradient_y)
# 将x方向和y方向的梯度相加
gradient = cv2.addWeighted(gradient_x, 0.5, gradient_y, 0.5, 0)
# 显示图像
plt.subplot(2, 2, 1), plt.imshow(image, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 2), plt.imshow(gradient_x, cmap='gray')
plt.title('Prewitt X'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 3), plt.imshow(gradient_y, cmap='gray')
plt.title('Prewitt Y'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 4), plt.imshow(gradient, cmap='gray')
plt.title('Prewitt Edge'), plt.xticks([]), plt.yticks([])
plt.show()
'''
# 显示图像和边缘图
cv2.imshow('Original Image', image)
cv2.imshow('X Direction Gradient', gradient_x)
cv2.imshow('Y Direction Gradient', gradient_y)
cv2.imshow('Combined Gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()'''
# 读取图像
image = cv2.imread('lenna.png', cv2.IMREAD_GRAYSCALE)
# 进行边缘检测
sobel_edge_detection(image)
代码运行结果如下:
可以看出Sobel比 Prewitt 算子的效果好。
3.1.2 Roberts算子
Roberts算子的两个模板如下:
Roberts算子计算的是对角线方向的梯度,由于其没有中心点,在实际中很少用
参考:
添加链接描述