文章目录
一、Sobel算子
(一)Sobel算子原理及数学表达
- Soble算子概念及原理
Sobel算子是一种常用于边缘检测的方式,图像每个像素点的邻近像素点对其影响不同,从而邻近像素点的权重不相同。Sobel算子的原理是对传进来的图像像素做卷积,卷积的实质是在求梯度值(加权平均)
,其中权值就是所谓的卷积核;然后对生成的新像素灰度值做阈值运算
,以此来确定边缘信息。 - 卷积核
Sobel算子是把图像中每个像素的上下左右四领域的灰度值加权差,在边缘处达到极值从而检测边缘。
举例3维的卷积核
Gx表示横向做卷积,Gy表示纵向做卷积。
G x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ∗ A a n d G y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] ∗ A Gx= \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \\ \end{bmatrix}*A \quad and \quad Gy= \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \\ \end{bmatrix}*A Gx=⎣⎡−1−2−1000121⎦⎤∗AandGy=⎣⎡−101−202−101⎦⎤∗A
实际上Sobel算子进行边缘检测上是做了两个方向的卷积,所以最终卷积结果如下
G
=
G
x
2
+
G
y
x
G=\sqrt{G_x^{2}+G_y^{x}}
G=Gx2+Gyx
(二)Sobel算子的实现过程
-
opencv实现Sobel算子函数
def Sobel(src: Any, ddepth: Any, dx: Any, dy: Any, dst: Any = None, ksize: Any = None, scale: Any = None, delta: Any = None, borderType: Any = None)
部分参数说明
src:原始图像对象
ddepth:图像深度,通常使用-1,表示与原始图像深度保持一致
dx:表示x方向的梯度(导数)
dy:表示y方向的梯度(导数) -
对比x,y方向
import cv2 import numpy as np def showImg(img, name): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows() circle = cv2.imread("circle.png") showImg(circle, "circle") sobel_x = cv2.Sobel(circle, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(circle, cv2.CV_64F, 0, 1, ksize=3) sobel_x_y = np.hstack((sobel_x, sobel_y)) # 显示结果 showImg(sobel_x_y, 'circleXY')
原始图像
检测结果
通过检测结果可以发现无论是在水平还是垂直方向上,都存在只检测出一半的情况。产生此种原因是由于后面边缘像素点在做卷积后得到的结果为负数,然而像素点的值是0~255之间,所以,所有负数部分都全部变为了0。
解决只检测一半的方式#在显示之前添加如下代码,对得到结果进行一个取绝对值操作 sobel_x = cv2.convertScaleAbs(sobel_x) sobel_y = cv2.convertScaleAbs(sobel_y)
对于取绝对值后的结果,会发现水平方向会在垂直方向上存在一些断点,垂直方向同理。出现此种原因是由于只做了某个方向的梯度计算。
直接计算两个方向的梯度sobel_xy = cv2.Sobel(circle, cv2.CV_64F, 1, 1, ksize=3) sobel_xy = cv2.convertScaleAbs(sobel_xy) showImg(sobel_xy, 'circleX')
分别求解两个方向的梯度后,进行加权平均值sobel_x = cv2.Sobel(circle, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(circle, cv2.CV_64F, 0, 1, ksize=3) sobel_x = cv2.convertScaleAbs(sobel_x) sobel_y = cv2.convertScaleAbs(sobel_y) sobel_x_y = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0) # 显示结果 showImg(sobel_x_y, 'circleXY')
两种方式的结果对比可以发现,第一种方式在水平和垂直方向上都出现断点的情况,而第二种方式就比较好的解决断点的情况。
二、Scharr算子
(一)Scharr算子的原理和数学表达
- Scharr算子的原理概念
Scharr算子实现原理跟Sobel算子基本上是一致的,都是通过对上下左右四领域的灰度值加权差。两种区别在于后者设定的权重相对更大。 - 卷积核
采用3维
Gx表示横向做卷积,Gy表示纵向做卷积。
G x = [ − 3 0 3 − 10 0 10 − 3 0 3 ] ∗ A a n d G y = [ − 3 − 10 − 3 0 0 0 3 10 3 ] ∗ A Gx= \begin{bmatrix} -3 & 0 & 3 \\ -10 & 0 & 10 \\ -3 & 0 & 3 \\ \end{bmatrix}*A \quad and \quad Gy= \begin{bmatrix} -3 & -10 & -3 \\ 0 & 0 & 0 \\ 3 & 10 & 3 \\ \end{bmatrix}*A Gx=⎣⎡−3−10−30003103⎦⎤∗AandGy=⎣⎡−303−10010−303⎦⎤∗A
最终结果也是通过分别求解取绝对值后,求解加权平均值。
(二)Scharr算子的实现
- opencv实现Scharr算子函数
其参数具体含义同Sobel算子。def Scharr(src: Any, ddepth: Any, dx: Any, dy: Any, dst: Any = None, scale: Any = None, delta: Any = None, borderType: Any = None)
- 实际举例
由于Scharr算子跟Sobel类似,所以就不过多分析。scharr_x = cv2.Scharr(circle, cv2.CV_64F, 1, 0) scharr_y = cv2.Scharr(circle, cv2.CV_64F, 0, 1) scharr_x = cv2.convertScaleAbs(scharr_x) scharr_y = cv2.convertScaleAbs(scharr_y) scharr_x_y = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0) showImg(scharr_x_y, 'scharr')
三、Laplacian算子
(一) Laplacian算子原理及数学表达
- Laplacian算子概念及原理
Laplacian算子是一个二阶算子,它是通过在水平方向运算两次,垂直方向运算两次,两个结果相叠加替换中心点(锚点)的像素值(灰度值),跟上面两种算子有着本质上的区别,也可以将该算子类似的看出二阶的Sobel算子。 - 卷积核
3维卷积核
G = [ 0 1 0 1 − 4 1 0 1 0 ] ∗ A G= \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \\ \end{bmatrix}*A G=⎣⎡0101−41010⎦⎤∗A
(二) Laplacian算子实现过程
-
opencv实现Laplacian算子函数
def Laplacian(src: Any, ddepth: Any, dst: Any = None, ksize: Any = None, scale: Any = None, delta: Any = None, borderType: Any = None)
其参数含义跟上面一致。
-
实际举例
laplacian = cv2.Laplacian(circle, cv2.CV_64F, ksize=3) laplacian = cv2.convertScaleAbs(laplacian) showImg(laplacian, 'laplacian')
四、三种算子的对比
- 对比过程
lena = cv2.imread("C:/Users/Administrator/Downloads/Lena1.png", cv2.IMREAD_GRAYSCALE) # Sobel算子 sobel_x = cv2.Sobel(lena, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(lena, cv2.CV_64F, 0, 1, ksize=3) sobel_x = cv2.convertScaleAbs(sobel_x) sobel_y = cv2.convertScaleAbs(sobel_y) sobel_x_y = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0) # Scharr算子 scharr_x = cv2.Scharr(lena, cv2.CV_64F, 1, 0) scharr_y = cv2.Scharr(lena, cv2.CV_64F, 0, 1) scharr_x = cv2.convertScaleAbs(scharr_x) scharr_y = cv2.convertScaleAbs(scharr_y) scharr_x_y = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0) # Laplacian算子 laplacian = cv2.Laplacian(lena, cv2.CV_64F, ksize=3) laplacian = cv2.convertScaleAbs(laplacian) plt.subplot(221), plt.imshow(lena, 'gray'), plt.title('ORIGINAL') plt.xticks([]), plt.yticks([]) plt.subplot(222), plt.imshow(sobel_x_y, 'gray'), plt.title('Sobel') plt.xticks([]), plt.yticks([]) plt.subplot(223), plt.imshow(scharr_x_y, 'gray'), plt.title('Scharr') plt.xticks([]), plt.yticks([]) plt.subplot(224), plt.imshow(laplacian, 'gray'), plt.title('Laplacian') plt.xticks([]), plt.yticks([]) plt.show()
- 对比结论
通过上面对比,可以发现针对于Sobel和Scharr算子来说,Scharr在边缘检测上会更详尽。整体上来说,Scharr算子在边缘处会更加详尽。