opencv13:图像梯度

目标

在本章中,将学习:

  • 查找图像梯度,边缘等
  • 学习以下函数:
    • cv2.Sobel()
    • cv2.Scharr()
    • cv2.Laplacian()

理论

1)边缘:灰度或结构等信息的突变处,边缘是一个区域的结束,也是另一个区域的开始,利用该特征可以分割图像

2)边缘点:图像中具有坐标[x,y],且处在强度显著变化的位置上的点。

3)边缘段:对应于边缘点坐标[x,y]及其方位 ,边缘的方位可能是梯度角

OpenCV提供三种类型的梯度滤波器或高通滤波器,即Sobel,Scharr和Laplacian

Sobel 算子

索贝尔算子(Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。

Sobel算子是高斯平滑加微分运算的联合运算,因此它更抗噪声

在这里插入图片描述
在这里插入图片描述
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高当对精度要求不是很高时,是一种较为常用的边缘检测方法

函数cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]])
参数:
src:输入图像
ddepth: 输出图像的深度(可以理解为数据类型),-1表示与原图像相同的深度
dx,dy:当组合为dx=1,dy=0时求x方向的一阶导数,当组合为dx=0,dy=1时求y方向的一阶导数(如果同时为1,通常得不到想要的结果)
ksize:(可选参数)Sobel算子的大小**,必须是1,3,5或者7,默认为3**。求X方向和Y方向一阶导数时,卷积核分别为:
scale:(可选参数)将梯度计算得到的数值放大的比例系数,效果通常使梯度图更亮,默认为1
delta:(可选参数)在将目标图像存储进多维数组前,可以将每个像素值增加delta,默认为0
borderType:(可选参数)决定图像在进行滤波操作(卷积)时边沿像素的处理方式,默认为BORDER_DEFAULT
返回值:梯度图

# sobel 算子
import cv2
import numpy
from matplotlib import pyplot as plt
img = cv2.imread('sudo.png', 0)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
sobelxy =  cv2.addWeighted(sobelx,0.5, sobely, 0.5, 0)  

plt.subplot(1,4,1)
plt.imshow(img,cmap = 'gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])

plt.subplot(1,4,2)
plt.imshow(sobelx, cmap='gray')
plt.title('sobelx')
plt.xticks([])
plt.yticks([])

plt.subplot(1,4,3)
plt.imshow(sobely, cmap='gray')
plt.title('sobely')
plt.xticks([])
plt.yticks([])

plt.subplot(1,4,4)
plt.imshow(sobelxy, cmap='gray')
plt.title('sobelxy')
plt.xticks([])
plt.yticks([])
plt.show()

在这里插入图片描述

注意:如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,该效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器)。

Scharr 算子

scharr算子与sobel算子基本一样,只是算子里面运算核参数有差别,如下图所示。Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化
在这里插入图片描述

dst=cv2.Scharr(src,ddpetph,dx,dy)
dst:处理结果
src:源图像
ddpetph:图像深度
dx:x轴方向,
dy:y轴方向

# scharr 算子
import cv2
import numpy
from matplotlib import pyplot as plt
img = cv2.imread('sudo.png', 0)
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharrxy = cv2.addWeighted(scharrx,0.5, scharry, 0.5, 0)  

plt.subplot(1,4,1)
plt.imshow(img,cmap = 'gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])

plt.subplot(1,4,2)
plt.imshow(scharrx, cmap='gray')
plt.title('Scharrx')
plt.xticks([])
plt.yticks([])

plt.subplot(1,4,3)
plt.imshow(scharry, cmap='gray')
plt.title('Scharry')
plt.xticks([])
plt.yticks([])

plt.subplot(1,4,4)
plt.imshow(scharrxy, cmap='gray')
plt.title('Scharrxy')
plt.xticks([])
plt.yticks([])
plt.show()

在这里插入图片描述

Laplacian 算子

Sobel算子是求一阶导数,获得图像像素值变化部分信息(在其一阶导数在边缘位置为极值,这就是Sobel算子使用的原理——极值处就是边缘)。而Laplacian算子是计算的二阶导(如果对像素值求二阶导数,会发现边缘处的导数值为0),它每个元素的二阶导数通过Sobel算子计算得到。
在这里插入图片描述

dst = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
前两个是必须的参数:
src:需要处理的图像;
ddepth: 图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;
其后是可选的参数:
dst: 输出内容;
ksize:算子的大小,必须为1、3、5、7。默认为1。
scale是缩放导数的比例常数,默认情况下没有伸缩系数;
delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT

注意,但是如果在sobel算子中设置ksize = 1, 直接使用以下内核用于过滤:
在这里插入图片描述

下面的代码显示了单个图表中的所有算子。所有内核都是5x5大小。输出图像的深度通过-1得到结果的np.uint8型。

# laplacian 算子
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('sudo.png', 0)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
plt.subplot(1,2,1)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])

plt.subplot(1,2,2)
plt.imshow(laplacian, cmap='gray')
plt.title('Laplacian')
plt.xticks([])
plt.yticks([])

plt.show()

在这里插入图片描述

注意

如果输出数据类型为cv2.CV_8Unp.uint8, 则会有一个小问题。黑色到白色的过渡被视为正斜率(具有正值),而白色到黑色的过渡被视为负斜率(具有负值)。因此,当数据转换为np.uint8时,所有负斜率均​​设为零。简而言之,会错过这一边缘信息。

如果要检测两个边缘,更好的选择是将输出数据类型保留为更高的形式,例如**cv2.CV_16S,cv2.CV_64F等,取其绝对值,然后转换回cv2.CV_8U**。

下面的代码演示了用于水平Sobel滤波器和结果差异的此过程

# cv2.CV_8U vs cv2.CV_64F

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('box.png', 0)
sobelx_8u = cv2.Sobel(img, cv2.CV_8U, 1, 0, ksize=5)  # Output dtype = cv.CV_8U
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx_64f = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)  
abs_sobel64f = np.absolute(sobelx_64f)
sobelx_8u = np.uint8(sobelx_8u)

plt.subplot(1,3,1)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])

plt.subplot(1,3,2)
plt.imshow(sobelx_8u, cmap='gray')
plt.title('Sobel CV_8U')
plt.xticks([])
plt.yticks([])

plt.subplot(1,3,3)
plt.imshow(sobelx_64f, cmap='gray')
plt.title('Sobel CV_64F')
plt.xticks([])
plt.yticks([])

plt.show()

在这里插入图片描述

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncle_ll

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值