Opencv-Python学习笔记(六):图像梯度计算

  • 本篇记录学习图像梯度的计算。
  • 查找图像渐变,边缘等
  • 将学习以下函数:cv2.Sobel()cv2.Scharr()cv2.Laplacian()

原理:

梯度简单来说就是求导。OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器: SobelScharrLaplacian。
Sobel, Scharr 其实就是求一阶或二阶导数。 Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化。 Laplacian 是求二阶导数。

Sobel 算子和 Scharr 算子:

Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。我们可以设定求导的方向( xorder 或 yorder)。

      G_{X}=\begin{bmatrix} -1 &0 &+1 \\ -2&0 &+2\\ -1&0 &+1 \end{bmatrix}\ast A     G_{Y}=\begin{bmatrix} -1& -2 &-1 \\ 0&0 & 0\\ +1& +2 &+1 \end{bmatrix}\ast A       

水平梯度=右-左;垂直梯度=下-上。我们以下图为例进行展示:

在opencv3中,看老师视频会讲到要取绝对值运算,即要用到cv.convertScaleAbs()函数,因为水平梯度=右-左,如图

因此输出应该是只有左半部分一个半圆弧,右边会被截断。但是我在使用opencv4进行计算的时候,发现左右都有一个圆弧,(猜测原因是opencv4进行了改进)代码及结果如下:

垂直梯度运算同样opencv3和人 opencv4运算结果不一样。

但是,如果图像深度和原图像保持一致,即cv.Sobel(src, -1,1, 0, ksize=3)函数的第二个参数填入 -1,结果就会出现截断情况,如图所示:

在进行梯度运算是水平和垂直要分别进行计算,最后在进行合成,用公式:G=\sqrt{G_{X}^{2}+G_{Y}^{2}},不建议进行一块运算,原因如下图:

整体运算:

分开运算-->再合成:

# -*- coding:utf-8 -*-
# Author : MMagicLoren
# @Email : [email protected]
# @Time : 2019/10/13 15:59
# @File : 图像梯度计算.py
# @Project : Workspace
import cv2 as cv


def sobel_demo(image):
    grad_x = cv.Sobel(src, cv.CV_64F, 1, 0, ksize=3)
    gradx = cv.convertScaleAbs(grad_x)
    grad_y = cv.Sobel(src, cv.CV_64F, 0, 1, ksize=3)
    grady = cv.convertScaleAbs(grad_y)
    gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
    cv.imshow("grad_x", grad_x)  # 将src图片放入该创建的窗口中
    cv.imshow("grad_y", grad_y)  # 将src图片放入该创建的窗口中
    cv.imshow("gradxy", gradxy)  # 将src图片放入该创建的窗口中


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/circular.png")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    sobel_demo(src)

    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

还可以设定使用的卷积核的大小( ksize)。如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,它的的效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器)。 3x3 的 Scharr 滤波器卷积核如下:
G_{X}=\begin{bmatrix} -3 & 0 &3 \\ -10 & 0 &10 \\ -3&0 & 3 \end{bmatrix}        G_{Y}=\begin{bmatrix} -3 & -10&-3 \\ 0 & 0 &0 \\ 3&10 & 3 \end{bmatrix}

# -*- coding:utf-8 -*-
# Author : MMagicLoren
# @Email : [email protected]
# @Time : 2019/10/13 15:59
# @File : 图像梯度计算.py
# @Project : Workspace
import cv2 as cv


def scharr_demo(image):
    scharr_x = cv.Scharr(src, cv.CV_64F, 1, 0)
    scharrx = cv.convertScaleAbs(scharr_x)
    scharr_y = cv.Scharr(src, cv.CV_64F, 0, 1)
    scharry = cv.convertScaleAbs(scharr_y)
    scharrxy = cv.addWeighted(scharrx, 0.5, scharry, 0.5, 0)
    # cv.imshow("grad_x", grad_x)  # 将src图片放入该创建的窗口中
    # cv.imshow("grad_y", grad_y)  # 将src图片放入该创建的窗口中
    cv.imshow("gradxy", scharrxy)  # 将src图片放入该创建的窗口中



if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu1.jpg")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    # sobel_demo(src)
    scharr_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()





可见scharr算子对线条更加敏感,对图像描述的更细致。

laplacian算子:

laplacian算子在实际应用中很少单独用到,都是结合一些其他的函数共同使用。

G=\begin{bmatrix} 0 &1 &0 \\ 1& -4 &1\\ 0& 1 &0 \end{bmatrix}

# -*- coding:utf-8 -*-
# Author : MMagicLoren
# @Email : [email protected]
# @Time : 2019/10/13 15:59
# @File : 图像梯度计算.py
# @Project : Workspace
import cv2 as cv


def laplacian_demo(image):
    laplacian = cv.Laplacian(src, cv.CV_64F)
    laplacian = cv.convertScaleAbs(laplacian)
    cv.imshow("laplacian", laplacian)  # 将src图片放入该创建的窗口中


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu1.jpg")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    # sobel_demo(src)
    # scharr_demo(src)
    laplacian_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

 


我们也可以自己定义一个拉普拉斯算子进行计算,看看效果如何。定义算子为:

G=\begin{bmatrix} 1 &1 &1 \\ 1& -8 &1 \\ 1& 1 & 1 \end{bmatrix}

# -*- coding:utf-8 -*-
# Author : MMagicLoren
# @Email : [email protected]
# @Time : 2019/10/13 15:59
# @File : 图像梯度计算.py
# @Project : Workspace
import cv2 as cv
import numpy as np


def laplacian_demo(image):
    # laplacian = cv.Laplacian(src, cv.CV_64F)
    kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1, ]])
    dst = cv.filter2D(src, cv.CV_64F, kernel=kernel)
    laplacian = cv.convertScaleAbs(dst)
    cv.imshow("laplacian", laplacian)  # 将src图片放入该创建的窗口中


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu1.jpg")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    # sobel_demo(src)
    # scharr_demo(src)
    laplacian_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

可见自定义的算子也是对原始算子的一个增强。

 

发布了29 篇原创文章 · 获赞 83 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/l59565455/article/details/102533507
今日推荐