图像分析:边缘检测中的梯度算子

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图像分析:边缘检测中的梯度算子相关的知识,希望对你有一定的参考价值。

参考技术A

边缘检测 是基于边界的图像分割方法的第一步,边缘就是两个不同的相邻区域之间 灰度值 不连续或者突变的地方。因此,检测边缘就是,检测灰度明显变化的地方。而边缘位置处灰度的明显变化是可以借助计算灰度的微分来检测的。一般使用一阶微分和二阶微分检测边缘,在边缘位置,一阶微分的幅度值会有局部极值,二阶微分的幅度值会出现过零点。本文主要介绍边缘检测中的一阶微分算子----梯度算子,包括Roberts、Prewitt和Sobel三种算子。

想要计算梯度图,就要设计模板卷积,首先要搞明白图像在计算时的坐标系,很多博文对应的模板和坐标系都不匹配,我们在后面的卷积操作中主要使用计算坐标系。

左图Cameraman所用的坐标系统,常用在图像计算中。其坐标原点在左上角,x轴是水平的,并且向右延伸;y是垂直的,并且向下延伸。 既可以代表这幅图像,也可以表示 坐标处像素的值。

右图Lena的坐标系统,常用在屏幕显示中,因为屏幕扫描是从左向右,从上向下进行的,原点在图像的左上角,纵轴标记图像的行,横轴标记图像的列。 既可以代表这个图像,也可以代表 行列交点处的图像值。

首先我们要知道的是梯度是一个向量,向量的话有方向和大小,梯度方向指向函数变化最快的方向,大小就是它的模,也是最大的变化率。对于二元函数 ,它在点 的梯度就是 , 或者 ,就是:

其中, ,这个梯度向量的幅度和方向角为

下图展示了一个灰度图的数学化表达,像素点 的灰度值是 ,它有八个邻域。

图像在点 的梯度为

其中

即 对应图像的水平方向, 即 对应水图像的竖直方向。

要理解梯度图的生成,就要先了解模板卷积的过程。
模板卷积是模板运算的一种方式,其步骤如下:
(1)将模板在输入图像中漫游,并将模板中心与图像中某个像素位置重合;
(2)将模板上各个系数与模板下各对应像素的灰度相乘;
(3)将所有乘积相加(为保持灰度范围,常将结果再除以模板系数之和,后面梯度算子模板和为0的话就不需要除了);
(4)将上述运算结果(模板的响应输出)赋给输出图像中对应模板中心位置的像素。

其实梯度图生成前面和模板卷积相同,不同的是要生成梯度图,还需要在模板卷积完成后计算在点 梯度的幅值,将幅值作为像素值,这样才算完。 。

下图是生成梯度图用到的的水平模板和竖直模板:

例如,如果只想生成水平方向的梯度图,那么只计算水平方向的梯度 ,则梯度图上对应点 的灰度值就是

一般是水平方向的 和竖直方向的 各用一个模板,然后结合,那么得到梯度图在点 的灰度值就是

它就是我们上面说到的梯度的幅值,是以计算以2为范数,对应欧式距离,由于涉及平方和开方运算,计算量比较大。(怎么简化计算呢??换一种近似计算方式吧!!!)
在真实的梯度图输出计算中,采用以1为范数(对应城区距离)的简单计算方式,即

另一种简单的方式是以 为范数(对应棋盘距离),即

首先了解下梯度算子的设计,一般是水平方向和竖直方向,水平方向模板转置再对折就是竖直方向。

其本质是一个对角线方向的梯度算子,对应的水平方向和竖直方向的梯度分别为
输出梯度图在 的灰度值为
优点:边缘定位较准
缺点:(1)没有描述水平和竖直方向的灰度变化,只关注了对角线方向,容易造成遗漏。(2)鲁棒性差。由于 点本身参加了梯度计算,不能有效的抑制噪声的干扰。
适用于边缘明显且噪声较少的图像。

Prewitt算子是典型的 模板,其模板中心对应要求梯度的原图像坐标 , 对应的8-邻域的像素灰度值如下表所示:

通过Prewitt算子的水平模板 卷积后,对应的水平方向梯度为

通过Prewitt算子的竖直模板 卷积后,对应的竖直方向梯度为

输出梯度图在 的灰度值为

Prewitt算子引入了类似局部平均的运算,对噪声具有平滑作用,较Roberts算子更能抑制噪声。

通过Prewitt算子的水平模板 卷积后,对应的水平方向梯度为

通过Prewitt算子的竖直模板 卷积后,对应的竖直方向梯度为

输出梯度图在 的灰度值为

Sobel算子引入了类似局部加权平均的运算,对边缘的定位比要比Prewitt算子好。

Python 调用OpenCV接口实现Sobel算子边缘检测

youcans 的 OpenCV 例程200篇150. 边缘检测梯度算子

欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中


【youcans 的 OpenCV 例程200篇】150. 边缘检测梯度算子


2. 点、线和边缘检测

本节基于图像灰度的不连续性,讨论根据灰度的突变检测边界,以此为基础进行图像分割。

  • 边缘像素是图像中灰度突变的像素,而边缘是相连边缘像素的集合。
  • 线是一条细边缘线段,其两侧的背景灰度与线段的像素灰度存在显著差异。
  • 孤立的点是一个被背景像素围绕的前景像素,或一个被前景像素围绕的背景像素。

导数可以用来检测灰度的局部突变:

  • 一阶导数通常产生粗边缘;
  • 二阶导数对精细细节(如细线、孤立点和噪声)的响应更强;
  • 二阶导数在灰度斜坡和台阶过渡处会产生双边缘响应,即二阶导数在进入和离开边缘时的符号相反;
  • 二阶导数的符号可用于确定边缘的过渡是从亮到暗还是从暗到亮。

计算图像中每个像素位置的一阶导数和二阶导数的方法是空间卷积。对一个 3*3 模板,计算模板区域内灰度值与模板系数的卷积。


2.4 边缘检测的常用梯度算子

边缘检测的基本方法通常是基于一阶导数和二阶导数的,因此需要进行图像的梯度计算。图像的梯度可以用一阶导数和二阶偏导数来求解。

以矩阵形式表达的数字图像 f,任意位置 (x,y) 的梯度 ∇ f \\nabla f f 定义为向量:
∇ f = g r a d ( f ) = [ g x g y ] = [ ∂ f / ∂ x ∂ f / ∂ x ] \\nabla f = grad(f) =\\beginbmatrix g_x \\\\ g_y \\endbmatrix =\\beginbmatrix \\partial f /\\partial x \\\\ \\partial f /\\partial x \\endbmatrix f=grad(f)=[gxgy]=[f/xf/x]
梯度算子的前向差分公式为:
g x ( x , y ) = ∂ f ( x , y ) ∂ x = f ( x + 1 , y ) − f ( x , y ) g y ( x , y ) = ∂ f ( x , y ) ∂ y = f ( x , y + 1 ) − f ( x , y ) g_x(x,y) = \\dfrac\\partial f(x,y)\\partial x = f(x+1,y) - f(x,y) \\\\ g_y(x,y) = \\dfrac\\partial f(x,y)\\partial y = f(x,y+1) - f(x,y) \\\\ gx(x,y)=xf(x,y)=f(x+1,y)f(x,y)gy(x,y)=yf(x,y)=f(x,y+1)f(x,y)
梯度向量的幅度 M 和角度 α \\alpha α 为:
M ( x , y ) = ∣ ∣ ∇ f ∣ ∣ = g x 2 + g y 2 α ( x , y ) = a r c t a n [ g y / g x ] M(x,y) = ||\\nabla f|| = \\sqrt g_x^2 + g_y^2 \\\\ \\alpha (x,y) = arctan[g_y / g_x] M(x,y)=f=gx2+gy2 α(x,y)=arctan[gy/gx]
在实际编程中,为了减少计算量,常用绝对值来近似梯度幅度:
M ( x , y ) ≈ ∣ g x ∣ + ∣ g y ∣ M(x,y) \\approx |g_x| + |g_y| M(x,y)gx+gy
根据梯度算子的定义和基本公式,可以发展多种不同的计算算法,称为梯度算子。对于图像的梯度计算,通常采用模板(卷积核)对原图像进行卷积运算来实现。

Robert 梯度算子:
简单的交叉差分算法,利用局部差分算子寻找边缘,采用对角线相邻两像素差作为梯度值检测边缘。形式简单,计算速度快,但对噪声敏感,无法抑制噪声。

Prewitt 算子:
利用两个方向模板与图像进行邻域卷积,一个方向模板检测水平边缘,另一个检测垂直边缘。能够抑制噪声,但对边缘的定位较 Roberts 算子差。

Sobel 算子:
是高斯平滑和微分求导的联合运算,抗噪声能力强。考虑了距离对权值的影响,距离越远的像素的影响越小。可以通过快速卷积实现,简单有效,应用广泛。

Isotropic Sobel 算子:
权值反比于中心距,具有各向同性,沿不同方向检测边缘时梯度幅度一致。

Scharr 算子:
是 Sobel 算子在 ksize=3 时的优化,在平滑部分中心元素占的权重更大,相当于使用更瘦高的平滑模板。与 Sobel 的速度相同,精度更高。

Lapacian 算子:
二阶微分算子,具有各向同向性,与坐标轴无关(无法检测方向)。对噪声非常敏感,可以先进行阈值处理或平滑处理。

这些基本的一阶、二阶微分算子如 Robert、Sobel、Prewitt、Laplacian 等,本质上都可以用于检测边缘,也被称为边缘检测算子。进一步地,考虑边缘和噪声的性质,可以改进边缘检测算子,如 Marr-Hildreth 算子、Canny 算子。


例程 11.4:边缘检测梯度算子

    # 11.4 边缘检测的梯度算子 (Roberts 算子, Prewitt 算子, Sobel 算子, Laplacian 算子)
    img = cv2.imread("../images/Fig1016a.tif", flags=0)  # 读取为灰度图像

    # 自定义卷积核
    # Roberts 边缘算子
    kernel_Roberts_x = np.array([[1, 0], [0, -1]])
    kernel_Roberts_y = np.array([[0, -1], [1, 0]])
    # Prewitt 边缘算子
    kernel_Prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
    kernel_Prewitt_y = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
    # Sobel 边缘算子
    kernel_Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
    kernel_Sobel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
    # Laplacian 边缘算子
    kernel_Laplacian_K1 = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
    kernel_Laplacian_K2 = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])

    # 卷积运算
    imgBlur = cv2.blur(img, (3,3))  # Blur 平滑后再做 Laplacian 变换
    imgLaplacian_K1 = cv2.filter2D(imgBlur, -1, kernel_Laplacian_K1)
    imgLaplacian_K2 = cv2.filter2D(imgBlur, -1, kernel_Laplacian_K2)
    imgRoberts_x = cv2.filter2D(img, -1, kernel_Roberts_x)
    imgRoberts_y = cv2.filter2D(img, -1, kernel_Roberts_y)
    imgRoberts = np.uint8(cv2.normalize(abs(imgRoberts_x) + abs(imgRoberts_y), None, 0, 255, cv2.NORM_MINMAX))
    imgPrewitt_x = cv2.filter2D(img, -1, kernel_Prewitt_x)
    imgPrewitt_y = cv2.filter2D(img, -1, kernel_Prewitt_y)
    imgPrewitt = np.uint8(cv2.normalize(abs(imgPrewitt_x) + abs(imgPrewitt_y), None, 0, 255, cv2.NORM_MINMAX))
    imgSobel_x = cv2.filter2D(img, -1, kernel_Sobel_x)
    imgSobel_y = cv2.filter2D(img, -1, kernel_Sobel_y)
    imgSobel = np.uint8(cv2.normalize(abs(imgSobel_x) + abs(imgSobel_y), None, 0, 255, cv2.NORM_MINMAX))

    plt.figure(figsize=(12, 8))
    plt.subplot(341), plt.title('Origin'), plt.imshow(img, cmap='gray'), plt.axis('off')
    plt.subplot(345), plt.title('Laplacian_K1'), plt.imshow(imgLaplacian_K1, cmap='gray'), plt.axis('off')
    plt.subplot(349), plt.title('Laplacian_K2'), plt.imshow(imgLaplacian_K2, cmap='gray'), plt.axis('off')
    plt.subplot(342), plt.title('Roberts'), plt.imshow(imgRoberts, cmap='gray'), plt.axis('off')
    plt.subplot(346), plt.title('Roberts_X'), plt.imshow(imgRoberts_x, cmap='gray'), plt.axis('off')
    plt.subplot(3,4,10), plt.title('Roberts_Y'), plt.imshow(imgRoberts_y, cmap='gray'), plt.axis('off')
    plt.subplot(343), plt.title('Prewitt'), plt.imshow(imgPrewitt, cmap='gray'), plt.axis('off')
    plt.subplot(347), plt.title('Prewitt_X'), plt.imshow(imgPrewitt_x, cmap='gray'), plt.axis('off')
    plt.subplot(3,4,11), plt.title('Prewitt_Y'), plt.imshow(imgPrewitt_y, cmap='gray'), plt.axis('off')
    plt.subplot(344), plt.title('Sobel'), plt.imshow(imgSobel, cmap='gray'), plt.axis('off')
    plt.subplot(348), plt.title('Sobel_X'), plt.imshow(imgSobel_x, cmap='gray'), plt.axis('off')
    plt.subplot(3,4,12), plt.title('Sobel_Y'), plt.imshow(imgSobel_y, cmap='gray'), plt.axis('off')
    plt.tight_layout()
    plt.show()


(本节完)


版权声明:

youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/124073181)

Copyright 2022 youcans, XUPT
Crated:2022-4-10


欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中

【youcans 的 OpenCV 例程200篇】01. 图像的读取(cv2.imread)
【youcans 的 OpenCV 例程200篇】02. 图像的保存(cv2.imwrite)
【youcans 的 OpenCV 例程200篇】03. 图像的显示(cv2.imshow)
【youcans 的 OpenCV 例程200篇】04. 用 matplotlib 显示图像(plt.imshow)
【youcans 的 OpenCV 例程200篇】05. 图像的属性(np.shape)
【youcans 的 OpenCV 例程200篇】06. 像素的编辑(img.itemset)
【youcans 的 OpenCV 例程200篇】07. 图像的创建(np.zeros)
【youcans 的 OpenCV 例程200篇】08. 图像的复制(np.copy)
【youcans 的 OpenCV 例程200篇】09. 图像的裁剪(cv2.selectROI)
【youcans 的 OpenCV 例程200篇】10. 图像的拼接(np.hstack)
【youcans 的 OpenCV 例程200篇】11. 图像通道的拆分(cv2.split)
【youcans 的 OpenCV 例程200篇】12. 图像通道的合并(cv2.merge&

以上是关于图像分析:边缘检测中的梯度算子的主要内容,如果未能解决你的问题,请参考以下文章

5.1 边缘检测基础

图像边缘检测小结

[ZZ] 边缘检测 梯度与RobertsPrewittSobelLapacian算子

Canny边缘检测算法

youcans 的 OpenCV 例程200篇150. 边缘检测梯度算子

Canny边缘检测算法的实现