目录
????????图像的梯度是指图像中每个像素点的强度变化情况。计算图像的梯度可以帮助我们了解图像中物体的边界和纹理等信息。梯度在计算机视觉和图像处理领域有着广泛的应用,以下是一些常见的应用场景:
????????梯度简单来说就是求导。OpenCV 提供了三种不同的梯度滤波器,或者说是高通滤波器,分别是Sobel,Scharr 和Laplacian。Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对Sobel(使用小的卷积核求解梯度角度梯度角度时的优化)。Laplacian 是求二阶导数。
????????Sobel算子和Scharr算子都是常用的图像梯度计算算子,用于边缘检测和图像增强等任务。它们可以计算图像在水平和垂直方向上的梯度,并且可以根据梯度的大小来判断图像中的边缘。
????????Sobel算子是一种离散的差分算子,可以用于计算图像的一阶导数。它分为水平和垂直两个方向的算子,分别表示为Sobel_x和Sobel_y。这两个算子可以通过卷积运算来计算图像在水平和垂直方向上的梯度。Sobel算子的计算公式如下:
Sobel_x = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]
Sobel_y = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]]
????????Scharr算子是Sobel算子的一种改进版本,它使用了一种更加精确的差分算法,可以提供更好的梯度近似。Scharr算子也分为水平和垂直两个方向的算子,分别表示为Scharr_x和Scharr_y。Scharr算子的计算公式如下:
Scharr_x = [[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]]
Scharr_y = [[-3, -10, -3], [0, 0, 0], [3, 10, 3]]
使用Sobel算子和Scharr算子可以通过卷积运算来计算图像的梯度。具体步骤如下:
3x3 的Scharr 滤波器卷积核如下:?
????????Laplacian算子是一种常用的图像二阶导数算子,用于图像增强、边缘检测和特征提取等任务。它可以通过计算图像的二阶导数来检测图像中的边缘和纹理特征。
Laplacian算子的计算公式如下:
Laplacian = [[0, 1, 0], [1, -4, 1], [0, 1, 0]]
使用Laplacian算子可以通过卷积运算来计算图像的二阶导数。具体步骤如下:
下面的代码分别使用以上三种滤波器对同一幅图像操作。使用的卷积核是5x5 的。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
img=cv2.imread('dave.jpg',0)
#cv2.CV_64F 输出图像的深度(数据类型)可以使用-1, 与原图像保持一致np.uint8
laplacian=cv2.Laplacian(img,cv2.CV_64F)
# 参数1,0 为只在x 方向求一阶导数最大可以求2阶导数。
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
# 参数0,1 为只在y 方向求一阶导数最大可以求2阶导数。
sobely=cv2.Sobel(img,cv2.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()
效果如下:
????????在查看上面这个例子的注释时不知到你有没有注意到,我们可以使用参数-1 来设定输出图像的深度(数据类型)与原图像保持一致,但是我们在代码中使用的却是cv2.CV_64F。这是为什么呢?想想一下一个从黑到白的边界的导数是正数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是np.int8 时,所有的负值会被截断变成0,换句话说就就是把边界信息丢失掉所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型设置的更高,比cv2.CV_16S,cv2.CV_64F 等。取绝对值然后再把它转回到cv2.CV_8U。下面的示例演示了输出图片的深度不同造成的不同效果。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('boxs.png',0)
# Output dtype = cv2.CV_8U
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# 也可以将参数为-1
#sobelx8u = cv2.Sobel(img,-1,1,0,ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()