图像处理学习2,边缘检测1(sobel算子,kirsch算子)

图像边缘的种类

图像中的边缘是像素灰度值发生加速变化而不连续的结果,边缘检测是常见的图像基元检测的基础,也是所有基于边界的图像分割方法的第一步。

Alt
图片来源:章毓晋.计算机视觉教程[M].北京:人民邮电出版社,2021.1:46

图像的边缘通常分为阶梯状边缘脉冲状边缘屋顶状边缘,通常用一阶导数的幅度值或二阶导数的过零点来检测图像的边缘,本文主要介绍一阶导数算子中的sobel算子kirsch算子来检测边缘。

算子与模板卷积

广义的讲,对任何函数进行某一项操作都可以认为是一个算子,狭义的算子实际上是指从一个函数空间到另一个函数空间(或它自身)的映射。我们这里使用的算子都是用模板卷积的方式进行计算的。

例如,考虑一个 3 × 3 3\times3 3×3的算子 A A A

[ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] \left[ \begin{matrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{matrix} \right] a11a21a31a12a22a32a13a23a33
待处理的图像为图像 F F F

F = [ f 11 f 12 ⋯ f 1 j f 21 f 22 ⋯ f 2 j ⋮ ⋮ ⋮ f i 1 f i 2 ⋯ f i j ] F = \left[ \begin{matrix} f_{11} & f_{12} &\cdots & f_{1j} \\ f_{21} & f_{22} &\cdots & f_{2j} \\ \vdots & \vdots & &\vdots \\ f_{i1} & f_{i2} & \cdots & f_{ij} \end{matrix} \right] F= f11f21fi1f12f22fi2f1jf2jfij
将这个算子和图像 F F F 进行卷积操作,得到的图像为 G G G

G = [ g 11 g 12 ⋯ g 1 j g 21 g 22 ⋯ g 2 j ⋮ ⋮ ⋮ g i 1 g i 2 ⋯ g i j ] G = \left[ \begin{matrix} g_{11} & g_{12} &\cdots & g_{1j} \\ g_{21} & g_{22} &\cdots & g_{2j} \\ \vdots & \vdots & &\vdots \\ g_{i1} & g_{i2} & \cdots & g_{ij} \end{matrix} \right] G= g11g21gi1g12g22gi2g1jg2jgij
考虑 算子 A A A f i j f_{ij} fij 的邻域

[ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] [ f i − 1 , j − 1 f i − 1 , j f i − 1 , j + 1 f i , j − 1 f i , j f i , j + 1 f i + 1 , j − 1 f i + 1 , j f i + 1 , j + 1 ] \left[ \begin{matrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{matrix} \right] \left[ \begin{matrix} f_{i-1,j-1} & f_{i-1,j} & f_{i-1,j+1} \\ f_{i,j-1} & f_{i,j} & f_{i,j+1} \\ f_{i+1,j-1} & f_{i+1,j} & f_{i+1,j+1} \end{matrix} \right] a11a21a31a12a22a32a13a23a33 fi1,j1fi,j1fi+1,j1fi1,jfi,jfi+1,jfi1,j+1fi,j+1fi+1,j+1
则对于 G G G

g i j = a 11 ∗ f i − 1 , j − 1 + a 12 ∗ f i − 1 , j + a 13 ∗ f i − 1 , j + 1 + a 21 ∗ f i , j − 1 + a 22 ∗ f i , j + a 23 ∗ f i , j + 1 + a 31 ∗ f i + 1 , j − 1 + a 32 ∗ f i + 1 , j + a 33 ∗ f i + 1 , j + 1 g_{ij} = a_{11}*f_{i-1,j-1} + a_{12}*f_{i-1,j} + a_{13}*f_{i-1,j+1} +\\ a_{21}*f_{i,j-1} + a_{22}*f_{i,j} + a_{23}*f_{i,j+1} +\\ a_{31}*f_{i+1,j-1} + a_{32}*f_{i+1,j} + a_{33}*f_{i+1,j+1}\\ gij=a11fi1,j1+a12fi1,j+a13fi1,j+1+a21fi,j1+a22fi,j+a23fi,j+1+a31fi+1,j1+a32fi+1,j+a33fi+1,j+1
这就是一般情况下算子的模板卷积操作,即将模板在图像上移动,并在每个位置将模板上的各个系数和模板下各对应像素的灰度值相乘求和,来计算对应中心像素的值,边缘检测所得到的像素值就是原像素的梯度值。

sobel算子

sobel算子是一种梯度算子,即所求的最终像素是原始像素的梯度值,它有两个方向的卷积核

沿 X 方向
[ − 1 0 1 − 2 0 2 − 1 0 1 ] \left[ \begin{matrix} -1 & 0 & 1\\ -2 & 0 & 2\\ -1 & 0 & 1 \end{matrix} \right] 121000121
沿 Y 方向
[ 1 2 1 0 0 0 − 1 − 2 − 1 ] \left[ \begin{matrix} 1 & 2 & 1\\ 0 & 0 & 0\\ -1 & -2 & -1 \end{matrix} \right] 101202101
它的卷积运算相当于对目标像素求一阶导。

Kirsch算子

kirsch模板除了 X 方向和 Y 方向外还包含了两个对角方向

比如,左上到右下方向
[ 3 3 3 3 0 − 5 3 − 5 − 5 ] \left[ \begin{matrix} 3 & 3 & 3\\ 3 & 0 & -5\\ 3 & -5 & -5 \end{matrix} \right] 333305355
右上到左下方向
[ − 5 − 5 3 − 5 0 3 3 3 3 ] \left[ \begin{matrix} -5 & -5 & 3\\ -5 & 0 & 3\\ 3 & 3 & 3 \end{matrix} \right] 553503333
它的卷积运算也相当于对目标像素求一阶导,只是求导的方向不同,类似的,你可以得到其他方向上的kirsch模板。

代码实现

下面我们将用 Python 来实现对图像的边缘检测算法

首先应该获取原始图像的灰度图像,这里我们直接使用之前处理得到的灰度图像

import cv2 as cv
# 获得原始图像
# 所得图像应为之前处理过的灰度图像
image = cv.imread("pai_mon_01_gray.jpg")

原始图像

在这里插入图片描述
先用sobel算子进行边缘检测

# 边缘检测
gray_gradient = gray.copy()
gray_height, gray_width = gray.shape[0:2]
for i in range(1, gray_height - 1):
    for j in range(1, gray_width - 1):
        # 水平方向检测
        # sobel 算子 [[-1, 0, 1], [-2, 0 ,2], [-1, 0, 1]]
        gradient_x = int(gray[i - 1, j - 1, 0]) - int(gray[i + 1, j - 1, 0]) + \
                     2 * int(gray[i - 1, j, 0]) - 2 * int(gray[i + 1, j, 0]) + \
                     int(gray[i - 1, j + 1, 0]) - int(gray[i + 1, j + 1, 0])

        # 垂直方向检测
        # sobel 算子 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]
        gradient_y = int(gray[i - 1, j + 1, 0]) - int(gray[i - 1, j - 1, 0]) + \
                     2 * int(gray[i, j + 1, 0]) - 2 * int(gray[i, j - 1, 0]) + \
                     int(gray[i + 1, j + 1, 0]) - int(gray[i - 1, j - 1, 0])
        gradient = max(gradient_x, gradient_y)
        # 将 gradient 转到像素值范围内
        if gradient < 0:
            gradient = abs(min(255, gradient))
        elif gradient > 255:
            gradient = 255
        gray_gradient[i, j, 0] = gradient
        gray_gradient[i, j, 1] = gradient
        gray_gradient[i, j, 2] = gradient


cv.imshow("gray_gradient", gray_gradient)
cv.waitKey(0)

这里需要注意的是我们要把卷积后得到的梯度值进行处理,使它在0到255这一个灰度值的取值范围内

得到的图像为

在这里插入图片描述
如果只用水平方向的sobel算子的话,得到的图像为

在这里插入图片描述
可以看到得到的效果明显不如前一张图片好

下面我们再加上kirsch算子观察检测结果

import cv2 as cv

# 获得原始图像
# 所得图像应为之前处理过的灰度图像
gray = cv.imread("pai_mon_01_gray.jpg")

# 边缘检测
gray_gradient = gray.copy()
gray_height, gray_width = gray.shape[0:2]
for i in range(1, gray_height - 1):
    for j in range(1, gray_width - 1):
        # 水平方向检测
        # sobel 算子 [[-1, 0, 1], [-2, 0 ,2], [-1, 0, 1]]
        gradient_x = int(gray[i - 1, j - 1, 0]) - int(gray[i + 1, j - 1, 0]) + \
                     2 * int(gray[i - 1, j, 0]) - 2 * int(gray[i + 1, j, 0]) + \
                     int(gray[i - 1, j + 1, 0]) - int(gray[i + 1, j + 1, 0])

        # 垂直方向检测
        # sobel 算子 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]
        gradient_y = int(gray[i - 1, j + 1, 0]) - int(gray[i - 1, j - 1, 0]) + \
                     2 * int(gray[i, j + 1, 0]) - 2 * int(gray[i, j - 1, 0]) + \
                     int(gray[i + 1, j + 1, 0]) - int(gray[i - 1, j - 1, 0])

        # 西北到东南方向检测
        # 基尔希算子 Kirsch = [[3, 3, 3], [3, 0, -5], [3, -5, -5]]
        gradient_WN_to_ES = 3 * (int(gray[i - 1, j - 1, 0]) + int(gray[i, j - 1, 0]) + int(gray[i + 1, j - 1, 0]) +
                                 int(gray[i - 1, j, 0]) + int(gray[i - 1, j + 1, 0])) - \
                            5 * (int(gray[i + 1, j, 0]) + int(gray[i, j + 1, 0]) + int(gray[i + 1, j + 1, 0]))

        # 东北到西南方向检测
        # 基尔希算子 Kirsch = [[3, 3, 3], [-5, 0, 3], [-5, -5, 3]]
        gradient_EN_to_WS = 3 * (int(gray[i - 1, j - 1, 0]) + int(gray[i - 1, j, 0]) + int(gray[i - 1, j + 1, 0]) +
                                 int(gray[i, j + 1, 0]) + int(gray[i + 1, j + 1, 0])) - \
                            5 * (int(gray[i, j - 1, 0]) + int(gray[i + 1, j - 1, 0]) + int(gray[i + 1, j, 0]))
        gradient = int(max(gradient_x, gradient_y, gradient_WN_to_ES, gradient_EN_to_WS))
        # 将 gradient 转到像素值范围内
        if gradient < 0:
            gradient = abs(min(255, gradient))
        elif gradient > 255:
            gradient = 255
        gray_gradient[i, j, 0] = gradient
        gray_gradient[i, j, 1] = gradient
        gray_gradient[i, j, 2] = gradient


cv.imshow("gray_gradient", gray_gradient)
cv.waitKey(0)

得到的结果为

在这里插入图片描述
可以发现和之前仅使用sobel算子相比,更多的边缘被检测出来了,同时边缘也更加明显,同时部分边缘也变得更粗,但总体来说得到的效果还算不错。

总结

图像的边缘是像素灰度值发生加速变化而不连续的结果,一种检测方法是通过计算像素的梯度值来显示边缘,其中sobel算子可以检测水平和竖直方向,kirsch算子可以检测八个方向,各方向间的夹角为45度,检测不同方向的算子对于不同方向上的边缘的响应效果会有所不同,可以更具具体情况进行使用。

参考文献

[1] 章毓晋.计算机视觉教程[M].北京:人民邮电出版社,2021.1:45-49
2] 链接:算子百度百科

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值