前言
梯度简单理解就是求导,OpenCV提供了三种梯度滤波器,或称为高通滤波器:Sobel,Scharr,Laplacian。
Sobel,Scharr 是求一阶或二阶导数。
Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化。
Laplacian 是求二阶导数。
Sobel算子
Sobel算子是高斯平滑和微分操作的结合体,它的抗噪声能力很好,可以提供比较精确的边缘方向信息,但是边缘的定位精度不高。
我们可以设定求导的方向为
x
\color{red}{x }
x或
y
\color{red}{y }
y,设定卷积核的大小。
我们使用上述3*3模板对原图像进行卷积操作(对应位置相乘然后求和)得到Gx、Gy
假设A为如下像素值矩阵:
[
P
1
P
2
P
3
P
4
P
5
P
6
P
7
P
8
P
9
]
\begin{bmatrix} P1 & P2 & P3\\ P4 & P5 & P6 \\P7 & P8 &P9 \end{bmatrix}
⎣⎡P1P4P7P2P5P8P3P6P9⎦⎤
P
5
x
\color{red}{P5x}
P5x =
(
P
3
−
P
1
)
+
(
2
P
6
−
2
P
4
)
+
(
P
9
−
P
7
)
\color{blue}{(P3 - P1) + (2P6 - 2P4) + (P9 - P7)}
(P3−P1)+(2P6−2P4)+(P9−P7)
P
5
y
\color{red}{P5y}
P5y =
(
P
7
−
P
1
)
+
(
2
P
8
−
2
P
2
)
+
(
P
9
−
P
3
)
\color{blue}{(P7 - P1) + (2P8 - 2P2) + (P9 - P3) }
(P7−P1)+(2P8−2P2)+(P9−P3)
把得到的结果替换掉中间的像素值,按照这样的方法对图像进行遍历,提取到水平和垂直方向的边界。
Sobel函数
dst = cv2.Sobel(src, ddepth, dx, dy,ksize)
- s r c , d s t : \color{orange}{src,dst:} src,dst: 输入图像和输出图像,图像尺寸一致。
-
i
n
t
\color{orange}{int}
int
d
d
e
p
t
h
:
\color{orange}{ddepth:}
ddepth:输出图像的深度,针对不同的输入图像,输出目标图像有不同的深度,具体组合如下:
若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
若src.depth() = CV_64F, 取ddepth = -1/CV_64F
注:ddepth =-1时,代表输出图像与输入图像相同的深度。 -
i
n
t
\color{orange}{int}
int
d
x
:
\color{orange}{dx:}
dx:int类型dx,x 方向上的差分阶数,1或0
i n t \color{orange}{int} int d y : \color{orange}{dy:} dy:int类型dy,y 方向上的差分阶数,1或0
dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘;
dx=0,dy=1,表示计算Y方向的导数,检测出的是水平方向上的边缘。 -
i
n
t
\color{orange}{int}
int
k
s
i
z
e
:
\color{orange}{ksize:}
ksize:为进行边缘检测时的模板大小为(ksize
∗
*
∗ksize),取值为1、3、5和7,其中默认值为3。
特殊情况:ksize=1时,采用的模板为3 ∗ * ∗ 1或1 ∗ * ∗ 3
Scharr
当ksize=3时,Sobel内核可能产生比较明显的误差,此时,可以使用 Scharr 函数,该函数仅作用于大小为3的内核(所以Scharr函数里不能自己设定卷积核大小)。它具有跟Sobel一样的速度,但结果更精确。
它的核模板为:
其运算法则也是跟上一个一样。
Scharr函数
dst = cv2.Scharr(src, ddepth, dx, dy)
参数含义与Sobel里面的一样,不做解释。
说明:
Sobel和Scharr
是先算一个水平梯度,再算一个垂直梯度,将两个结果按照0.5的权重进行图像融合来得到完整的边界。
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)
Laplacian
Laplacian算子与前面的两种算子不同,它本身是一个二阶的,在水平方向运算两次,在垂直方向运行两次,两个结果相叠加替换中心点的像素值。
使用的核模板为:
把前面的那个像素值拿下来,假设A为如下像素值矩阵:
[
P
1
P
2
P
3
P
4
P
5
P
6
P
7
P
8
P
9
]
\begin{bmatrix} P1 & P2 & P3\\ P4 & P5 & P6 \\P7 & P8 &P9 \end{bmatrix}
⎣⎡P1P4P7P2P5P8P3P6P9⎦⎤
P 5 \color{red}{P5} P5 = ( P 2 + P 4 + P 6 + P 8 ) − 4 P 5 \color{blue}{(P2 + P4 + P6 + P8)- 4P5} (P2+P4+P6+P8)−4P5
三种算子对比结果可视化:
Scharr算子所获取的信息更多。
完整代码
import cv2
import numpy as np
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
cv_show('img', img)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
# 取绝对值,并将梯度图像转换成256色位图,转换为unit8类型
sobelx = cv2.convertScaleAbs(sobelx)
#cv_show('sobelx', sobelx)
# 白到黑是整数,黑到白就是负数了, 所有的负数都会截断成0,所以要取绝对值
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobely = cv2.convertScaleAbs(sobely)
#cv_show('sobely', sobely)
sobelxy1 = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
sobelxy1 = cv2.convertScaleAbs(sobelxy1)
# 直接计算
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0) # 结果更好
#cv_show('sobelxy', sobelxy)
# 不同方向信息对比
# res1 = np.hstack((img, sobelx, sobely, sobelxy, sobelxy1))
# cv_show('res1', res1)
# Scharr 算子
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharrx = cv2.convertScaleAbs(sobelx)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)
#Laplacian
laplacian= cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
# 结果对比
res = np.hstack((img, sobelxy, scharrx, laplacian))
cv_show('res', res)