图像边缘检测

发布时间:2024年01月05日

1. 什么是边缘检测

边缘检测是图像处理与计算机视觉中的重要技术之一。其目的是检测识别出图像中亮度变化剧烈的像素点构成的集合。图像边缘的正确检测对于分析图像中的内容、实现图像中物体的分割、定位等具有重要的作用。边缘检测大大减少了源图像的数据量,剔除了与目标不相干的信息,保留了图像重要的结构属性。

图像的边缘指的是图像中像素灰度值突然发生变化的区域,如果将图像的每一行像素和每一列像素都描述成一个关于灰度值的函数,那么图像的边缘对应在灰度值函数中是函数值突然变大的区域。函数值的变化趋势可以用函数的导数描述。当函数值突然变大时,导数也必然会变大,而函数值变化较为平缓区域,导数值也比较小,因此可以通过寻找导数值较大的区域去寻找函数中突然变化的区域,进而确定图像中的边缘位置。

2 边缘检测的常用方法及Python应用

边缘检测的方法大致可分为两类:基于搜索和基于零交叉。

基于搜索的边缘检测方法:首先计算边缘强度,通常用一阶导数表示,例如梯度模,然后,计算估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值。

基于零交叉的边缘检测方法:找到由图像得到的二阶导数的零交叉点来定位边缘,通常用拉普拉斯算子或非线性微分方程的零交叉点。

滤波作为边缘检测的预处理通常是必要的,通常采用高斯滤波。

2.1 一阶微分算子

一阶微分为基础的边缘检测,通过计算图像的梯度值来检测图像的边缘,如Roberts算子、Prewitt算子和Sobel算子等。

2.1.1 Roberts算子

Roberts算子是一种最简单的算子,它利用局部差分算子寻找边缘。采用对角线相邻两像素之差近似梯度幅值检测边缘,检测垂直边缘的效果比斜向边缘要好,定位精度高,但对噪声比较敏感,无法抑制噪声的影响。

Roberts算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差,如下的2个卷积核形成了Roberts算子,图像中的每一个点都用这2个核做卷积:

在这里插入图片描述

若对于输入图像f(x,y),使用Roberts算子后输出的目标图像为g(x,y),则

在这里插入图片描述

在Python中,Roberts算子主要是通过Numpy定义模板,再调用OpenCV的filter2D()函数实现边缘提取。该函数主要是利用内核实现对图像的卷积运算,其函数原型如下:

dst = filter2D(src, ddepth, kernel, dts, anchor,delta, borderType)

参数说明:
src:表示输入图像;
ddepth: 表示目标图像所需的深度;
kernel: 表示卷积核,一个单通道浮点型矩阵;
anchor: 表示内核的基准点,其默认值为(-1, -1),位于中心位置;
delta:表示在存储目标图像前可选的添加到像素的值,默认值为0;
borderType:表示边框模式。

实验代码如下:

 def Roberts(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Roberts算子
    kernelx = np.array([[1, 0], [0, -1]], dtype=int)
    kernely = np.array([[0, -1], [1, 0]], dtype=int)
    x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
    y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
    # 转成uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    # 显示图形
    titles = ["Original Image", "Roberts Image"]
    images = [img, Roberts]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

在这里插入图片描述

2.1.2 Prewitt算子

Prewitt是一种图像边缘检测的微分算子,其原理是利用特定区域内像素值产生的差分实现边缘检测。由于Prewitt算子采用3x3模板对区域内的像素值进行计算,而Roberts算子的模板为2x2,故Prewitt算子的边缘检测结果在水平和垂直方向均比Roberts算子更加明显。Prewitt算子适合用来识别噪声较多,灰度渐变的图像。

Prewitt算子卷积核如下:

在这里插入图片描述

实验代码如下:

 def Prewitt(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Prewitt算子
    kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
    kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
    x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
    y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
    # 转成uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    # 显示图形
    titles = ["Original Image", "Prewitt Image"]
    images = [img, Prewitt]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

在这里插入图片描述

2.1.3 Sobel算子

在边缘检测中,常用的一种模板是Sobel算子。Sobel算子有两个卷积核,一个是检测水平边缘的;另一个是检测垂直边缘的。与Prewitt算子相比,Sobel算子对于像素的位置的影响做了加权,可以降低边缘模糊程度,因此效果更好。

Sobel算子卷积核如下:

在这里插入图片描述

在opencv-python中定义了Sobel算子,其函数原型如下:

dst = Sobel(src, ddepth, dx, dy, dst,ksize, scale, delta, borderType)

参数说明:
src:表示输入图像;
dst:表示输出的边缘图,其大小和通道数与输入图像相同;
ddepth:表示目标图像所需的深度,针对不同的输入图像,输出目标图像有不同的深度;
dx:表示x方向上的差分阶数,取值1或0;
dy:表示y方向上的差分阶数,取值1或0;
ksize:表示Sobel算子的大小,其值必须是正数和奇数;
scale:表示缩放导数的比例常数,默认情况下没有伸缩系数;
delta:表示将结果存入目标图像之前,添加到结果中的可选增量值。

实验代码如下:

 def Sobel_demo(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Sobel算子
    x = cv2.Sobel(grayImage, cv2.CV_16S, 1, 0)
    y = cv2.Sobel(grayImage, cv2.CV_16S, 0, 1)
    # 转成uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    # 显示图形
    titles = ["Original Image", "Sobel Image"]
    images = [img, Sobel]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

在这里插入图片描述

2.2 二阶微分算子

二阶微分为基础的边缘检测,通过寻求二阶导数中的过零点来检测边缘,如Laplacian算子和Canny算子等。

2.2.1 Laplacian算子

Laplacian算子是n维欧几里德空间中的一个二阶微分算子,常用于图像增强和边缘提取。它通过灰度差分计算邻域内的像素,基本流程是:判断图像中心像素灰度值与它周围其他像素的灰度值,如果中心像素的灰度更高,则提升中心像素的灰度;反之降低中心像素的灰度,从而实现图像锐化操作。在算法实现过程中,Laplacian算子通过对邻域中心像素的四方向或八方向求梯度,再将梯度相加起来判断中心像素灰度与邻域内其他像素灰度的关系,最后通过梯度运算的结果对像素灰度进行调整。

在opencv-python中,Laplacian算子封装在Laplacian()函数中,其函数原型如下:

dst = Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

参数说明:
src:表示输入图像;
dst:表示输出的边缘图,其大小和通道数与输入图像相同;
ddepth:表示目标图像所需的深度;
ksize:表示用于计算二阶导数的滤波器的孔径大小,其值必须是正数和奇数,且默认值为1;
scale:表示计算拉普拉斯算子值的可选比例因子,默认值为1;
delta:表示将结果存入目标图像之前,添加到结果中的可选增量值,默认值为0;
borderType:表示边框模式。

当ksize=1时,Laplacian()函数采用3x3模板(四邻域)进行变换处理。下面的实验代码是采用ksize=3的Laplacian算子进行图像锐化处理:

def Laplacian_demo(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Laplacian算子
    Laplacian = cv2.Laplacian(grayImage, cv2.CV_16S, ksize=3)
    # 转成uint8
    Laplacian = cv2.convertScaleAbs(Laplacian)
    # 显示图形
    titles = ["Original Image", "Laplacian Image"]
    images = [img, Laplacian]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

在这里插入图片描述

2.2.2 Canny算子

Canny算子由John F. Canny在1986年提出,由于它出色的检测和容错能力,至今一直被广泛使用。Canny边缘检测具有以下特点:
(1)较低的错误率 - 只有真实存在的边缘才会被检测到。
(2)较好的边缘定位 - 检测出来的结果和图像中真实的边缘在距离上的误差很小。
(3)没有重复的检测 - 对于每一条边缘,只会返回一个与之对应的结果。

Canny算子的计算步骤大概分成以下几步:

  1. 图像灰度化
  2. 用高斯滤波去噪:目的是平滑一些纹理较弱的非边缘区域,以得到更准确的边缘。
  3. 计算梯度方向和大小:图像梯度表达的是各个像素点之间,像素值大小的变化幅度大小,变化较大,则可以认为是处于边缘位置。
  4. 非极大值抑制:在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点,即对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。
  5. 双阈值选取和滞后边界跟踪:确定哪些边界才是真正的边界。这时我们需要设置两个阈值:minVal和maxVal。当图像的灰度梯度高于maxVal时被认为是真的边界,那些低于minVal的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。

在Python Opencv接口中,提供了Canny函数,其函数原型如下:

canny = cv2.Canny(image,threshold1,threshold2)

参数说明:
image:灰度图;
threshold1:minval,较小的阈值将间断的边缘连接起来;
threshold2:maxval,较大的阈值检测图像中明显的边缘。

实验代码如下:

def Canny_demo(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 高斯滤波
    img_GaussianBlur = cv2.GaussianBlur(gray, (3,3), 0)
    # Canny算子
    Canny = cv2.Canny(img_GaussianBlur, 0, 100)
    # 显示图形
    titles = ["Original Image", "Canny Image"]
    images = [img, Canny]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:
在这里插入图片描述

3. 源码仓库地址

🌼图像处理、机器学习的常用算法汇总

文章来源:https://blog.csdn.net/crossoverpptx/article/details/128574313
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。