在本章中,将学习:
cv2.Sobel()
cv2.Scharr()
cv2.Laplacian()
1)边缘:灰度或结构等信息的突变处,边缘是一个区域的结束,也是另一个区域的开始,利用该特征可以分割图像。
2)边缘点:图像中具有坐标[x,y],且处在强度显著变化的位置上的点。
3)边缘段:对应于边缘点坐标[x,y]及其方位 ,边缘的方位可能是梯度角
OpenCV提供三种类型的梯度滤波器或高通滤波器,即Sobel,Scharr和Laplacian。
索贝尔算子(Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。
Sobel算子是高斯平滑加微分运算的联合运算,因此它更抗噪声。
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
函数cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]])
参数:
src:输入图像
ddepth: 输出图像的深度(可以理解为数据类型),-1表示与原图像相同的深度
dx,dy:当组合为dx=1,dy=0时求x方向的一阶导数,当组合为dx=0,dy=1时求y方向的一阶导数(如果同时为1,通常得不到想要的结果)
ksize:(可选参数)Sobel算子的大小**,必须是1,3,5或者7,默认为3**。求X方向和Y方向一阶导数时,卷积核分别为:
scale:(可选参数)将梯度计算得到的数值放大的比例系数,效果通常使梯度图更亮,默认为1
delta:(可选参数)在将目标图像存储进多维数组前,可以将每个像素值增加delta,默认为0
borderType:(可选参数)决定图像在进行滤波操作(卷积)时边沿像素的处理方式,默认为BORDER_DEFAULT
返回值:梯度图
# sobel 算子
import cv2
import numpy
from matplotlib import pyplot as plt
img = cv2.imread('sudo.png', 0)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
sobelxy = cv2.addWeighted(sobelx,0.5, sobely, 0.5, 0)
plt.subplot(1,4,1)
plt.imshow(img,cmap = 'gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])
plt.subplot(1,4,2)
plt.imshow(sobelx, cmap='gray')
plt.title('sobelx')
plt.xticks([])
plt.yticks([])
plt.subplot(1,4,3)
plt.imshow(sobely, cmap='gray')
plt.title('sobely')
plt.xticks([])
plt.yticks([])
plt.subplot(1,4,4)
plt.imshow(sobelxy, cmap='gray')
plt.title('sobelxy')
plt.xticks([])
plt.yticks([])
plt.show()
注意:如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,该效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器)。
scharr算子与sobel算子基本一样,只是算子里面运算核参数有差别,如下图所示。Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化
dst=cv2.Scharr(src,ddpetph,dx,dy)
dst:处理结果
src:源图像
ddpetph:图像深度
dx:x轴方向,
dy:y轴方向
# scharr 算子
import cv2
import numpy
from matplotlib import pyplot as plt
img = cv2.imread('sudo.png', 0)
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharrxy = cv2.addWeighted(scharrx,0.5, scharry, 0.5, 0)
plt.subplot(1,4,1)
plt.imshow(img,cmap = 'gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])
plt.subplot(1,4,2)
plt.imshow(scharrx, cmap='gray')
plt.title('Scharrx')
plt.xticks([])
plt.yticks([])
plt.subplot(1,4,3)
plt.imshow(scharry, cmap='gray')
plt.title('Scharry')
plt.xticks([])
plt.yticks([])
plt.subplot(1,4,4)
plt.imshow(scharrxy, cmap='gray')
plt.title('Scharrxy')
plt.xticks([])
plt.yticks([])
plt.show()
Sobel算子是求一阶导数,获得图像像素值变化部分信息(在其一阶导数在边缘位置为极值,这就是Sobel算子使用的原理——极值处就是边缘)。而Laplacian算子是计算的二阶导(如果对像素值求二阶导数,会发现边缘处的导数值为0),它每个元素的二阶导数通过Sobel算子计算得到。
dst = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
前两个是必须的参数:
src:需要处理的图像;
ddepth: 图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;
其后是可选的参数:
dst: 输出内容;
ksize:算子的大小,必须为1、3、5、7。默认为1。
scale是缩放导数的比例常数,默认情况下没有伸缩系数;
delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT
注意,但是如果在sobel算子中设置ksize = 1, 直接使用以下内核用于过滤:
下面的代码显示了单个图表中的所有算子。所有内核都是5x5大小。输出图像的深度通过-1得到结果的np.uint8型。
# laplacian 算子
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('sudo.png', 0)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
plt.subplot(1,2,1)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])
plt.subplot(1,2,2)
plt.imshow(laplacian, cmap='gray')
plt.title('Laplacian')
plt.xticks([])
plt.yticks([])
plt.show()
如果输出数据类型为cv2.CV_8U
或np.uint8
, 则会有一个小问题。黑色到白色的过渡被视为正斜率(具有正值),而白色到黑色的过渡被视为负斜率(具有负值)。因此,当数据转换为np.uint8
时,所有负斜率均设为零。简而言之,会错过这一边缘信息。
如果要检测两个边缘,更好的选择是将输出数据类型保留为更高的形式,例如**cv2.CV_16S,cv2.CV_64F
等,取其绝对值,然后转换回cv2.CV_8U
**。
下面的代码演示了用于水平Sobel滤波器和结果差异的此过程
# cv2.CV_8U vs cv2.CV_64F
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('box.png', 0)
sobelx_8u = cv2.Sobel(img, cv2.CV_8U, 1, 0, ksize=5) # Output dtype = cv.CV_8U
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx_64f = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
abs_sobel64f = np.absolute(sobelx_64f)
sobelx_8u = np.uint8(sobelx_8u)
plt.subplot(1,3,1)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])
plt.subplot(1,3,2)
plt.imshow(sobelx_8u, cmap='gray')
plt.title('Sobel CV_8U')
plt.xticks([])
plt.yticks([])
plt.subplot(1,3,3)
plt.imshow(sobelx_64f, cmap='gray')
plt.title('Sobel CV_64F')
plt.xticks([])
plt.yticks([])
plt.show()