Python+opencv学习记录16:图像梯度

1.图像梯度

梯度从微积分的角度来说就是求导,即:
在这里插入图片描述
在图像处理中,梯度常被用于提取图像边缘,经典的图像梯度算法是考虑图像的每个像素的某个邻域内的灰度变化,利用边缘临近的一阶或二阶导数变化规律,对原始图像中像素某个邻域设置梯度算子,通常我们用小区域模板进行卷积来计算。
Opencv提供了三种类型的梯度滤波器或高通滤波器,分别是Sobel,Scharr和Laplacian。

2.一阶导数与Sobel算子

图像提取边缘时我们可以求图像函数的一阶导数,其示意图为:
在这里插入图片描述
可以看出在图像函数变化的过程中,其导函数有最大值,所以我们通过找到最大值来寻找图像边缘。
Sobel算子是高斯平滑加分散操作的联合,因此更能抵抗噪音。 我们可以指定要采取的导数的方向,垂直方向或水平方向(分别为参数yorder和xorder)。
其代码为:

def sobel_demo(image):          # Sobel算子
    grad_x = cv.Sobel(image, cv.CV_32F, 1, 0)       # 对x求一阶导
    grad_y = cv.Sobel(image, cv.CV_32F, 0, 1)       # 对y求一阶导
    gradx = cv.convertScaleAbs(grad_x)              # 用convertScaleAbs()函数将其转回原来的uint8形式
    grady = cv.convertScaleAbs(grad_y)

    cv.imshow("gradient-x", gradx)
    cv.imshow("gradient-y", grady)

    gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
    cv.imshow("gradient", gradxy)

原图为:
在这里插入图片描述
x方向的梯度为:
在这里插入图片描述
y方向的梯度为:
在这里插入图片描述
x和y方向的梯度按照1:1的比例混合后:
在这里插入图片描述
Scharr算子是对Sobel算子的优化,其代码为:

	grad_x = cv.Scharr(image, cv.CV_32F, 1, 0)       # 对x求一阶导(Sobel算子的增强版)
    grad_y = cv.Scharr(image, cv.CV_32F, 0, 1)       # 对y求一阶导(Sobel算子的增强版)

x方向的梯度为:
在这里插入图片描述
y方向的梯度为:
在这里插入图片描述
经混合后的图像为:
在这里插入图片描述

3.二阶导数与Laplacian算子

在二阶导数的时候,最大变化处的值为零即边缘是零。通过二阶导数计算,在这里插入图片描述
依据此理论我们可以计算图像二阶导数,提取边缘。
其图解为:
在这里插入图片描述
在这里插入图片描述
在使用拉普拉斯算子时,代码如下:

def Lapalace_demo(image):       # 拉普拉斯算子
    dst = cv.Laplacian(image, cv.CV_32F)
    lpls = cv.convertScaleAbs(dst)
    cv.imshow("Lapalace_demo", lpls)

短短4行代码得到的效果为:
在这里插入图片描述
除了使用Laplacian这个API外,我们还可以自定义拉普拉斯算子,其代码为:

def Lapalace_demo(image):       # 拉普拉斯算子
    # 自定义拉普拉斯算子
    kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
    dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
    lpls = cv.convertScaleAbs(dst)
    cv.imshow("Lapalace_demo", lpls)

其中kernel是我们自定义的一个核,得到的效果为:
在这里插入图片描述
我们可以修改核的大小,修改为:

kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])

此时的效果为:
在这里插入图片描述
可以看出修改后的结果边缘更加明显,并且Opencv自带的拉普拉斯算子默认的核为我们自定义的第一个核。
注:我们的sobel函数的第二个参数(数据类型)会换成cv2.CV_16S或cv2.CV_64F,最后再变回uint8。这是因为从黑到白的边界点的导数是正数,而从白到黑是负数,如果还是使用uint8,那么所有的负数都会变为0,即被截断。

完整代码

import cv2 as cv                # 导入opencv模块
import numpy as np              # 导入数学函数库


def Lapalace_demo(image):       # 拉普拉斯算子
    # dst = cv.Laplacian(image, cv.CV_32F)
    # lpls = cv.convertScaleAbs(dst)
    # 自定义拉普拉斯算子
    # kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
    kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])
    dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
    lpls = cv.convertScaleAbs(dst)
    cv.imshow("Lapalace_demo", lpls)


def sobel_demo(image):          # Sobel算子和Scharr算子
    # grad_x = cv.Sobel(image, cv.CV_32F, 1, 0)       # 对x求一阶导
    # grad_y = cv.Sobel(image, cv.CV_32F, 0, 1)       # 对y求一阶导
    grad_x = cv.Scharr(image, cv.CV_32F, 1, 0)       # 对x求一阶导(Sobel算子的增强版)
    grad_y = cv.Scharr(image, cv.CV_32F, 0, 1)       # 对y求一阶导(Sobel算子的增强版)
    gradx = cv.convertScaleAbs(grad_x)              # 用convertScaleAbs()函数将其转回原来的uint8形式
    grady = cv.convertScaleAbs(grad_y)

    cv.imshow("gradient-x", gradx)
    cv.imshow("gradient-y", grady)

    gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
    cv.imshow("gradient", gradxy)


print("------------hello python!------------")

src = cv.imread("D:/opencv3/image/snow girl5.png")
cv.namedWindow("input_image", cv.WINDOW_AUTOSIZE)
cv.imshow("input_image", src)
# sobel_demo(src)
Lapalace_demo(src)

cv.waitKey(0)
cv.destroyAllWindows()          # 释放所有窗口

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值