OpenCV学习笔记(10)——图像梯度
- 学习图像梯度,图像边界等
梯度简单来说就是求导。
OpenCV提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,Scharr和Lapacian。Sobel,Scharr其实就是求一阶或二阶导。Scharr是对Sobel的部分优化。Laplacian是求二阶导。
1.Sobel算子和Scharr算子
Sobel算子是高斯平滑和微分操作的结合体,所以他的抗噪声能力很好。你可以设定求导的方向(xorder 或 yorder)。还可以设定使用的卷积核大小(ksize)。当ksize=-1时,会使用3*3 的Scharr滤波器,他的效果要比3*3的Sobel滤波器好,而且速度相同,所以在使用3*3滤波器时应该尽量使用Scharr滤波器(一般就用Sobel算子即可)。3*3 的 Scharr滤波器卷积核如下所示:
2.Laplacian算子
拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二阶Sobel导数。事实上,OpenCV在计算拉普拉斯算子时直接调用Sobel算子,具体计算公式如下:
拉普拉斯滤波器使用的卷积核:
下面的例程将展示三种滤波器对同一副图像进行操作产生的效果,其使用的卷积核大小都是5*5的
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('7.png',0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)#注意这里的depth参数!
#cv2.CV_64F 输出图像的深度(数据类型),可以使用-1,与原图像保持一致
sobelx = cv2.Sobel(img, cv2.CV_64F,1,0,ksize=5)
#参数1,0表示在x方向求一阶导数,最大可以求2阶导数
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
#同理0,1表示在y方向求一阶导数,最大可以求二阶导数
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
书上的例图更为明显的表现出区别:
这里解释为什么要用cv2.CV_64F,当使用-1(或者cv2.CV_8U)(与原图深度(数据类型)保持一致)时,输出的图像如下
laplacian sobelx
想象一下一个从黑到白的边界的导数し整数,而一个从白到黑的边界点的导数却是负数。如果原图像的深度是np.int8时,所有的负值都会截断成为0,换句话说就是把边界丢失掉了。因此如果想把两种边界都检测到,最好的办法就是将输出的数据类型设置到更高,如cv2.CV_16S,cv2.CV_64F等,取绝对值然后再把它转回到cv2.CV_8U(即把本来为负的部分转为正的,在转回uint8便可以显示)