图像卷积与边缘检测

什么是边缘检测

边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。

所谓边缘,就是图像中强度发生突变的区域,通常暗示着物体边界的存在。

灰度变化不明显的部分会被过滤掉

边缘检测的一般流程

先灰度化,再用低通滤波器降噪,用高通滤波器提取边缘,最后二值化。
低通滤波器见前面我发布的图像预处理,下面讲的都是高通滤波器。

滤波器与卷积操作

为了提取到图像的边缘,我们需要滤波器的帮助。这类滤波器以矩阵的形式存在,通常被称为卷积核,就是一些值网格,能够对图像进行修改。

对于图像的每一个像素点,计算它的邻域像素和滤波器矩阵的对应元素的乘积,然后加起来,作为该像素位置的值。这样就完成了滤波过程。这种计算被称为图像卷积。

卷积核上的每一位乘数被称为权值,它们决定了这个像素的分量有多重。它们的总和加起来如果等于1,不会改变图像的灰度强度。如果大于1,会增加灰度强度,使得图像变亮。如果小于1,会减少灰度强度,使得图像变暗。如果和为0,图像不会变黑,但也会非常暗(凸出边缘)。

滤波器的大小应该是奇数,这样它才有一个中心,例如3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2。

几种经典的过滤器

1、索贝尔过滤器

索贝尔过滤器常用于边缘检测和发现图像中的强度模式。向图像中应用索贝尔过滤器就相当于沿着 x 或 y 方向求图像的(近似)导数。
Sobel_x和 Sobel_y的运算符分别如下所示:

接下来,我们看看将这两种过滤器应用到大脑图像的示例。

在上图中,可以看到在 x 和 y 方向计算的梯度检测出大脑的边缘并选出其他边缘。沿着 x 方向计算的梯度强调的是接近垂直方向的边缘,沿着 y 方向计算的梯度强调的是接近水平方向的边缘 。

大小
索贝尔还会检测哪些边缘最强,这一点体现在梯度大小上;大小越大,边缘越强。梯度的大小或绝对值是单个 x 和 y 梯度平方值的平方根。对于 x 和 y 方向的梯度,大小是平方和的平方根。

方向
在很多情形下,我们需要查找特定方向的边缘。例如,我们可能需要查找仅朝上或朝左的线条。通过单独计算 x 和 y 方向的图像梯度方向,我们可以判断该梯度的方向!
梯度方向是指 y 梯度除以 x 梯度的反正切:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# coding = utf-8
import numpy as np
import matplotlib.pyplot as plt
import cv2 as cv

# 图像读取并灰度化
image_stripes = cv.imread('images/stripes.jpg')
image_stripes = cv.cvtColor(image_stripes, cv.COLOR_BGR2RGB)
gray_stripes = cv.cvtColor(image_stripes, cv.COLOR_RGB2GRAY)

sobel_x = np.array([[ -1, 0, 1],
[ -2, 0, 2],
[ -1, 0, 1]])

sobel_y = np.array([[ -1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]])

filtered_image1 = cv.filter2D(gray_stripes, -1, sobel_x)
filtered_image2 = cv.filter2D(gray_stripes, -1, sobel_y)

2、拉普拉斯过滤器

Laplace算子是一种各向同性算子,二阶微分算子,在只关心边缘的位置而不考虑其周围的像素灰度差值时比较合适。
Laplace算子对孤立象素的响应要比对边缘或线的响应要更强烈,因此只适用于无噪声图象。存在噪声情况下,使用Laplacian算子检测边缘之前需要先进行低通滤波。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# coding = utf-8
import numpy as np
import matplotlib.pyplot as plt
import cv2 as cv

# 图像读取并灰度化
image_stripes = cv.imread('tools/33.jpg')
image_stripes = cv.cvtColor(image_stripes, cv.COLOR_BGR2RGB)
gray_stripes = cv.cvtColor(image_stripes, cv.COLOR_RGB2GRAY)

laplacian=np.array([[0, 1, 0],
[1,-4, 1],
[0, 1, 0]])

filtered_image = cv.filter2D(gray_stripes, -1, laplacian)

了解更多

3、Canny过滤器

Canny边缘检测是一种非常流行的边缘检测算法,是John Canny在1986年提出的。它是一个多阶段的算法,即由多个步骤构成。
首先,图像降噪。我们知道梯度算子可以用于增强图像,本质上是通过增强边缘轮廓来实现的,也就是说是可以检测到边缘的。但是,它们受噪声的影响都很大。那么,我们第一步就是想到要先去除噪声,因为噪声就是灰度变化很大的地方,所以容易被识别为伪边缘。
第二步,计算图像梯度,得到可能边缘。计算图像梯度能够得到图像的边缘,因为梯度是灰度变化明显的地方,而边缘也是灰度变化明显的地方。当然这一步只能得到可能的边缘。因为灰度变化的地方可能是边缘,也可能不是边缘。这一步就有了所有可能是边缘的集合。
第三步,非极大值抑制。通常灰度变化的地方都比较集中,将局部范围内的梯度方向上,灰度变化最大的保留下来,其它的不保留,这样可以剔除掉一大部分的点。将有多个像素宽的边缘变成一个单像素宽的边缘。即“胖边缘”变成“瘦边缘”。
第四步,双阈值筛选。通过非极大值抑制后,仍然有很多的可能边缘点,进一步的设置一个双阈值,即低阈值(low),高阈值(high)。灰度变化大于high的,设置为强边缘像素,低于low的,剔除。在low和high之间的设置为弱边缘。进一步判断,如果其领域内有强边缘像素,保留,如果没有,剔除。这样做的目的是只保留强边缘轮廓的话,有些边缘可能不闭合,需要从满足low和high之间的点进行补充,使得边缘尽可能的闭合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# coding = utf-8
import numpy as np
import matplotlib.pyplot as plt
import cv2 as cv

# 图像读取并灰度化
image_stripes = cv.imread('tools/33.jpg')
image_stripes = cv.cvtColor(image_stripes, cv.COLOR_BGR2RGB)
gray_stripes = cv.cvtColor(image_stripes, cv.COLOR_RGB2GRAY)

lower = 50
upper = 100

edges = cv.Canny(gray_stripes, lower, upper)
plt.imshow(edges)
plt.show()
0%