• 【OpenCV 例程300篇】250. 梯度算子的传递函数


    『youcans 的 OpenCV 例程300篇 - 总目录』


    【youcans 的 OpenCV 例程300篇】250. 梯度算子的传递函数


    1. 空间卷积与频域滤波

    空间域图像滤波是图像与滤波器核的卷积,而空间卷积的傅里叶变换是频率域中相应变换的乘积,因此频率域图像滤波是频率域滤波器(传递函数)与图像的傅里叶变换相乘。频率域中的滤波概念更加直观,滤波器设计也更容易。

    对于二维图像处理,通常使用 x , y x, y x,y 表示离散的空间域坐标变量,用 u , v u,v u,v 表示离散的频率域变量。二维离散傅里叶变换(DFT)和反变换(IDFT)为:

    F ( u , v ) = ∑ x = 0 M − 1 ∑ y = 0 N − 1 f ( x , y ) e − j 2 π ( u x / M + v y / N ) f ( x , y ) = 1 M N ∑ u = 0 M − 1 ∑ v = 0 N − 1 F ( u , v ) e j 2 π ( u x / M + v y / N ) \begin{aligned} F(u,v) &= \sum_{x=0}^{M-1} \sum_{y=0}^{N-1} f(x,y) e^{-j 2\pi (ux/M+vy/N)}\\ f(x,y) &= \frac{1}{MN} \sum_{u=0}^{M-1} \sum_{v=0}^{N-1} F(u,v) e^{j 2\pi (ux/M+vy/N)} \end{aligned} F(u,v)f(x,y)=x=0M1y=0N1f(x,y)ej2π(ux/M+vy/N)=MN1u=0M1v=0N1F(u,v)ej2π(ux/M+vy/N)

    空间取样和频率间隔是相互对应的,频率域所对应的离散变量间的间隔为: Δ u = 1 / M Δ T , Δ v = 1 / N Δ Z \Delta u = 1/M \Delta T,\Delta v = 1/N \Delta Z Δu=1/MΔTΔv=1/NΔZ。即:频域中样本之间的间隔,与空间样本之间的间隔及样本数量的乘积成反比。

    空间域滤波器和频率域滤波器也是相互对应的,形成一个傅里叶变换对:
    f ( x , y ) ⊗ h ( x , y ) ⇔ F ( u , v ) H ( u , v ) f ( x , y ) h ( x , y ) ⇔ F ( u , v ) ⊗ H ( u , v ) f(x,y) \otimes h(x,y) \Leftrightarrow F(u,v)H(u,v) \\f(x,y) h(x,y) \Leftrightarrow F(u,v) \otimes H(u,v) f(x,y)h(x,y)F(u,v)H(u,v)f(x,y)h(x,y)F(u,v)H(u,v)

    这表明 F 和 H 分别是 f 和 h 的傅里叶变换;f 和 h 的空间卷积的傅里叶变换,是它们的变换的乘积。

    因此,计算两个函数的空间卷积,可以直接在空间域计算,也可以在频率域计算:先计算每个函数的傅里叶变换,再对两个变换相乘,最后进行傅里叶反变换转换回空间域。

    也就是说,空间域滤波器和频率域滤波器实际上是相互对应的,也是可以相互转换的。空间域滤波的核心是卷积核,频域滤波的核心是构造滤波器的传递函数。有些空间域滤波器在频率域通过傅里叶变换实现会更方便、更快速。

    在空间滤波中,除Laplacian算子之外还讨论了Sobel算子、Scharr算子,但在频域滤波中却很少提及。这是因为空间滤波中的平滑(模糊)/锐化的概念,与频域滤波中的低通滤波/高通滤波虽然相似,也有密切联系,但在本质上却是不同的。平滑滤波相当于低通滤波,但锐化与高通滤波是不同的。

    对空间滤波器核进行傅里叶变换,得到空间滤波器在频域的传递函数,可以清晰和直观地理解二者的联系和区别。


    2. 梯度算子在空间域的卷积核

    2.1 拉普拉斯卷积核(Laplacian)

    各向同性卷积核的响应与方向无关。最简单的各向同性导数算子(卷积核)是拉普拉斯算子(Laplace):

    K 1 = [ 0 1 0 1 − 4 1 0 1 0 ] ,   K 2 = [ 1 1 1 1 − 8 1 1 1 1 ] ,   K 3 = [ 0 − 1 0 − 1 4 − 1 0 − 1 0 ] ,   K 4 = [ − 1 − 1 − 1 − 1 8 − 1 − 1 − 1 − 1 ] K1= \begin{bmatrix} 0 & 1 &0\\ 1 & -4 &1\\ 0 & 1 &0\\ \end{bmatrix}, \ K2= \begin{bmatrix} 1 & 1 &1\\ 1 & -8 &1\\ 1 & 1 &1\\ \end{bmatrix}, \ K3= \begin{bmatrix} 0 & -1 &0\\ -1 & 4 &-1\\ 0 & -1 &0\\ \end{bmatrix}, \ K4= \begin{bmatrix} -1 & -1 &-1\\ -1 & 8 &-1\\ -1 & -1 &-1\\ \end{bmatrix} K1= 010141010 , K2= 111181111 , K3= 010141010 , K4= 111181111


    2.2 Sobel 梯度算子

    Sobel 算子是一种离散的微分算子,是高斯平滑和微分求导的联合运算,抗噪声能力强。

    Sobel 梯度算子利用局部差分寻找边缘,计算得到梯度的近似值。

    Sobel 梯度算子的卷积核为:
    K x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ,   K y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] K_x = \begin{bmatrix} -1 & 0 &1\\ -2 & 0 &2\\ -1 & 0 &1\\ \end{bmatrix}, \ K_y = \begin{bmatrix} -1 &-2 &-1\\ 0 &0 &0\\ 1 &2 &1\\ \end{bmatrix} Kx= 121000121 , Ky= 101202101


    2.3 Scharr 梯度算子

    Scharr 算子是 Soble 算子在 ksize=3 时的优化,与 Soble 的速度相同,且精度更高。Scharr 算子与 Sobel 算子的不同点是在平滑部分,其中心元素占的权重更重,相当于使用较小标准差的高斯函数,也就是更瘦高的模板。

    Scharr 算子的卷积核为:
    G x = [ − 3 0 3 − 10 0 10 − 3 0 3 ] ,   G y = [ − 3 10 − 3 0 0 10 3 10 3 ] G_x = \begin{bmatrix} -3 & 0 &3\\ -10 & 0 &10\\ -3 & 0 &3\\ \end{bmatrix}, \ G_y = \begin{bmatrix} -3 &10 &-3\\ 0 &0 &10\\ 3 &10 &3\\ \end{bmatrix} Gx= 31030003103 , Gy= 303100103103


    3. 【例程】梯度算子的传递函数

    本例程给出由空间滤波器核计算频域传递函数的子程序,比较常用空间域滤波器和梯度算子的传递函数。

    import cv2 as cv
    import numpy as np
    from matplotlib import pyplot as plt
    
    def getTransferFun(kernel, r):  # 计算滤波器核的传递函数
        hPad, wPad = r-kernel.shape[0]//2, r-kernel.shape[1]//2
        kernPadded = cv.copyMakeBorder(kernel, hPad, hPad, wPad, wPad, cv.BORDER_CONSTANT)
        kernFFT = np.fft.fft2(kernPadded)
        fftShift = np.fft.fftshift(kernFFT)
        kernTrans = np.log(1 + np.abs(fftShift))
        transNorm = np.uint8(cv.normalize(kernTrans, None, 0, 255, cv.NORM_MINMAX))
        return transNorm
    
    if __name__ == '__main__':
        radius = 64
        plt.figure(figsize=(9, 5.5))
    
        # (1) 盒式滤波器
        plt.subplot(241), plt.axis('off'), plt.title("1. BoxFilter")
        kernBox = np.ones((5,5), np.float32)  # BoxF 滤波器核
        HBox = getTransferFun(kernBox, radius)  # BoxF 传递函数
        plt.imshow(HBox, cmap='gray', vmin=0, vmax=255)
    
        # (2) 高斯低通滤波器
        plt.subplot(242), plt.axis('off'), plt.title("2. Gaussian")
        kernX = cv.getGaussianKernel(5, 0)  # 一维高斯核
        kernGaussian = kernX * kernX.T  # 二维高斯核
        HGaussian = getTransferFun(kernGaussian, radius)  # 高斯低通传递函数
        plt.imshow(HGaussian, cmap='gray', vmin=0, vmax=255)
    
        # (3) 拉普拉斯算子 K1
        plt.subplot(243), plt.axis('off'), plt.title("3. Laplacian K1")
        kernLaplacian1 = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])  # Laplacian K1
        hLaplacian1 = getTransferFun(kernLaplacian1, radius)  # Laplacian K1 传递函数
        plt.imshow(hLaplacian1, cmap='gray', vmin=0, vmax=255)
    
        # (4) 拉普拉斯算子 K2
        plt.subplot(244), plt.axis('off'), plt.title("4. Laplacian K2")
        kernLaplacian2 = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])  # Laplacian K2
        hLaplacian2 = getTransferFun(kernLaplacian2, radius)  # Laplacian K2 传递函数
        plt.imshow(hLaplacian2, cmap='gray', vmin=0, vmax=255)
    
        # (5) Sobel 算子,X轴方向
        plt.subplot(245), plt.axis('off'), plt.title("5. Sobel-X")
        kernSobelX = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
        HSobelX = getTransferFun(kernSobelX, radius)  # Sobel-X 传递函数
        plt.imshow(HSobelX, cmap='gray', vmin=0, vmax=255)
    
        # (6) Sobel 算子,Y轴方向
        plt.subplot(246), plt.axis('off'), plt.title("6. Sobel-Y")
        kernSobelY = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
        HSobelY = getTransferFun(kernSobelY, radius)  # Sobel-Y 传递函数
        plt.imshow(HSobelY, cmap='gray', vmin=0, vmax=255)
    
        # (7) Scharr 算子,X轴方向
        plt.subplot(247), plt.axis('off'), plt.title("7. Scharr-X")
        kernScharrX = np.array([[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]])
        HScharrX = getTransferFun(kernScharrX, radius)  # Scharr-X 传递函数
        plt.imshow(HScharrX, cmap='gray', vmin=0, vmax=255)
    
        # (8) Scharr 算子,Y轴方向
        plt.subplot(248), plt.axis('off'), plt.title("8. Scharr-Y")
        kernScharrY = np.array([[-3, -10, -3], [0, 0, 0], [3, 10, 3]])
        HScharrY = getTransferFun(kernScharrY, radius)  # Scharr-X 传递函数
        plt.imshow(HScharrY, cmap='gray', vmin=0, vmax=255)
    
        plt.tight_layout()
        plt.show()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/246290f557a240b3a7d2adb694c302d6.png#pic_center)

    在这里插入图片描述

    【本节完】

    版权声明:
    youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/127751487)
    Copyright 2022 youcans, XUPT
    Crated:2022-11-08

  • 相关阅读:
    mysql怎么导入sql文件
    SQL练习 2022/7/2
    Selenium web自动化测试环境搭建
    嵌入式必备技能---git与github
    机器学习预备知识
    [2022/6/29]考试总结
    【STM32】入门(二):跑马灯-GPIO端口输出控制
    【Docker】 Docker 通关命令:10个关键命令带你解锁容器任务
    antd form Item设置初始值
    122. 在 SAP UI5 应用中使用浏览器原生的 Fetch API 发起网络请求
  • 原文地址:https://blog.csdn.net/youcans/article/details/127751487