图像分析:边缘检测中的梯度算子
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/∂x∂f/∂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)=∂x∂f(x,y)=f(x+1,y)−f(x,y)gy(x,y)=∂y∂f(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&以上是关于图像分析:边缘检测中的梯度算子的主要内容,如果未能解决你的问题,请参考以下文章
[ZZ] 边缘检测 梯度与RobertsPrewittSobelLapacian算子