精华内容
下载资源
问答
  • Tensorflow–一维离散卷积 一维离散卷积的运算是一种主要基于向量的计算方式 一.一维离散卷积的计算原理 一维离散卷积通常有三种卷积类型:full卷积,same卷积和valid卷积 1.full卷积 full卷积的计算过程如下:K沿着...

    Tensorflow–一维离散卷积

    一维离散卷积的运算是一种主要基于向量的计算方式

    一.一维离散卷积的计算原理

    一维离散卷积通常有三种卷积类型:full卷积,same卷积和valid卷积

    1.full卷积

    full卷积的计算过程如下:K沿着I顺序移动,每移动一个固定位置,对应位置的值相乘,然后对其求和

    其中K称为卷积核或者滤波器或者卷积掩码

    2.valid卷积

    从full卷积的计算过程可知,如果K靠近I,就会有部分延伸到I之外,valid卷积只考虑能完全覆盖K内的情况,即K在I内部移动的情况

    3.same卷积

    首先在卷积核K上指定一个锚点,然后将锚点顺序移动到输入张量I的每一个位置处,对应位置相乘然后求和,卷积核锚点的位置一般有以下规则,假设卷积核的长度为FL:

    如果FL为奇数,则锚点的位置在(FL-1)/2处

    如果FL为偶数,则锚点的位置在(FL-2)/2处

    4.full,same,valid卷积的关系

    假设一个长度为L的一维张量与一个长度为FL的卷积核卷积,其中Fa代表计算same卷积时,锚点的位置索引,则两者的full卷积与same卷积的关系如下:
    Csame=Cfull[FL-Fa-1,FL-Fa+L-2]

    full卷积C与valid卷积C的关系如下:
    C=C[FL-1,L-1]

    same卷积C与valid卷积C的关系如下:
    C=C[FL-Fa-2,L-Fa_2]

    注意:大部分书籍中对卷积运算的定义分为两步。第1步是将卷积核翻转180;第2步是将翻转的结果沿输入张量顺序移动,每移动到一个固定位置,对应位置相乘然后求和,如Numpy中实现的卷积函数convolve和Scipy中实现的卷积函数convolve,函数内部都进行了以上两步运算。可见,最本质的卷积运算还是在第2步。Tensorflow中实现的卷积函数

    tf.nn.conv1d(value,filters,stride,padding,use_cudnn_on_gpu=None,data_format=None,name=None)
    

    其内部就没有进行第1步操作,而是直接进行了第2步操作

    import tensorflow as tf
    
    # 输入张量I
    I=tf.constant(
        [
            [[3],[4],[1],[5],[6]]  
        ]
        ,tf.float32
    )
    
    # 卷积核
    K=tf.constant(
        [
            [[-1]],
            [[-2]],
            [[2]],
            [[1]]
        ]
        ,tf.float32
    )
    
    I_conv1d_K=tf.nn.conv1d(I,K,1,'SAME')
    
    session=tf.Session()
    
    print(session.run(I_conv1d_K))
    
    [[[  3.]
      [ -4.]
      [ 10.]
      [  1.]
      [-17.]]]
    

    函数tf.nn.conv1d只实现了same卷积和valid卷积,它就是为更方便地搭建卷积神经网络而设计的。利用Numpy或者Scipy中的卷积函数convolve实现上述示例的full卷积代码如下:

    import numpy as np
    from scipy import signal
    
    I=np.array([3,4,1,5,6],np.float32)
    K=np.array([-1,-2,2,1],np.float32)
    
    # 卷积核K翻转180
    K_reverse=np.flip(K,0)
    # r=np.convolve(I,K_reverse,mode='full')
    r=signal.convolve(I,K_reverse,mode='full')
    
    print(r)
    
    [  3.  10.   3.  -4.  10.   1. -17.  -6.]
    

    注意:如果卷积核的长度是偶数,函数convolve和tf.nn.conv1d在实现same卷积时,其结果会略有不同,但也只是在边界处(两端)的值有所不同,这是因为这两个函数对卷积核锚点的位置定义不同,本质上就是从full卷积结果中取的区域不一样

    二.一维卷积定理

    1.一维离散傅里叶变换

    Tensorflow通过函数fft和ifft分别实现一维离散的傅里叶变换及逆变换

    import tensorflow as tf
    
    # 输入长度为3的一维张量
    f=tf.constant([4,5,6],tf.complex64)
    
    session=tf.Session()
    
    # 一维傅里叶变换
    F=tf.fft(f)
    
    print("傅里叶变换F的值:")
    print(session.run(F))
    
    # 计算F的傅里叶逆变换(显然与输入的f是相等的)
    F_ifft=tf.ifft(F)
    
    print("打印F的傅里叶逆变换的值:")
    print(session.run(F_ifft))
    
    傅里叶变换F的值:
    [15.       -5.9604645e-08j -1.4999998+8.6602545e-01j
     -1.4999999-8.6602563e-01j]
    打印F的傅里叶逆变换的值:
    [4.+3.9736431e-08j 5.-3.1789145e-07j 6.+0.0000000e+00j]
    

    2.卷积定理

    假设有长度为L的一维张量I,I(l)代表I的第l个数,其中0≤l<L,有长度为FL的一维卷积核K,那么I与K的full卷积结果的尺寸为L+FL-1

    首先,在I的末尾补零,将I的尺寸扩充到与full卷积尺寸相同,即
    未命名文件.png

    然后,将卷积核K翻转180得到K_rotate180,在末尾进行补零操作,且将K_rotate180的尺寸扩充到和full卷积相同,即
    未命名文件 (1).png
    假设fft_Ip和fft_Krp分别是I_padded和K_rotate180_padded的傅里叶变换,那么I☆K的傅里叶变换等于fft_Ipfft_Krp,即
    未命名文件 (2).png
    其中
    代表对应元素相乘,即对应位置的两个复数相乘,该关系通常称为卷积定理

    从卷积定理中可以看出分别有对张量的补零操作(或称为边界扩充)和翻转操作。这两种操作在Tensorflow中有对应的函数实现,我们先介绍实现边界扩充的函数:

    pad(tensor,padding,mode='CONSTANT',name=None,constant_value=0)
    

    以长度为2的一维张量为例,上侧补1个0,下侧补2个0

    import tensorflow as tf
    
    x=tf.constant([2,1],tf.float32)
    
    r=tf.pad(x,[[1,2]],mode='CONSTANT')
    
    session=tf.Session()
    
    print(session.run(r))
    
    [0. 2. 1. 0. 0.]
    

    当使用常数进行扩充时,也可以选择其他常数,通过参数constant_value进行设置,默认缺省值为0

    import tensorflow as tf
    
    x=tf.constant(
        [
            [1,2,3],
            [4,5,6]
        ]
        ,tf.float32
    )
    
    # 常数边界扩充,上侧补1行10,下侧补2行10,右侧补1列10
    r=tf.pad(x,[[1,2],[0,1]],mode='CONSTANT',constant_values=10)
    
    session=tf.Session()
    
    print(session.run(r))
    
    [[10. 10. 10. 10.]
     [ 1.  2.  3. 10.]
     [ 4.  5.  6. 10.]
     [10. 10. 10. 10.]
     [10. 10. 10. 10.]]
    

    除了常数边界扩充,还有其他扩充方式,可以通过参数mode设置,当mode='SYMMETRIC’时,代表镜像方式的边界扩充;当mode='REFLECT’时,代表反射方式的边界扩充,可以修改以上程序观察打印结果

    Tensorflow通过函数reverse(tensor,axis,name=None)实现张量的翻转,二维张量的每一列翻转(沿"0"方向),则称为水平镜像;对每一行翻转(沿"1"方向),则称为垂直镜像

    import tensorflow as tf
    
    t=tf.constant(
        [
            [1,2,3],
            [4,5,6]
        ]
        ,tf.float32
    )
    
    # 水平镜像
    rh=tf.reverse(t,axis=[0])
    
    # 垂直镜像
    rv=tf.reverse(t,axis=[1])
    
    # 逆时针翻转180:先水平镜像在垂直镜像(或者先垂直再水平)
    r=tf.reverse(t,[0,1])
    
    session=tf.Session()
    
    print("水平镜像的结果:")
    print(session.run(rh))
    print("垂直镜像的结果:")
    print(session.run(rv))
    print("逆时针翻转180的结果:")
    print(session.run(r))
    
    水平镜像的结果:
    [[4. 5. 6.]
     [1. 2. 3.]]
    垂直镜像的结果:
    [[3. 2. 1.]
     [6. 5. 4.]]
    逆时针翻转180的结果:
    [[6. 5. 4.]
     [3. 2. 1.]]
    

    掌握了张量边界扩充和翻转的对应函数后,利用卷积定理计算前面中x和K的full卷积

    import tensorflow as tf
    
    # 长度为5的输入张量
    I=tf.constant(
        [3,4,1,5,6],tf.complex64
    )
    
    # 长度为4的卷积核
    K=tf.constant(
        [-1,-2,2,1],tf.complex64
    )
    
    # 补0操作
    I_padded=tf.pad(I,[[0,3]])
    
    # 将卷积核翻转180
    K_rotate180=tf.reverse(K,axis=[0])
    
    # 翻转进行0操作
    K_rotate180_padded=tf.pad(K_rotate180,[[0,4]])
    
    # 傅里叶变换
    I_padded_fft=tf.fft(I_padded)
    
    # 傅里叶变换
    K_rotate180_padded_fft=tf.fft(K_rotate180_padded)
    
    # 将以上两个傅里叶变换点乘操作
    IK_fft=tf.multiply(I_padded_fft,K_rotate180_padded_fft)
    
    # 傅里叶逆变换
    IK=tf.ifft(IK_fft)
    
    # 因为输入的张量和卷积核都是实数,对以上傅里叶逆变换进行取实部的操作
    IK_real=tf.real(IK)
    session=tf.Session()
    
    print(session.run(IK_real))
    
    [  2.9999998  10.          3.         -4.         10.          1.
     -17.         -6.       ]
    

    三.具备深度的一维离散卷积

    1.具备深度的张量与卷积核的卷积

    张量x可以理解为是一个长度为3,深度为3的张量,K可以理解为是一个长度为2,深度为3的张量,两者same卷积的过程就是锚点顺序移动到输入张量的每一个位置处,然后对应位置相乘,求和

    注意:输入张量的深度和卷积核的深度是相等的

    import tensorflow as tf
    
    # 1个长度为3,深度为3的张量
    x=tf.constant(
        [
            [[2,5,2],[6,1,-1],[7,9,-5]]
        ]
        ,tf.float32
    )
    
    # 1个长度为2,深度为3的卷积核
    k=tf.constant(
        [
            [[-1],[5],[4]],[[2],[1],[6]]
        ]
        ,tf.float32
    )
    
    # 一维same卷积
    v_conv1d_k=tf.nn.conv1d(x,k,1,'SAME')
    
    session=tf.Session()
    
    print(session.run(v_conv1d_k))
    
    [[[ 38.]
      [-12.]
      [ 18.]]]
    

    2.具备深度的张量分别与多个卷积核的卷积

    同一个张量与多个卷积核的卷积本质上是该张量分别与每一个卷积核卷积,然后将每一个卷积结果在深度方向上连接在一起。以长度为3,深度为3的输入张量与2个长度为2,深度为3的卷积核卷积为例

    import tensorflow as tf
    
    x=tf.constant(
        [
            [[2,5,2],[6,1,-1],[7,9,-5]]
        ]
        ,tf.float32
    )
    
    # 2个长度为2,深度为3的卷积核
    k=tf.constant(
        [
            [[-1,1],[5,3],[4,7]],[[2,-2],[1,-1],[6,9]]
        ]
        ,tf.float32
    )
    
    v_conv1d_k=tf.nn.conv1d(x,k,1,'SAME')
    
    session=tf.Session()
    
    print(session.run(v_conv1d_k))
    
    [[[ 38.   9.]
      [-12. -66.]
      [ 18.  -1.]]]
    

    1个深度为C的张量与M个深度为C的卷积核的卷积结果的深度为M,即最后输出结果的深度与卷积核的个数相等

    3.多个具备深度的张量分别与多个卷积核的卷积

    计算3个长度为3,深度为3的张量与2个长度为2,深度为3的卷积核的卷积

    import tensorflow as tf
    
    # 3个长度为3,深度为3的张量
    x=tf.constant(
            [
                
                [[2,5,2],[6,1,-1],[7,9,-5]], # 第1个
                [[1,3,2],[5,2,-2],[8,4,3]],  # 第2个
                [[4,5,-1],[1,9,5],[2,7,0]]   # 第3个
            ]
        ,tf.float32
    )
    
    # 2个长度为2,深度为3的卷积核
    k=tf.constant(
            [
                
                [[-1,1],[5,3],[4,7]],[[2,-2],[1,-1],[6,9]]
            ]
        ,tf.float32
    )
    
    v_conv1d_k=tf.nn.conv1d(x,k,1,'SAME')
    
    session=tf.Session()
    
    print(session.run(v_conv1d_k))
    
    [[[ 38.   9.]
      [-12. -66.]
      [ 18.  -1.]]
    
     [[ 22.  -6.]
      [ 35.   4.]
      [ 24.  41.]]
    
     [[ 58.  46.]
      [ 75.  52.]
      [ 33.  23.]]]
    

    函数tf.nn.conv1d可以实现任意多个输入量分别与任意多个卷积核的卷积,输入张量的深度和卷积核的深度是相等的

    转载于:https://www.cnblogs.com/LQ6H/p/10335523.html

    展开全文
  • 第一章:一维离散卷积假设有两个限序列:,我们把他们放入以下栅格:然后,沿着,进行滑窗: 从下面开始出现相互重叠的栅格: 直到不再有相互重叠的栅格:矩阵法求卷积:首先给出矩阵构造的方法步骤:然后在末尾...

    第一章:一维离散卷积

    假设有两个有限序列:


    我们把他们放入以下栅格:



    然后,沿着,进行滑窗:

          

                                      

    从下面开始出现相互重叠的栅格: 

         


         


         

        

        

         直到不再有相互重叠的栅格:


    我们可以看出上面一共得出了5个值,即以下序列:

                                                                                                                                            

    那么该序列即:的卷积,显然序列的长度为 :的长度+的长度-1。

    --------------------------------------------------------------------------------------------------------------------------------------------

    矩阵法求卷积:

    给出矩阵构造的方法步骤:

    1:从上述卷积的图解法,可以看出,虽然-1排在前面,但是第一次参与运算的是 1,所以我们首先把旋转180度,得到

    2:在末尾进行补零,使它们的长度均为 4+2-1=5(即卷积后元素的个数), 为了讨论方便 补零后 都转换为列向量:
     、

    补零后的列向量为基础,构造以下循环矩阵,

    3、两个矩阵相乘,即可得到卷积的结果:


    可以看到矩阵相乘得到的结果,即为卷积得到的结果:

                                                                                                         


    【补充:解释上述构造循环矩阵的方法】

    假设有以下向量:


    那么以它为基础的循环矩阵为:


    -----------------------------------------------------------------------------------------------------------------------------------

    多项式法求卷积:

                                                                                                       
    翻转180,得到
                                                                                                     ,

    然后分别以它们为系数(从高次幂到低次幂),得到两个多项式:

    ,

    然后上述两个多项式相乘:


    把上述多项式的结果,按从高次幂到低次幂取出它们的系数,即为卷积的结果:
    --------------------------------
    以下会补上一维离散的傅里叶变换、以及一维离散的傅里叶变换和一维离散卷积的关系。
    并将以上内容推广到二维,用于数字图像处理。。

    展开全文
  • 使用库中实现的快速傅立叶变换 (FFT) 执行实向量一维离散卷积的简单 C++ 示例。 这段代码是著名的卷积定理的简单直接应用。 它效率不高,但意味着易于理解。 算法说明 设 v1, v2 是两个实数向量。 这些向量的离散...
  • 二维离散卷积的计算原理同一维离散卷积的计算原理类似,也有三种卷积类型:full卷积,same卷积核valid卷积。通过3行3列的二维张量x和2行2列的二维张量K 1.full卷积 full卷积的计算过程如下:K沿着x按照先行后列的...

    Tensorflow–二维离散卷积

    一.二维离散卷积的计算原理

    二维离散卷积的计算原理同一维离散卷积的计算原理类似,也有三种卷积类型:full卷积,same卷积核valid卷积。通过3行3列的二维张量x和2行2列的二维张量K
    未命名文件.png

    1.full卷积

    full卷积的计算过程如下:K沿着x按照先行后列的顺序移动,每移动到一个固定位置,对应位置的值相乘,然后求和

    注意:同一维卷积类似,对二维卷积的定义一般分为两步,首先将卷积核翻转180,然后计算对应位置相乘的和,如常用的Numpy,MATLAB中实现的卷积函数都是先将输入的卷积核翻转180,Tensorflow中实现二维卷积的函数为:

    tf.nn.conv2d(input,filter,strides,padding,use_cudnn_on_gpu=True,data_format="NHWC",dilations=[1,1,1,1],name=None)
    

    该函数内部没有对卷积核翻转

    2.same卷积

    x和K进行same卷积,首先为K指定一个锚点,然后将锚点先行后列地移动到输入张量x的每一个位置处,对应位置相乘然后求和。卷积核K的高等于FH,宽等于FW,其锚点的位置一般用以下规则定义

    .如果FH为奇数,FW为奇数,锚点的位置是((FH-1)/2,(FW-1)/2)

    .如果FH为奇数,FW为偶数,锚点的位置是((FH-1)/2,(FW-2)/2)

    .如果FH为偶数,FW为奇数,锚点的位置是((FH-2)/2,(FW-1)/2)

    .如果FH为偶数,FW为偶数,锚点的位置是((FH-2)/2,(FW-2)/2)

    这里的位置索引是从0开始的

    以上面的示例为例,K的高为2,宽为2,所以锚点的位置在K的(0,0)处

    import tensorflow as tf
    
    X=tf.constant(
        [
            [
                [[2],[3],[8]],
                [[6],[1],[5]],
                [[7],[2],[-1]]
            ]
        ]
        ,tf.float32
    )
    
    K=tf.constant(
        [
            [
                [[4]],[[1]]],
            [
                [[2]],[[3]]
            ]
        ]
        ,tf.float32
    )
    
    # same卷积
    conv=tf.nn.conv2d(X,K,(1,1,1,1),'SAME')
    
    session=tf.Session()
    
    print(session.run(conv))
    
    [[[[26.]
       [37.]
       [42.]]
    
      [[45.]
       [10.]
       [18.]]
    
      [[30.]
       [ 7.]
       [-4.]]]]
    

    3.valid卷积

    如果卷积核K靠近x的边界,那么K就会有部分延伸到x外,导致访问到未定义的值;如果忽略边界,只考虑x能完全覆盖K的值情况(即K在x内部移动),则该过程称为valid卷积

    import tensorflow as tf
    
    X=tf.constant(
        [
            [
                [[2],[3],[8]],
                [[6],[1],[5]],
                [[7],[2],[-1]]
            ]
        ]
        ,tf.float32
    )
    
    K=tf.constant(
        [
            [
                [[4]],[[1]]],
            [
                [[2]],[[3]]
            ]
        ]
        ,tf.float32
    )
    
    # same卷积
    conv=tf.nn.conv2d(X,K,(1,1,1,1),'VALID')
    
    session=tf.Session()
    
    print(session.run(conv))
    
    [[[[26.]
       [37.]]
    
      [[45.]
       [10.]]]]
    

    4.full,same,valid卷积的关系

    假设有H行W列的二维张量x与FH行FW列的二维张量K卷积,两者full卷积的结果记为,same卷积的结果记为,valid的结果记为

    full卷积与valid卷积的关系
    Cvalid=C[FH-1:H-1,FW-1:W-1]

    full卷积与same卷积的关系
    假设same卷积的卷积核的锚点的位置在第Fr行,第Fc列处
    C=C[FH-Fr-1:H+FH-Fr-2,FW-Fc-1:W+FW-Fc-2]

    same卷积与valid卷积的关系
    C=Csame[Fr:H-FH+Fr,Fc:W-FW+FC]
    未命名文件 (1).png

    5.卷积结果的输出尺寸

    我们讨论的卷积操作,在卷积过程中卷积核的移动步长均是1,所以H行W列的x与FH行FW列的卷积核K的same卷积结果的尺寸为H行W列,valid卷积结果的尺寸为H-FH+1行W-FW+1列

    same卷积结果的尺寸

    valid卷积结果的尺寸

    二.离散卷积的性质

    1.可分离的卷积核

    如果一个卷积核由至少两个尺寸比它小的卷积核full卷积二成,既满足

    Kennel=kernel1☆kernel2☆…kerneln

    其中kerneli的尺寸均比Kernel小,1≤i≤n,则陈卷积核Kernel是可分离的

    2.full和same卷积的性质

    以下代码实现了I与卷积核Kernel的same卷积,因为Kernel是可分离的,利用same卷积的性质,可以计算两者的same卷积

    import tensorflow as tf
    
    # 输入张量5x5
    I=tf.constant(
        [
            [
                [[2],[9],[11],[4],[8]],
                [[6],[12],[20],[16],[5]],
                [[1],[32],[13],[14],[10]],
                [[11],[20],[27],[40],[17]],
                [[9],[8],[11],[4],[1]]
            ]
        ]
        ,tf.float32
    )
    
    # 卷积核3x3
    Kernel=tf.constant(
        [
            [
                [[4]],[[8]],[[12]]
            ],
            [
                [[5]],[[10]],[[15]]  
            ],
            [
                [[6]],[[12]],[[18]]
            ]
        ]
        ,tf.float32
    )
    
    session=tf.Session()
    
    # 输入张量与卷积核直接卷积
    result=tf.nn.conv2d(I,Kernel,[1,1,1,1],'SAME')
    print("直接卷积结果是:")
    print(session.run(result))
    
    # 卷积核分离为3x1的垂直卷积核和1x3的水平卷积核
    kernel1=tf.constant(
        [
            [[[4]]],
            [[[5]]],
            [[[6]]]
        ]
        ,tf.float32
    )
    
    kernel2=tf.constant(
        [
            [[[3]],[[2]],[[1]]]
        ]
        ,tf.float32
    )
    
    # 将kernel2翻转180
    rotate180_kernel2=tf.reverse(kernel2,axis=[1])
    
    # 输入张量与分离的卷积核的卷积
    result1=tf.nn.conv2d(I,kernel1,[1,1,1,1],'SAME')
    result2=tf.nn.conv2d(result1,rotate180_kernel2,[1,1,1,1],'SAME')
    print("利用卷积核的分离性的卷积结果:")
    print(session.run(result2))
    
    直接卷积结果是:
    [[[[ 443.]
       [ 805.]
       [ 815.]
       [ 617.]
       [ 256.]]
    
      [[ 952.]
       [1286.]
       [1272.]
       [ 933.]
       [ 414.]]
    
      [[1174.]
       [1672.]
       [2064.]
       [1571.]
       [ 718.]]
    
      [[1054.]
       [1424.]
       [1622.]
       [1206.]
       [ 542.]]
    
      [[ 538.]
       [ 818.]
       [ 986.]
       [ 742.]
       [ 326.]]]]
    利用卷积核的分离性的卷积结果:
    [[[[ 443.]
       [ 805.]
       [ 815.]
       [ 617.]
       [ 256.]]
    
      [[ 952.]
       [1286.]
       [1272.]
       [ 933.]
       [ 414.]]
    
      [[1174.]
       [1672.]
       [2064.]
       [1571.]
       [ 718.]]
    
      [[1054.]
       [1424.]
       [1622.]
       [1206.]
       [ 542.]]
    
      [[ 538.]
       [ 818.]
       [ 986.]
       [ 742.]
       [ 326.]]]]
    

    3.快速计算卷积

    假设输入张量I的尺寸是H_W,卷积核Kernel的尺寸为FH_W_FH*FW

    如果卷积核Kernel是可分离的,分离为FH_1的垂直卷积核kernel1和1_W_(FH+FW)

    以上面示例为例,两者same卷积的计算次数为5_5_3=225,利用卷积核的分离性及卷积的结合率,same卷积的计算次数为(5_5)*(3+3)=150。显然,利用卷积核的分离性,计算次数比直接卷积减少了很多,张量或者卷积核的尺寸越大,忧伤越明显

    三.二维卷积定理

    二维卷积定理是一维卷积定理的推广,它揭示了二维傅里叶变换和二维卷积的某种关系

    1.二维离散傅里叶变换

    假设有M行N列的复数数列f,其中f(x,y)代表f第x行第y列对应的值,那么对任意的x∈[0,M-1],y∈[0,N-1],是否存在M行N列的复数数列F,使得以下等式成立:

    Tensorflow通过函数fft2d和ifft2d实现二维离散的傅里叶变换及逆变换

    import tensorflow as tf
    
    f=tf.constant(
        [
            [10,2,8],
            [5,12,3]
        ]
        ,tf.complex64
    )
    
    session=tf.Session()
    
    F=tf.fft2d(f)
    
    print("f的二维离散傅里叶变换:")
    print(session.run(F))
    
    # 计算F的傅里叶逆变换(显然与输入的f是相等的)
    F_ifft2d=tf.ifft2d(F)
    
    print("F的傅里叶逆变换:")
    print(session.run(F_ifft2d))
    
    f的二维离散傅里叶变换:
    [[4.0000000e+01-2.3841858e-07j 2.4999998e+00-2.5980763e+00j
      2.5000002e+00+2.5980752e+00j]
     [4.7683716e-07-2.3841858e-07j 7.5000000e+00+1.2990381e+01j
      7.5000005e+00-1.2990381e+01j]]
    F的傅里叶逆变换:
    [[10.       -4.7683716e-07j  1.9999998+1.5894572e-07j
       7.9999995+3.1789145e-07j]
     [ 5.       -1.5894572e-07j 12.       +6.3578290e-07j
       3.       -1.5894572e-07j]]
    

    2.二维与一维傅里叶变换的关系

    二维离散傅里叶变换也可以分解为先计算每一列的傅里叶变换,再计算每一行的傅里叶变换

    Tensorflow并没有提供分别计算二维数列的行或列的傅里叶变换,Numpy中函数fft可以实现该功能,具体代码如下:

    import numpy as np
    
    f=np.array(
        [
            [10,2,8],
            [5,12,3]
        ]
        ,np.complex64
    )
    
    # 第1步:对每一列进行傅里叶变换
    f_0_fft=np.fft.fft(f,axis=0)
    print(f_0_fft)
    
    # 第2步:将上面结果,分别对每一行进行傅里叶变换
    f_0_1_fft=np.fft.fft(f_0_fft,axis=1)
    print(f_0_1_fft)
    
    [[ 15.+0.j  14.+0.j  11.+0.j]
     [  5.+0.j -10.+0.j   5.+0.j]]
    [[40.  +0.j          2.5 -2.59807621j  2.5 +2.59807621j]
     [ 0.  +0.j          7.5+12.99038106j  7.5-12.99038106j]]
    

    以下代码是先计算每一行的一维傅里叶变换,再计算每一列的一维离散傅里叶变换,代码如下:

    # 第1步:对每一行进行傅里叶变换
    f_1_fft=np.fft.fft(f,axis=1)
    print(f_1_fft)
    
    # 第2步:将上面得到的结果,分别对每一列进行傅里叶变换
    f_1_0_fft=np.fft.fft(f_1_fft,axis=0)
    print(f_1_0_fft)
    

    3.卷积定理

    假设有高为H,宽为W的二维输入张量I,高为FH,宽为FW的卷积核k,那么I与k的full卷积结果的尺寸是高为H+FH-1,宽为W+FW-1

    在I的右侧和下层补零,且将I的尺寸扩充到与full卷积的尺寸相同,即

    其中0≤h≤H+FH-1,0≤w<W+FW-1

    将卷积核k逆时针翻转180得到k_rotate180,然后对其右侧和下侧进行补零,且将k_rotate180的尺寸可从到和full卷积相同的尺寸

    其中0≤h≤H+FH-1,0≤w<W+FW-1

    假设fft2_Ip和fft2_krp分别是I_padded和k_rotate180_padded的傅里叶变换,那么I☆k的傅里叶变换等于fft2_Ip*fft2_krp,即

    其中*代表对应位置的元素相乘,即对应位置的两个复数相乘,该性质称为卷积定理

    4.利用卷积定理快速计算卷积

    我们以上例中的x和K为例,利用卷积定理计算两者的卷积,具体实现代码如下:

    import tensorflow as tf
    
    # 输入张量I
    I=tf.constant(
        [
            [2,3,8],
            [6,1,5],
            [7,2,-1]
        ]
        ,tf.complex64
    )
    
    # 卷积核
    k=tf.constant(
        [
            [4,1],
            [2,3]
        ]
        ,tf.complex64
    )
    
    # 对输入张量的下侧和右侧补0
    I_padded=tf.pad(I,[[0,1],[0,1]])
    
    # 翻转卷积核180
    k_rotate180=tf.reverse(k,[0,1])
    
    # 对翻转后的卷积核下侧和右侧补0
    k_rotate180_padded=tf.pad(k_rotate180,[[0,2],[0,2]])
    
    # 二维离散傅里叶变换
    I_padded_fft2=tf.fft2d(I_padded)
    k_rotate180_padded_fft2=tf.fft2d(k_rotate180_padded)
    
    # 两个二维傅里叶变换对应位置相乘
    xk_fft2=tf.multiply(I_padded_fft2,k_rotate180_padded_fft2)
    
    # 对以上相乘的结果进行傅里叶逆变换
    xk=tf.ifft2d(xk_fft2)
    
    session=tf.Session()
    
    # 利用卷积定理计算的full卷积的结果
    print(session.run(xk))
    
    [[ 6.+0.j 13.+0.j 30.+0.j 16.+0.j]
     [20.+0.j 26.+0.j 37.+0.j 42.+0.j]
     [27.+0.j 45.+0.j 10.+0.j 18.+0.j]
     [ 7.+0.j 30.+0.j  7.+0.j -4.+0.j]]
    

    四.多深度的离散卷积

    1.基本的多深度卷积

    我们以3行3列2深度的三维张量x和2行2列2深度的三维卷积核k的valid卷积为例

    import tensorflow as tf
    
    # 3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ]
        ]
        ,tf.float32
    )
    
    # 2行2列2深度的卷积核
    k=tf.constant(
        [
            [[[3],[1]],[[-2],[2]]],
            [[[-1],[-3]],[[4],[5]]]
        ]
        ,tf.float32
    )
    
    # 每一深度分别计算乘积,然后求和
    x_conv2d_k=tf.nn.conv2d(x,k,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(x_conv2d_k))
    
    [[[[16.]
       [33.]]
    
      [[10.]
       [ 3.]]]]
    

    即1个3行3列2深度的三维张量与1个2行2列2深度的卷积核的valid卷积结果是1个2行2列1深度的三维张量

    2.1个张量与多个卷积核的卷积

    示例理解1个3行3列2深度的张量与3个2行2列2深度的卷积核卷积

    import tensorflow as tf
    
    # 1个3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ]
        ]
        ,tf.float32
    )
    
    # 3个2行2列2深度的卷积核
    kernels=tf.constant(
        [
            [[[3,1,-3],[1,-1,7]],[[-2,2,-5],[2,7,3]]],
            [[[-1,3,1],[-3,-8,6]],[[4,6,8],[5,9,-5]]]
        ]
        ,tf.float32
    )
    
    # valid卷积
    validResult=tf.nn.conv2d(x,kernels,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(validResult))
    
    [[[[16. 58. 33.]
       [33. 83. 11.]]
    
      [[10.  9. 52.]
       [ 3. 40. -5.]]]]
    

    即1个3行3列2深度的输入张量,与3个2行2列2深度的卷积核的valid卷积结果是1个2行2列3深度的三维张量

    3.多个张量分别与多个卷积核的卷积

    以2个3行3列2深度的三维张量,分别与3个2行2列2深度的卷积核进行基本的多深度卷积

    import tensorflow as tf
    
    # 2个3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ],
            [
                [[1,3],[2,1],[3,2]],
                [[1,1],[2,2],[1,4]],
                [[3,4],[4,2],[-1,1]]
            ]
        ]
        ,tf.float32
    )
    
    # 3个2行2列2深度的卷积核
    kernels=tf.constant(
        [
            [[[3,1,-3],[1,-1,7]],[[-2,2,-5],[2,7,3]]],
            [[[-1,3,1],[-3,-8,6]],[[4,6,8],[5,9,-5]]]
        ]
        ,tf.float32
    )
    
    # valid卷积
    validResult=tf.nn.conv2d(x,kernels,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(validResult))
    
    [[[[16. 58. 33.]
       [33. 83. 11.]]
    
      [[10.  9. 52.]
       [ 3. 40. -5.]]]
    
    
     [[[18. 34. 24.]
       [21. 53. -6.]]
    
      [[15. 37. 49.]
       [ 5. 29. 18.]]]]
    

    即2个3行3列2深度的输入张量分别与3个2行2列2深度的卷积核的valid卷积结果是2个2行2列3深度的三维张量(即四维张量)

    总结:利用函数tf.nn.conv2d可以计算M个深度为D三维张量分别与N个深度为D的卷积核的卷积,其返回结果为M个深度为N的三维张量(即四维张量)

    函数tf.nn.conv2d实现的是分别在深度上卷积,然后沿深度上求和的卷积计算方式。接下来介绍另一个函数depthwise_conv2d,该函数实现的只是在深度上卷积

    4.在每一深度上分别卷积

    函数depthwise_conv2d与函数conv2d的不同之处在于conv2d在每一深度上卷积,然后求和,depthwise_conv2d没有求和这一步,具体代码如下

    x_depthwise_conv2d_k=tf.nn.depthwise_conv2d(x,k,[1,1,1,1],‘VALID’)

    import tensorflow as tf
    
    # 3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ]
        ]
        ,tf.float32
    )
    
    # 2行2列2深度的卷积核
    k=tf.constant(
        [
            [[[3],[1]],[[-2],[2]]],
            [[[-1],[-3]],[[4],[5]]]
        ]
        ,tf.float32
    )
    
    # 每一深度分别计算乘积,然后求和
    x_depthwise_conv2d_k=tf.nn.depthwise_conv2d(x,k,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(x_depthwise_conv2d_k))
    
    [[[[ -2.  18.]
       [ 12.  21.]]
    
      [[ 17.  -7.]
       [-13.  16.]]]]
    

    5.单个张量与多个卷积核在深度上分别卷积

    以1个3行3列2深度的三维张量与3个2行2列2深度的三维卷积核卷积,因为输入张量与每个卷积核的卷积结果的深度为2,一共与3个卷积核卷积,即有3个卷积结果,将它们在深度方向上连接,所以最终结果的深度为2*3=6

    import tensorflow as tf
    
    # 1个3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ]
        ]
        ,tf.float32
    )
    
    # 3个2行2列2深度的卷积核
    kernels=tf.constant(
        [
            [[[3,1,-3],[1,-1,7]],[[-2,2,-5],[2,7,3]]],
            [[[-1,3,1],[-3,-8,6]],[[4,6,8],[5,9,-5]]]
        ]
        ,tf.float32
    )
    
    # valid卷积
    x_depthwise_conv2d_k=tf.nn.depthwise_conv2d(x,kernels,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(x_depthwise_conv2d_k))
    
    [[[[ -2.  32.  -7.  18.  26.  40.]
       [ 12.  52.  -8.  21.  31.  19.]]
    
      [[ 17.  41.   0.  -7. -32.  52.]
       [-13.  11. -34.  16.  29.  29.]]]]
    

    总结:1个深度为D的三维张量与N个深度为D的卷积核的depthwise_conv2d卷积,其结果为1个深度为NxD的三维张量

    6.分离卷积

    我们介绍Tensorflow实现的另一个关于卷积的函数:

    separable_conv2d(input,depthwise_filter,pointwise_filter,strides,padding,rate=None,name=None,data_format=None)
    

    函数separable_conv2d实现的功能是函数depthwise_conv2d和conv2d的组合,代码如下:

    import tensorflow as tf
    
    # 1个3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ]
        ]
        ,tf.float32
    )
    
    # 1个2行2列2深度的卷积核depthwiseFilter
    depthwise_filter=tf.constant(
        [
            [[[3],[1]],[[-2],[2]]],
            [[[-1],[-3]],[[4],[5]]]
        ]
        ,tf.float32
    )
    
    # 1行1列2深度的卷积核pointwiseFilter
    pointwise_filter=tf.constant(
        [
            [[[-1],[1]]]
        ]
        ,tf.float32
    )
    
    # 分离卷积
    result=tf.nn.separable_conv2d(x,depthwise_filter,pointwise_filter,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(result))
    
    [[[[ 20.]
       [  9.]]
    
      [[-24.]
       [ 29.]]]]
    

    假设有1个3行3列2深度的三维张量,先与3个2行2列2深度的卷积核进行depthwise_conv2d卷积,其结果的深度为6,然后与2个1行1列6深度的卷积核conv2d卷积,最后结果的深度为2,具体代码如下:

    import tensorflow as tf
    
    # 1个3行3列2深度
    x=tf.constant(
        [
            [
                [[2,5],[3,3],[8,2]],
                [[6,1],[1,2],[5,4]],
                [[7,9],[2,3],[-1,3]]
            ]
        ]
        ,tf.float32
    )
    
    # 3个2行2列2深度的卷积核depthwiseFilter
    depthwise_filter=tf.constant(
        [
            [[[3,1,-3],[1,-1,7]],[[-2,2,-5],[2,7,3]]],
            [[[-1,3,1],[-3,-8,6]],[[4,6,8],[5,9,-5]]]
        ]
        ,tf.float32
    )
    
    # 2个1行1列6深度的卷积核pointwiseFilter
    pointwise_filter=tf.constant(
        [
            [[[0,0],[1,0],[0,1],[0,0],[0,0],[0,0]]]
        ],tf.float32
    )
    
    # 分离卷积
    result=tf.nn.separable_conv2d(x,depthwise_filter,pointwise_filter,[1,1,1,1],'VALID')
    
    session=tf.Session()
    
    print(session.run(result))
    
    [[[[ 32.  -7.]
       [ 52.  -8.]]
    
      [[ 41.   0.]
       [ 11. -34.]]]]
    

    转载于:https://www.cnblogs.com/LQ6H/p/10339963.html

    展开全文
  • Opencv 图像平滑基础二维离散卷积维离散卷积实现二维离散卷积运算可分离卷积核离散卷积核的性质1、full卷积的性质2、same卷积的性质 二维离散卷积 要进行二维离散卷积运算首先需要对矩阵边界进行扩充,opencv提供...

    二维离散卷积

    要进行二维离散卷积运算首先需要对矩阵边界进行扩充,opencv提供了函数copyMakeBorder

    import cv2
    import numpy as np
    src = np.array([[5,1,7],[1,5,9],[2,6,2]])
    dst = cv2.copyMakeBorder(src,2,2,2,2,cv2.BORDER_REFLECT_101)
    dst
    
    array([[2, 6, 2, 6, 2, 6, 2],
           [9, 5, 1, 5, 9, 5, 1],
           [7, 1, 5, 1, 7, 1, 5],
           [9, 5, 1, 5, 9, 5, 1],
           [2, 6, 2, 6, 2, 6, 2],
           [9, 5, 1, 5, 9, 5, 1],
           [7, 1, 5, 1, 7, 1, 5]], dtype=int32)
    

    实现二维离散卷积运算

    使用Python的科学计算包Scipy提供的函数:convolve2d(in1m in2m mode = ‘full’, boundary = ‘fill’, fillvalue = 0)

    #计算任意卷积核且任意指定的锚点的same卷积
    import numpy as np
    from scipy import signal
    #主函数
    if __name__ == "__main__":
        #输入矩阵
        I = np.array([[1,2],[3,4]],np.float32)
        # I 的高和宽
        H1, W1 = I.shape[:2]
        #卷积核
        K = np.array([[-1,-2],[2,1]],np.float32)
        # K的高、宽
        H2, W2 = K.shape[:2]
        #计算full卷积
        c_full = signal.convolve2d(I,K,mode = 'full')
        #指定锚点位置
        kr, kc = 0,0
        #更具锚点位置,从full卷积中截取得到same卷积
        c_same = c_full[H2-kr-1:H1+H2-kr-1,W2-kc-1:W1+W2-kc-1]
        print(c_same)
    
    [[-5. -6.]
     [11.  4.]]
    

    可分离卷积核

    一个卷积核至少由两个尺寸比它小的卷积核full卷积而成,并且在计算过程中在所有边界处均进行扩充0的操作

    full卷积不满足交换律,但是一维水平方向和一维垂直方向上的卷积核的full卷积是满足交换律的

    import numpy as np
    from scipy import signal
    # 主函数
    if __name__ == "__main__":
        kernel1 = np.array([[1,2,3]], np.float32)
        kernel2 = np.array([[4],[5],[6]], np.float32)
        #计算两个核的全卷积
        kernel = signal.convolve2d(kernel1,kernel2,mode = 'full')
        print(kernel)
    
    [[ 4.  8. 12.]
     [ 5. 10. 15.]
     [ 6. 12. 18.]]
    

    离散卷积核的性质

    1、full卷积的性质

    如果卷积核可以分离则满足结合利率

    import numpy as np
    from scipy import signal
    I = np.array([[1,2,3,10,12],
                  [32,43,12,4,190],
                  [12,234,78,0,12],
                  [43,90,32,8,90],
                  [71,12,4,98,123]],np.float32)
    K = np.array([[1,0,-1],
                  [1,0,-1],
                  [1,0,-1]],np.float32)
    #计算两者的full卷积
    I_Kernel = signal.convolve2d(I,K,mode='full',boundary = 'fill',fillvalue = 0)
    print(I_Kernel)
    
    
    [[   1.    2.    2.    8.    9.  -10.  -12.]
     [  33.   45.  -18.  -31.  187.  -14. -202.]
     [  45.  279.   48. -265.  121.  -14. -214.]
     [  87.  367.   35. -355.  170.  -12. -292.]
     [ 126.  336.  -12. -230.  111. -106. -225.]
     [ 114.  102.  -78.    4.  177. -106. -213.]
     [  71.   12.  -67.   86.  119.  -98. -123.]]
    
    kernel1 = np.array([[1],[1],[1]],np.float32)
    kernel2 = np.array([[1,0,-1]],np.float32)
    I_k1 = signal.convolve2d(I,kernel1,mode='full',boundary = 'fill',fillvalue = 0)
    I_k1_k2 = signal.convolve2d(I_k1,kernel2,mode='full',boundary = 'fill',fillvalue = 0)
    print(I_k1_k2)
    
    [[   1.    2.    2.    8.    9.  -10.  -12.]
     [  33.   45.  -18.  -31.  187.  -14. -202.]
     [  45.  279.   48. -265.  121.  -14. -214.]
     [  87.  367.   35. -355.  170.  -12. -292.]
     [ 126.  336.  -12. -230.  111. -106. -225.]
     [ 114.  102.  -78.    4.  177. -106. -213.]
     [  71.   12.  -67.   86.  119.  -98. -123.]]
    

    2、same卷积的性质

    讨论一种情况:Kernel的宽和高均为奇数,并且可以分离为水平和垂直方向一维卷积核,则满足结合律,利用此规律可以降低运算量

    c_same = signal.convolve2d(I,K,mode='same',boundary = 'fill',fillvalue = 0)
    print(c_same)
    
    [[  45.  -18.  -31.  187.  -14.]
     [ 279.   48. -265.  121.  -14.]
     [ 367.   35. -355.  170.  -12.]
     [ 336.  -12. -230.  111. -106.]
     [ 102.  -78.    4.  177. -106.]]
    
    #利用卷积核的分离性进行same卷积运算,且采用0扩充边界,如果不采用0扩充边界卷积结果的上下左右边界的值会不同,在图像处理中可以忽略
    c_same1 = signal.convolve2d(I,kernel1,mode='same',boundary = 'fill',fillvalue = 0)
    c_same2 = signal.convolve2d(c_same1,kernel2,mode='same',boundary = 'fill',fillvalue = 0)
    print(c_same2)
    
    [[  45.  -18.  -31.  187.  -14.]
     [ 279.   48. -265.  121.  -14.]
     [ 367.   35. -355.  170.  -12.]
     [ 336.  -12. -230.  111. -106.]
     [ 102.  -78.    4.  177. -106.]]
    
    展开全文
  • 在OpenCV里实现二维离散卷积1

    千次阅读 2019-09-07 22:36:48
    为了解决这个问题,在大多数情况之下,通过平滑技术(或称为滤波技术)进行抑制或者消除,常用的平滑处理算法包括二维离散卷积的高斯平滑、均值平滑,基于统计方法的中值平滑等等。 在进行平滑算法之前,先来温习...
  • 幅图像都包含某种程度的噪声,噪声可以理解为由...常用的平滑处理算法包括基于二维离散卷积的高斯平滑、均值平滑,基于统计学方法的中值平滑,具备保持边缘作用的平滑算法的双边滤波。 在介绍基于二维离散卷...
  • 从结果里可以看到,当卷积使用full计算时,输出结果会比原来输入的图像变大,这样会破坏...当卷积核的中心(K)与image的边角重合时,开始做卷积运算,可见卷积核的运动范围比full模式小了圈。注意:这里的same还...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,940
精华内容 5,176
关键字:

一维离散卷积