概念:
梯度简单来说就是求导,在图像上表现出来的就是提取图像的边缘(不管是横向的、纵向的、斜方向的等等),所需要的无非也是一个核模板,模板的不同结果也不同。所以可以看到,所有的这些个算子函数,归结到底都可以用函数cv2.filter2D()来表示,不同的方法给予不同的核模板,然后演化为不同的算子而已。
OpenCV提供了三种梯度过滤器(高通过滤器)-Sobel、Scharr、Laplacian。
一、Sobel算子与Scharr算子
Sobel算子是高斯平滑与微分操作的结合体,所以其抗噪能力很强,用途较多。一般的sobel算子包括x与y两个方向,算子模板为:
在opencv函数中,还可以设置卷积核(ksize)的大小,如果ksize=-1,就演变为3*3的Scharr算子,模板无非变了个数字:
二、Laplacian(拉普拉斯)算子
其模板为:
三、Sobel算子函数
函数原型:
dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
参数:
前四个是必须的参数:
1. src - 需要处理的图像
2. ddepth - 图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度
3. dx - 对x轴方向求导的阶数,一般为0、1、2,其中0表示这个方向上没有求导
4. dy - 对y轴方向求导的阶数,一般为0、1、2,其中0表示这个方向上没有求导
其后是可选的参数:
5. dst - 目标图像
6. ksize - Sobel算子的大小,必须为1、3、5、7
7. scale - 缩放导数的比例常数,默认情况下没有伸缩系数
8. delta - 可选增量, 将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中
9. borderType - 图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT
四、简单实现
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
if __name__ == '__main__':
img = cv.imread('1.png',0)
# cv2.IMREAD_COLOR:默认参数,读入一副彩色图片,忽略alpha通道,可以直接写1
# cv2.IMREAD_GRAYSCALE:读入灰度图片,可以直接写0
# cv2.IMREAD_UNCHANGED:顾名思义,读入完整图片,包括alpha通道,可以直接写-1
# laplacion 梯度
laplacian_64f = cv.Laplacian(img,cv.CV_64F)
# 在经过处理后,需要用convertScaleAbs()函数将其转回原来的uint8形式,否则将无法显示图像,而只是一副灰色的窗口
laplacian_8u = cv.convertScaleAbs(laplacian_64f)
# abs_laplacian_64f = np.absolute(laplacian_64f)
# # 转换为cv.CV_8U
# laplacian_8u = np.uint8(abs_laplacian_64f)
# Sobel 梯度 x 方向
sobelx_64f = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobelx_8u = cv.convertScaleAbs(sobelx_64f)
# Sobel 梯度 y 方向
sobely_64f = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
sobely_8u = cv.convertScaleAbs(sobely_64f)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.subplot(2,2,2),plt.imshow(laplacian_8u,cmap = 'gray')
plt.subplot(2,2,3),plt.imshow(sobelx_8u,cmap = 'gray')
plt.subplot(2,2,4),plt.imshow(sobely_8u,cmap = 'gray')
plt.show()
输出结果:
五、小结
在上面的例子中(original图),(imshow)输出数据类型(图像深度)是cv2.CV_8U或者是np.uint8。
但是由于输出数据类型[python-灰度:0是黑、1是白],当有正梯度的时候,会出现黑白变换,同样,当有负梯度的时候,会导致白黑变换。因此,当输出图像深度为np.uint8时,python 会让所有的负梯度自动变为0。简单的说就是丢失边缘。
如果想要获得所有的边缘,就需要提高图像深度,将输出图像数据类型变成更高格式,比如cv2.CV_16S或者cv2.CV_64F等,然后取绝对值,再转换为cv2.CV_8U。下面的代码将展示这个流程,对一副图片进行水平Sobel过滤,不同的图像深度会导致不同的结果。