opencv图像处理之图像梯度

1.原理

梯度简单来说就是求导。OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:SobelScharrLaplacian
Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对Sobel的优化(使用小的卷积核求解求解梯度角度时)。Laplacian 是求二阶导数。

2.Sobel算子

  • cv.Sobel()
sobelx=cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=5)

参数按顺序为:原图、输出图像的深度、x方向求导的阶数、y方向求导的阶数、卷积核大小。
Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。如果ksize=-1,会使用3x3 的Scharr 滤波器,它的的效果要比3x3 的Sobel 滤波器好(而且速度相同,所以在使用3x3 滤波器时应该尽量使用Scharr 滤波器)。3x3 的Scharr 滤波器卷积核如下:
在这里插入图片描述

3.Laplacian算子

  • cv.Laplacian()
laplacian=cv2.Laplacian(gray,cv2.CV_64F)

拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二阶Sobel 导数,事实上,OpenCV 在计算拉普拉斯算子时直接调用Sobel 算子。

4.代码和效果

def gradient(self):
    gray=cv2.imread(self.infile,0)
    #参数1,0为只在x方向求一阶导数,最大可以求2阶导数。y方向同理。
    #参数cv2.CV_64F结果图像的深度,可以使用-1,与原图像保持一致(np.uint8)
    sobelx=cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=5)
    sobely=cv2.Sobel(gray,cv2.CV_64F,0,1,ksize=5)
    laplacian=cv2.Laplacian(gray,cv2.CV_64F)

    titles = ['raw', 'sobelx','sobely','laplacian']
    images = [gray, sobelx,sobely,laplacian]
    for i in range(4):
        plt.subplot(1,4,i+1),plt.imshow(images[i],'gray')
        plt.title(titles[i])
        plt.xticks([]),plt.yticks([])
    plt.show()

在这里插入图片描述
下面打印了部分gray和sobelx的像素值。有点不太明白为什么出现大片的灰色。超出255和小于0的值是怎么处理的呢?

[[ 98 122 98 … 120 97 98]
[113 206 229 … 246 199 122]
[ 98 234 255 … 255 237 110]

[121 248 255 … 254 234 109]
[ 99 198 226 … 232 216 121]
[105 129 103 … 111 120 80]]
[[ 0. 2804. 1940. … -1938. -3142. 0.]
[ 0. 3515. 2286. … -2230. -3667. 0.]
[ 0. 4563. 2669. … -2555. -4429. 0.]

[ 0. 4397. 2688. … -2617. -4417. 0.]
[ 0. 3459. 2343. … -2115. -3467. 0.]
[ 0. 2854. 2036. … -1762. -2894. 0.]]

我们应该知道为什么要扩大图像的深度,而不采用原来的深度呢?

想象一下一个从黑到白的边界的导数是正数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是np.int8 时,所有的负值都会被截断变成0,换句话说就是把从白到黑边界丢失掉。所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型设置的更高,比如cv2.CV_16S,cv2.CV_64F 等。取绝对值然后再把它转回到cv2.CV_8U。

如下图所示:直接用-1为参数,从白色到黑色的边界被丢弃了。而先扩深度,再去绝对值并减小深度,是可以检测出从白色到黑色的边界的。
在这里插入图片描述
(1) 此时若把我的代码中的cv2.CV_64F都改为-1,得到结果如下:

sobelx=cv2.Sobel(gray,-1,1,0,ksize=5)
sobely=cv2.Sobel(gray,-1,0,1,ksize=5)
laplacian=cv2.Laplacian(gray,-1)

在这里插入图片描述
(2)若我把得到的图像先取绝对值,再把数据深度转为和原来相同的。

sobelx=np.uint8(np.abs(cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=5)))
sobely=np.uint8(np.abs(cv2.Sobel(gray,cv2.CV_64F,0,1,ksize=5)))
laplacian=np.uint8(np.abs(cv2.Laplacian(gray,cv2.CV_64F)))

在这里插入图片描述

5.怎么理解用卷积核来检测边界呢?

假设一个3x3的图像(白黑白): [ 255 0 255 255 0 255 255 0 255 ] \begin{bmatrix} 255&0&255\\ 255&0&255\\ 255&0&255\\ \end{bmatrix} 和Sobel的x方向的卷积核 [ 3 0 3 10 0 10 3 0 3 ] \begin{bmatrix} -3&0&3\\ 10&0&10\\ -3&0&3\\ \end{bmatrix} 运算后,结果如下:
[ 0 5100 0 0 5100 0 0 5100 0 ] \begin{bmatrix} 0&5100&0\\ 0&5100&0\\ 0&5100&0\\ \end{bmatrix} (黑白黑)。左右两侧为0是因为原图的中间一列为0。中间的5100是因为卷积核的4个角正负抵消,只和10做了计算。为什么它是x方向的呢,可以想象到因为在水平方向上变化比较大,所以两侧的梯度比较大。

若[白黑白] T ^T 的一个图像: [ 255 255 255 0 0 0 255 255 255 ] \begin{bmatrix}255&255&255\\0&0&0\\255&255&255\\\end{bmatrix} 和x方向卷积核运算后,结果为: [ 2550 5100 2550 0 0 0 2550 5100 2550 ] \begin{bmatrix} 2550&5100&2550\\ 0&0&0\\ 2550&5100&2550\\ \end{bmatrix} 。可以看到黑白并没有什么变化,也就是说并不能检测出边界来。

发布了132 篇原创文章 · 获赞 40 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_36622009/article/details/104544490