opencv-python-学习笔记十三(图像梯度)
什么是梯度?梯度简单来说就是求导数。根据导数来检测图像的边缘。
本文使用的参数
src | input image. | ||||||||||
dst | 与输入图像具有相同大小,相同通道数的输出图像 | ||||||||||
ddepth |
输出图像的深度, see combinations; 针对不同输入图像,有不同深度的输出图像,具体如下
|
||||||||||
dx | x方向求导阶数,所以可以使dx=1,dy=0实现x方向求导 | ||||||||||
dy | y方向求导阶数,所以可以使dx=0,dy=1实现y方向求导 | ||||||||||
ksize | 内核大小,必须取 1, 3, 5, or 7. | ||||||||||
scale | 缩放大小,默认1 | ||||||||||
delta | 增量数值,默认0。optional delta value that is added to the results prior to storing them in dst. | ||||||||||
borderType | 边界类型默认BORDER_DEFAULT, see BorderTypes |
原理
OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,Scharr 和 Laplacian。
Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对 Sobel(使用 小的卷积核求解求解梯度角度时)的优化。Laplacian 是求二阶导数。
1.Sobel and Scharr Derivatives
sobel是高斯平滑加微分的联合运算,所以它对噪声具有良好的抵抗力。你可以设定求导的方向(xorder 或 yorder)。还可以设定使用的卷积核的大小(ksize)。
函数:
dst=cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
函数介绍:
使用扩展的Sobel算子计算第一、第二、第三或混合图像导数。除一种情况外,所有情况均采用ksize×ksize可分核计算导数。当ksize = 1时,使用
3×1 or 1×3核(也就是说,没有高斯平滑),ksize = 1只能用于第一个或第二个x-或y-导数。还有一个特殊的值ksize =-1时,会使用 3x3 的 Scharr 滤波器,它的的效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器),此时也可以使用cv2.Laplacian(src, ddepth, dst, ksize, scale, delta, borderType)函数。
Sobel算子将高斯平滑和微分相结合,结果对噪声有一定的抑制作用。通常,函数用(xorder = 1, yorder = 0, ksize = 3)或(xorder = 0, yorder = 1, ksize = 3)来计算第一个x或y图像导数
第一种情况对应于:
第二种情况对应于:
2. Laplacian Derivatives
拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二阶 Sobel 导数,事实上,OpenCV 在计算拉普拉斯算子时直接调用 Sobel 算 子。计算公式如下:
如果ksize = 1,则使用以下内核进行过滤
函数:
dst=cv.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
函数介绍:
该函数通过将使用Sobel算子计算得到的第二个x、y导数相加,计算出源图像的拉普拉斯算子:
这是在ksize > 1时完成的。当ksize == 1时,通过对如下3×3孔径图像进行滤波计算拉普拉斯矩阵:
举例
import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('1.jpg', 0) laplacian = cv.Laplacian(img, cv.CV_64F) sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5) sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=5) 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()
想象一下一个从黑到白的边界 的导数是整数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是 np.int8 时,所有的负值都会被截断变成 0,换句话说就是把把边界丢失掉。 所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型设置的更高,比如 cv2.CV_16S,cv2.CV_64F 等。取绝对值然后再把它转回到 cv2.CV_8U。下面的示例演示了输出图片的深度不同造成的不同效果。