OpenCV-Python——第14章:图像梯度

目录

原理    Sobel 算子和 Scharr 算子     Laplacian 算子     举例


0 原理

梯度简单来说就是求导,为什么对图像进行求导是重要的呢? 假设我们需要检测图像中的边缘。

假设我们有一张一维图形。下图中灰度值的”跃升”表示边缘的存在:

 使用一阶微分求导我们可以更加清晰的看到边缘”跃升”的存在(这里显示为高峰值)

OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,Scharr 和 Laplacian。

Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对 Sobel(使用 小的卷积核求解求解梯度角度时)的优化。Laplacian 是求二阶导数。

1 Sobel 算子和 Scharr 算子

Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。你可以设定求导的方向(xorder 或 yorder)。还可以设定使用的卷积核的大小(ksize)。

cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)

  • src:输入图像
  • ddepth:图像的颜色深度,针对不同的输入图像,输出目标图像有不同的深度,具体组合如下: 

若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F 

若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F 

若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F 

若src.depth() = CV_64F, 取ddepth = -1/CV_64F 

-1表示与图像深度一致

  • dx:x方向求导阶数,所以可以使dx=1,dy=0实现x方向求导
  • dy:y方向求导阶数,所以可以使dx=0,dy=1实现y方向求导
  • dst:输出图像
  • ksize:内核大小常取1,3,5,7等奇数
  • scale:缩放大小,默认1
  • delta:增量数值,默认0
  • borderType:边界类型默认BORDER_DEFAULT

如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,它的的效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器)。也可以使用下面的函数

Scharr(src, ddepth, dx, dy, dst, scale, delta, borderType) 

  • 参数含义同上

2 Laplacian 算子

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

cv2.Laplacian(src, ddepth, dst, ksize, scale, delta, borderType)

  • 参数含义同上

3 举例

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('test19.jpg', 0)

laplacian = cv2.Laplacian(img, cv2.CV_8U)

sobel = cv2.Sobel(img, cv2.CV_8U, 1, 1, ksize=5)
sobelx = cv2.Sobel(img, cv2.CV_8U, 1, 0, ksize=5)
sobelx1 = cv2.Sobel(img, cv2.CV_8U, 1, 0, ksize=-1)  # ksize=-1同样可以实现scharr
sobely = cv2.Sobel(img, cv2.CV_8U, 0, 1, ksize=5)

scharr = cv2.Scharr(img, cv2.CV_8U, 0, 1)
scharrx = cv2.Scharr(img, cv2.CV_8U, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_8U, 0, 1)

plt.subplot(241), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(242), plt.imshow(laplacian, cmap='gray'), plt.title('Laplacian')
plt.subplot(243), plt.imshow(sobel, cmap='gray'), plt.title('Sobel')
plt.subplot(244), plt.imshow(sobelx, cmap='gray'), plt.title('Sobel X')
plt.subplot(245), plt.imshow(sobelx1, cmap='gray'), plt.title('Scharr X')
plt.subplot(246), plt.imshow(scharrx, cmap='gray'), plt.title('Scharr X')
plt.subplot(247), plt.imshow(sobely, cmap='gray'), plt.title('Sobel Y')
plt.subplot(248), plt.imshow(scharry, cmap='gray'), plt.title('Scharr Y')

plt.show()

结果如下:

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

import cv2
import numpy as np
from matplotlib import pyplot as plt

img1 = cv2.imread('test8.jpg', 0)
img2 = cv2.imread('test19.jpg', 0)

sobelx8u1 = cv2.Sobel(img1, cv2.CV_8U, 1, 0, ksize=5)
sobelx64f1 = cv2.Sobel(img1, cv2.CV_64F, 1, 0, ksize=5)
abs_sobel64f1 = np.absolute(sobelx64f1)
sobel_8u1 = np.uint8(abs_sobel64f1)

sobelx8u2 = cv2.Sobel(img2, cv2.CV_8U, 1, 0, ksize=5)
sobelx64f2 = cv2.Sobel(img2, cv2.CV_64F, 1, 0, ksize=5)
abs_sobel64f2 = np.absolute(sobelx64f2)
sobel_8u2 = np.uint8(abs_sobel64f2)

plt.subplot(231), plt.imshow(img1, cmap='gray'), plt.title('Original')
plt.subplot(232), plt.imshow(sobelx8u1, cmap='gray'), plt.title('Sobel CV_8U')
plt.subplot(233), plt.imshow(sobel_8u1, cmap='gray'), plt.title('Sobel abs(CV_64F)')
plt.subplot(234), plt.imshow(img2, cmap='gray'), plt.title('Original')
plt.subplot(235), plt.imshow(sobelx8u2, cmap='gray'), plt.title('Sobel CV_8U')
plt.subplot(236), plt.imshow(sobel_8u2, cmap='gray'), plt.title('Sobel abs(CV_64F)')

plt.show()

结果如下:

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值