Sobel算子
sobel算子是图像边缘检测的最重要的算子之一,在机器学习、数字媒体、计算机视觉等领域起着重要作用。
Sobel算子包含两组3×3的滤波器,分别对水平及垂直方向上的边缘敏感。
G
x
=
[
x
1
x
2
x
3
x
4
x
5
x
6
x
7
x
8
x
9
]
=
[
−
1
0
1
−
2
0
2
−
1
0
1
]
∗
A
(3)
G_x= \left[\begin{matrix}x1 & x2 &x3 \\x4 & x5 & x6 \\x7 & x8 & x9\end{matrix}\right] =\left[\begin{matrix}-1 & 0 & 1 \\-2 &0 & 2 \\-1 & 0 & 1\end{matrix}\right] \tag{3}*A
Gx=⎣
⎡x1x4x7x2x5x8x3x6x9⎦
⎤=⎣
⎡−1−2−1000121⎦
⎤∗A(3)
G
y
=
[
x
1
x
2
x
3
x
4
x
5
x
6
x
7
x
8
x
9
]
=
[
−
1
−
2
−
1
0
0
0
1
2
1
]
∗
A
(3)
G_y = \left[\begin{matrix}x1 & x2 &x3 \\x4 & x5 & x6 \\x7 & x8 & x9\end{matrix}\right] =\left[\begin{matrix}-1 & -2 & -1 \\0 &0 & 0 \\1 & 2 & 1\end{matrix}\right] \tag{3}*A
Gy=⎣
⎡x1x4x7x2x5x8x3x6x9⎦
⎤=⎣
⎡−101−202−101⎦
⎤∗A(3)
Sobel算法的本质是让两个方向模板分别沿着x轴、y轴与图像做卷积,方向是从上到下和从左到右。将模板的中心和图像上的某个像素重合,并将该像素周围的点 与模板上对应的系数相乘,其中Gx及Gy分别代表经横向及纵向边缘检测的图像梯度值。
只算左右方向
import cv2
import numpy as np
img = cv2.imread('img.jpg')
#dst = cv2.Sobel(src,ddepth,dx,dy,ksize,scale, delta, borderType)
#前四个为必须项,其后为可选参数
#ddepth:图形的深度
#dx和dy分别表示水平和竖直方向
#ksize是Sobel算子的大小
#scale是缩放导数的比例常数,默认情况下没有伸缩系数;
#delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
#borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
img_sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
cv2.imshow('img_sobelx', img_sobelx)
cv2.waitKey(0)
cv2.destroyAllWindows()
原图于运行结果:
![](https://i-blog.csdnimg.cn/blog_migrate/b6cb55cb31b5ed46c7c634c60cf83403.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bf99aadd1e4b8c5d5bd1ce3da67fa0cf.png)
因为从黑到白为正数,从白到黑为负数,所有的负数被截断成0,所以要去绝对值。
import cv2
import numpy as np
img = cv2.imread('img.jpg')
img_sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
img_sobelx = cv2.convertScaleAbs(img_sobelx)
cv2.imshow('img_sobelx', img_sobelx)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果明显变好,边缘处既无错误判断,也能处理从白到黑的地方
只算上下方向
import cv2
import numpy as np
img = cv2.imread('img.jpg')
img_sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
img_sobely = cv2.convertScaleAbs(img_sobely)
cv2.imshow('img_sobely', img_sobely)
cv2.waitKey(0)
cv2.destroyAllWindows()
两个方向的计算结果求和
import cv2
import numpy as np
img = cv2.imread('img.jpg')
img_sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
img_sobelx = cv2.convertScaleAbs(img_sobelx)
img_sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
img_sobely = cv2.convertScaleAbs(img_sobely)
img_sobel = cv2.addWeighted(img_sobely,0.5,img_sobely,0.5,0)
cv2.imshow('img_sobel',img_sobel)
cv2.waitKey(0)
cv2.destroyAllWindows()
不建议直接计算两个方向,在融合的过程中会出现发散,效果不好
import cv2
import numpy as np
img = cv2.imread('img.jpg')
img_sobel = cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
img_sobel = cv2.convertScaleAbs(img_sobel)
cv2.imshow('img_sobel',img_sobel)
cv2.waitKey(0)
cv2.destroyAllWindows()
Scharr算子
Scharr算子是对Sobel算子差异性的增强,因此两者之间的在检测图像边缘的原理和使用方式上相同。Scharr算子的边缘检测滤波的尺寸为3×3,因此也有称其为Scharr滤波器。可以通过将滤波器中的权重系数放大来增大像素值间的差异,Scharr算子就是采用的这种思想,其在X方向和Y方向的边缘检测算子:
G
x
=
[
x
1
x
2
x
3
x
4
x
5
x
6
x
7
x
8
x
9
]
=
[
−
3
0
3
−
10
0
10
−
3
0
3
]
∗
A
(3)
G_x= \left[\begin{matrix}x1 & x2 &x3 \\x4 & x5 & x6 \\x7 & x8 & x9\end{matrix}\right] =\left[\begin{matrix}-3 & 0 & 3 \\-10 &0 & 10 \\-3 & 0 & 3\end{matrix}\right] \tag{3}*A
Gx=⎣
⎡x1x4x7x2x5x8x3x6x9⎦
⎤=⎣
⎡−3−10−30003103⎦
⎤∗A(3)
G
y
=
[
x
1
x
2
x
3
x
4
x
5
x
6
x
7
x
8
x
9
]
=
[
−
3
−
10
−
3
0
0
0
3
10
3
]
∗
A
(3)
G_y = \left[\begin{matrix}x1 & x2 &x3 \\x4 & x5 & x6 \\x7 & x8 & x9\end{matrix}\right] =\left[\begin{matrix}-3 & -10 & -3 \\0 &0 & 0 \\3 & 10 & 3\end{matrix}\right] \tag{3}*A
Gy=⎣
⎡x1x4x7x2x5x8x3x6x9⎦
⎤=⎣
⎡−303−10010−303⎦
⎤∗A(3)
laplacian算子
Laplacian算子具有各方向同性的特点,能够对任意方向的边缘进行提取,具有无方向性的优点,因此使用Laplacian算子提取边缘不需要分别检测X方向的边缘和Y方向的边缘,只需要一次边缘检测即可。Laplacian算子是一种二阶导数算子,对噪声比较敏感,因此常需要配合高斯滤波一起使用。
G
x
=
[
0
1
0
1
−
4
1
0
1
0
]
(3)
G_x=\left[\begin{matrix}0 & 1 & 0 \\1 & -4 & 1 \\0 & 1 & 0\end{matrix}\right] \tag{3}
Gx=⎣
⎡0101−41010⎦
⎤(3)
import cv2
import numpy as np
img = cv2.imread('img.jpg')
scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharry = cv2.convertScaleAbs(scharry)
scharr = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
cv2.imshow('scharr',scharr)
cv2.imshow('laplacian',laplacian)
cv2.waitKey(0)
cv2.destroyAllWindows()
![](https://i-blog.csdnimg.cn/blog_migrate/d57e532265255f228e15ca18dfe11548.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c10031403225ab5f39ae1b5a1ec190bd.png)
补充:关于参数ddepth的使用
首先来说CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F,都是opencv定义的数据类型。
具体定义如下
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))
#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))
#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))
#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))
#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))
#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))
#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))
#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))
具体含义
S = 有符号整型 U = 无符号整型 F = 浮点型
CV_8U : 8位无符号整数
CV_8S : 8位有符号整数
CV_16U : 16位无符号整数
CV_16S : 16位有符号整数
CV_32S : 32位有符号整数
CV_32F : 32位浮点数
CV_64F : 64位浮点数
Canny边缘检测
1、基本步骤
1)图像降噪。一般使用高斯滤波器,以平滑图像,滤除噪声。
高斯滤波器的模板系数,随着距离模板中心的增大而系数减小。
高斯滤波器相比于均值滤波器对图像个模糊程度较小
#高斯模糊的卷积核里的数值是高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(noiseimage, (5, 5), 1 )
2)计算图像梯度。计算图像中每个像素点的梯度强度和大小。
因为梯度是灰度变化明显的地方,而边缘也是灰度变化明显的地方,所以计算图像梯度能够初步得到图像的边缘
3)非极大值抑制。应用非极大值抑制,以消除边缘检测带来的杂散效应。
非极大值抑制,简称为NMS算法。其思想是搜素局部最大值,抑制非极大值。目标检测的过程中在同一目标的位置上会产生大量的候选框,这些候选框相互之间可能会有重叠,此时我们需要利用非极大值抑制找到最佳的目标边界框,消除冗余的边界框
4)阈值筛选。应用双阈值检测来确定真实的和潜在的边缘,通过抑制孤立的弱边缘最终完成边缘检测
定义了两个参数,minval和maxval。
小于minval可认为不是边界,舍弃。
大于maxval则处理为边界。
大于minval,小于maxval,与边界相连就保留,否则舍弃。
使用opencv自带函数的运行代码:
import cv2
import numpy as np
img = cv2.imread('image1.png',cv2.IMREAD_GRAYSCALE )
v1 = cv2.Canny(img, 80, 150)
v2 = cv2.Canny(img, 50, 100)
cv2.imshow('init',img)
cv2.imshow('v1', v1)
cv2.imshow('v2', v2)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果显示
![](https://i-blog.csdnimg.cn/blog_migrate/b222f1c9325c4709d8ee2f4f941681a1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cef5e4fba3f4a3128fe76440d6a3c4b8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0bc0a1eec982938a6f832c612de96bfc.png)