opencv-python-学习笔记十三(图像梯度)

 

什么是梯度?梯度简单来说就是求导数。根据导数来检测图像的边缘。

本文使用的参数

src input image.                                                                                                                          
dst 与输入图像具有相同大小,相同通道数的输出图像
ddepth

输出图像的深度, see combinations; 针对不同输入图像,有不同深度的输出图像,具体如下

Input depth (src.depth())Output depth (ddepth)
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16U/CV_16S -1/CV_32F/CV_64F                                                                                                
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F                      
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。下面的示例演示了输出图片的深度不同造成的不同效果。

 

posted @ 2019-07-29 18:18  少灬儿郎  Views(596)  Comments(0Edit  收藏  举报