查找图像渐变、边缘等
使用到以下方法 : cv.Sobel(), cv.Scharr(), cv.Laplacian()…
原理
OpenCV 提供了三种类型的梯度滤波器或高通滤波器,Sobel、Scharr 和 Laplacian。
1. Sobel 和 Scharr 导数
Sobel算子是联合高斯平滑加微分运算,所以更抗噪。您可以指定要采用的导数的方向,垂直或水平(分别通过参数 yorder 和 xorder)。您还可以通过参数 ksize 指定内核的大小。如果 ksize = -1,则使用 3x3 Scharr 过滤器,其结果比 3x3 Sobel 过滤器更好。请参阅所用内核的文档。
1.1 Sobel()方法
在除一种情况之外的所有情况下,ksize×ksize 可分离核用于计算导数。当ksize = 1时,使用3×1或1×3核(即不做高斯平滑)。 ksize = 1 只能用于一阶或二阶 x 或 y 导数。
还有一个特殊值 ksize = FILTER_SCHRR (-1) 对应于 3×3 Scharr 过滤器,它可能给出比 3×3 Sobel 更准确的结果。shcarr为:
[
−
3
0
3
−
10
0
10
−
3
0
3
]
(1)
\begin{bmatrix} -3&0&3\\ -10&0&10\\ -3&0&3\\ \end{bmatrix}\tag{1}
⎣⎡−3−10−30003103⎦⎤(1)
该函数通过使用适当的内核对图像进行卷积来计算图像导数:
d
s
s
t
=
∂
x
o
r
d
e
r
+
y
o
r
d
e
r
s
r
c
∂
x
x
o
r
d
e
r
∂
y
y
o
r
d
e
r
dsst=\frac{∂^{xorder}+y^{order}src}{∂x^{xorder}∂y^{yorder}}
dsst=∂xxorder∂yyorder∂xorder+yordersrc
Sobel 算子结合了高斯平滑和微分,因此结果或多或少地抵抗噪声。大多数情况下,使用 ( xorder = 1, yorder = 0, ksize = 3) 或 ( xorder = 0, yorder = 1, ksize = 3) 调用该函数来计算第一个 x 或 y 图像导数。第一种情况对应于以下内核:
[
−
1
0
1
−
2
0
2
−
1
0
2
]
(2)
\begin{bmatrix} -1&0&1\\ -2&0&2\\ -1&0&2\\ \end{bmatrix}\tag{2}
⎣⎡−1−2−1000122⎦⎤(2)
第二种情况对应于以下内核:
[
−
1
−
2
−
1
0
0
0
1
2
1
]
(3)
\begin{bmatrix} -1&-2&-1\\ 0&0&0\\ 1&2&1\\ \end{bmatrix}\tag{3}
⎣⎡−101−202−101⎦⎤(3)
python中的用法:
dst = cv.Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] )
参数 | 描述 |
---|---|
src | 输入图片矩阵 |
dst | 输出图片矩阵 |
ddepth | 图像深度 |
dx | 导数x的阶数 |
dy | 导数y的阶数 |
ksize | 扩展 Sobel 核的大小;它必须是 1、3、5 或 7 |
scale | 缩放尺度 |
delta | 在将结果存储到 dst 之前添加到结果中的可选增量值。 |
borderType | 像素拓展方法,参见 BorderTypes。不支持 BORDER_WRAP。 |
1.2shcarr()方法
Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)
相当于
Sobel(src, dst, ddepth, dx, dy, FILTER_SCHARR, scale, delta, borderType).
2. Laplacian 导数
它计算由关系给出的图像的拉普拉斯算子 Δ s r c = ∂ 2 s r c ∂ x 2 + ∂ 2 s r c ∂ y 2 Δsrc=\frac{∂2src}{∂x2}+\frac{∂2src}{∂y2} Δsrc=∂x2∂2src+∂y2∂2src这是在ksize>1时进行的,其中每个导数都是使用 Sobel 导数找到的。如果 ksize = 1,则使用以下内核进行过滤:
k
e
r
n
e
l
=
[
0
1
0
1
−
4
1
0
1
0
]
(4)
kernel = \begin{bmatrix} 0&1&0\\ 1&-4&1\\ 0&1&0\\ \end{bmatrix}\tag{4}
kernel=⎣⎡0101−41010⎦⎤(4)
python中用法:
dst = cv.Laplacian( src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]] )
参数 | 描述 |
---|---|
src | 输入图片矩阵 |
dst | 输出图片矩阵 |
ddepth | 图像深度 |
ksize | 用于计算二阶导数滤波器的孔径大小。大小必须是正数和奇数。 |
scale | 计算的拉普拉斯值的可选比例因子。默认情况下,不应用缩放。 |
delta | 可选的delta值,在将结果存储到dst之前,将其添加到结果中。 |
borderType | 像素拓展方法,参见 BorderTypes。不支持 BORDER_WRAP。 |
img = cv.imread("../../file/photos/dave.jpg", 0)
# ksize = 9 是最好的效果
laplacian = cv.Laplacian(img, cv.CV_64F, ksize=9)
sobelx = cv.Sobel(img, cv.CV_8U, 1, 0, ksize=-1)
sobely = cv.Sobel(img, cv.CV_8U, 0, 1, ksize=5)
plt.subplot(2, 2, 1), plt.imshow(img, cmap='gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 2), plt.imshow(laplacian, cmap='gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 3), plt.imshow(sobelx, cmap='gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 4), plt.imshow(sobely, cmap='gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
结果如下,Lapalacian结果和官方文档结果不同
在最后一个示例中,输出数据类型是 cv.CV_8U 或 np.uint8。但这有一个小问题。黑到白过渡被视为正斜率(它具有正值),而白到黑过渡被视为负斜率(它具有负值)。因此,当您将数据转换为 np.uint8 时,所有负斜率都为零。简而言之,你错过了那个边缘,如果你想检测两个边缘,更好的选择是将输出数据类型保持为更高的形式,如 cv.CV_16S、cv.CV_64F 等,取其绝对值,然后转换回 cv.CV_8U。下面的代码演示了水平 Sobel 滤波器的此过程和结果差异。
img = cv.imread("../../file/photos/box1.jpg", 0)
sobelx8u = cv.Sobel(img, -1, 1, 0, ksize=3)
sobelx64f = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3)
abs_sobel64f = np.abs(sobelx64f) # 对其求绝对值
sobel_8u = np.uint8(abs_sobel64f) # 将绝对值转换成uint8
结果如下
depth类型
Input depth (src.depth()) | Output depth (ddepth) |
---|---|
CV_8U | -1/CV_16S/CV_32F/CV_64F |
CV_16U/CV_16S | -1/CV_32F/CV_64F |
CV_32F | -1/CV_32F/CV_64F |
CV_64F | -1/CV_64F |