OpenCV-Python——第14章:图像梯度
发布日期:2021-06-29 01:51:45 浏览次数:4 分类:技术文章

本文共 3475 字,大约阅读时间需要 11 分钟。

目录

              


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 cv2import numpy as npfrom matplotlib import pyplot as pltimg = 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同样可以实现scharrsobely = 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 cv2import numpy as npfrom matplotlib import pyplot as pltimg1 = 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()

结果如下:

 

转载地址:https://blog.csdn.net/yukinoai/article/details/87533036 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:OpenCV-Python——第15章:Canny边缘检测
下一篇:树莓派(Raspberry Pi)——利用原装摄像头实现简单圆形检测

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月02日 11时57分48秒