卷積到底是怎麼【卷】的

卷積,這個詞大家應該都不陌生,數學中傅立葉變換的時候,物理中信號處理的時候,圖像處理中濾波的時候、提取邊緣的時候,還有深度學習中卷積神經網絡的時候,處處可見卷積的影子。卷積在圖像處理中的應用非常廣泛,可以說理解了卷積,就可以理解圖像處理算法的半壁江山,也不知道這個說法是否誇張了。

但是都說卷積卷積,那捲積到底是怎麼個卷法呢?本文嘗試解答這一問題。

理解的卷積計算過程

想要理解卷積,一些必要的數學公式是少不了的,放心吧,就下面這一個公式了,所有討論圍繞這一個公式展開。

我們從維基百科中對於卷積的解釋引入:

設:f(x)f(x),g(x)g(x)RR上的兩個可積函數,作積分:

f(τ)g(xτ)dτ{\displaystyle \int _{-\infty }^{\infty }f(\tau )g(x-\tau )\,\mathrm {d} \tau }

可以證明,關於幾乎所有的 ${\displaystyle x\in (-\infty ,\infty )} $,上述積分是存在的。這樣,隨着 x{\displaystyle x} 的不同取值,這個積分就定義了一個新函數$ {\displaystyle h(x)}$ ,稱爲函數 f{\displaystyle f}g{\displaystyle g}的卷積,記爲 h(x)=(fg)(x){\displaystyle h(x)=(f*g)(x)}

我們提取下重點公式寫在下面,記爲公式1:

h(x)=(fg)(x)=f(τ)g(xτ)dτ(1){\displaystyle h(x)=(f*g)(x)} = {\displaystyle \int _{-\infty }^{\infty }f(\tau )g(x-\tau )\,\mathrm {d} \tau }\tag{1}

以上公式1最令人迷惑也是最需要注意的部分在於,在等式的左邊,自變量是xx,然而等式的右邊自變量卻變成了τ\tau,更令人疑惑的是——右邊自變量不是xxτ\tau也就算了,竟然還出現了一個xx

那麼問題來了,xxτ\tau,到底哪個在變?還是兩個都在變?如果是都在變,那到底是怎麼個變法?

這些問題還是需要慢慢道來。我們先看一個卷積稍微通俗一點的解釋。

卷積

(1)即是通過兩個函數ffgg生成第三個函數的一種數學算子。

(2)表徵函數f與經過翻轉和平移的g的乘積函數所圍成的的曲邊梯形的面積。

上面兩句話都非常重要,我們從第二句話開始看,第二句話中包含了以下四個重點信息:

  • 翻轉
  • 平移
  • 乘積
  • 積分(函數圍成的面積不就是積分麼?)

我們一個一個來看。先看右邊,我們不妨先令x=x0x=x_0, 也就是xx不變而τ\tau變的情況。於是公式1就變成了公式2:

h(x0)=(fg)(x0)=f(τ)g(x0τ)dτ(2){\displaystyle h(x_0)=(f*g)(x_0)} = {\displaystyle \int _{-\infty }^{\infty }f(\tau )g(x_0-\tau )\,\mathrm {d} \tau }\tag{2}

1. 翻轉

先看翻轉,怎麼翻轉一個函數呢,想一下最簡單的f(τ)=τf(\tau)=\tau,不難發現,f(τ)f(\tau)翻轉之後即爲f(τ)f(-\tau)。我用Python畫出了這倆函數的圖像,看起來更爲直觀。

翻轉

2. 平移

然後看一下一個函數如何平移,仍然以f(τ)=τf(\tau)=\tau爲例,回一下我們中學學過的數學知識,也許還能記起來,f(x0τ)f(x_0-\tau)就是由f(τ)f(-\tau)向右平移x0x_0得到的。我們仍然以圖說話,用Python作圖如下,x0x_0分別取值爲20,40,60,8020,40,60,80

平移

3. 乘積

現在我們只看公式的右邊部分:

KaTeX parse error: \tag works only in display equations

現在我們可以知道g(x0τ)g(x_0-\tau)就是g(τ)g(\tau)翻轉之後又向右平移x0x_0個單位。這時候需要考慮另一個函數f(τ)f(\tau)了。這裏 我們繼續舉個例子,不妨令f(τ)=sin(τ10)f(\tau)=sin(\frac{\tau}{10})

我們繼續用Python畫出f(τ)g(x0τ)f(\tau )g(x_0-\tau )如下圖所示:

乘積

4. 積分

現在是較爲完整的公式3的樣子了,這裏爲了能夠更好地表達,我們把區間從(,)(-\infty ,\infty )改爲(50,50)(-50,50),即畫出

KaTeX parse error: \tag works only in display equations

積分

注意了,在上面的所有過程中,xx一直是不變的,變的是τ\tau。即我們上面一直是在做的是公式2右邊的計算,公式2如下:

h(x0)=(fg)(x0)=f(τ)g(x0τ)dτ(2){\displaystyle h(x_0)=(f*g)(x_0)} = {\displaystyle \int _{-\infty }^{\infty }f(\tau )g(x_0-\tau )\,\mathrm {d} \tau }\tag{2}

不論τ\tau怎麼變化,最後一旦積分,等式右邊就成了一個確定的數字,一個常量。一個xx對應一個yy嘛。此時我們可以繼續看公式左邊了,我們直接看公式1:

h(x)=(fg)(x)=f(τ)g(xτ)dτ(1){\displaystyle h(x)=(f*g)(x)} = {\displaystyle \int _{-\infty }^{\infty }f(\tau )g(x-\tau )\,\mathrm {d} \tau }\tag{1}

左邊換下位置我們也許會更好理解,即(fg)(x)=h(x){\displaystyle (f*g)(x) = h(x)}。也即之前提到的一句話:卷積即是通過兩個函數ffgg生成第三個函數的一種數學算子。

總結一下,卷積計算過程可以分解爲四步:翻轉、平移、乘積、積分

卷積爲什麼叫“卷積”?

1. 卷積之【卷】

那麼問題來了?卷積爲什麼要叫“卷積”呢?換言之,卷積之“卷”和卷積之“積”分別是什麼含義?

這裏想像一下如果我們要捲起一張A4紙,需要怎麼做?

(1)首先我們需要提起對着自己一條邊,向上翻轉使之對着自己身體前方——翻轉!

在這裏插入圖片描述

(2)然後繼續向下打個圈之後,就可以向前推了——平移!

在這裏插入圖片描述

看到沒?翻轉!平移!

你肯定還記得上面說的卷積計算的四個過程:翻轉、平移、乘積、積分。卷積之“卷”,你明白了嗎?

本文完


什麼?還不能走?把畫圖的源碼交出來?

# coding:utf-8  
""" 
Author:  CVPy-冰不語
Date:    2019/11/26
"""  
  
import numpy as np  
import matplotlib.pyplot as plt  


# 定義函數f(x)
def f(x):
    """$f\ (\\tau)$"""
    return x


# 定義函數g(x)
def g(x):
    """$f(x)=sin(x)$"""
    return np.sin(x/10)


# 設置座標系
def set_ax(ax):
    ax.spines['top'].set_color('none')
    ax.spines['right'].set_color('none')
    ax.spines['bottom'].set_color('deepskyblue')
    ax.spines['left'].set_color('deepskyblue')

    ax.xaxis.set_ticks_position('bottom')
    ax.spines['bottom'].set_position(('data', 0))
    ax.yaxis.set_ticks_position('left')
    ax.spines['left'].set_position(('data', 0))

    ax.set_xticks(np.arange(-100,101, 50))
    # ax.set_yticks(np.arange(-100,101, 50))

    return ax


if __name__ == "__main__":
    # x的取值範圍
    x = np.arange(-100, 100, 0.1)

    # ---------第一幅圖:f(x)和f(-x)----------
    fig = plt.figure(figsize=(6, 6))

    # 左邊畫f(x)
    ax1 = fig.add_subplot(121)
    ax1 = set_ax(ax1)
    ax1.plot(x, f(x), 'orange', label=f.__doc__)
    plt.legend(loc="upper left", bbox_to_anchor=[0, 1],
               ncol=1, fancybox=True)

    # 右邊畫f(-x)
    ax2 = fig.add_subplot(122)
    ax2 = set_ax(ax2)
    plt.plot(x, f(-x), 'orange', label="$f\ (-\\tau)$")
    plt.legend(loc="upper right", bbox_to_anchor=[1, 1],
               ncol=1, fancybox=True)
    plt.show()

    # ---------第二幅圖:f(t-x)----------
    fig2 = plt.figure(figsize=(6, 6))
    ax3 = fig2.add_subplot(111)

    ax3 = set_ax(ax3)
    ax3.set_xticks(np.arange(-100, 101, 20))
    ax3.set_yticks(np.arange(-100, 181, 20))

    for t in [20, 40, 60, 80]:
        plt.plot(x, f(t-x), label="$f\ (x_0 - \\tau) \ \ x_0={0}$".format(t))
    plt.legend(loc="upper right", bbox_to_anchor=[1, 1], ncol=1, fancybox=True)

    plt.show()

    # ---------第三幅圖:g(x) * f(t-x)----------
    t = 80


    def f_mul_g(x, t):
        """$f(\\tau-x)*g(\\tau)$"""
        return f(t-x)*g(x)

    fig3, ax4 = plt.subplots()

    ax4 = set_ax(ax4)
    ax4.set_xticks(np.arange(-100,101, 20))
    # ax4.set_yticks(np.arange(-100,181, 20))

    plt.plot(x, f_mul_g(x, t), label=f_mul_g.__doc__)
    plt.legend(loc="upper right", bbox_to_anchor=[1, 1], ncol=1, fancybox=True)

    plt.show()

    # ---------第四幅圖:g(x) * f(t-x)的積分----------
    import matplotlib.patches as mPatches


    def int_fg(x, t, ax5):
        ax5 = set_ax(ax5)

        plt.plot(x,f_mul_g(x, t), 'orange', label="$f\ (\\tau)*g(x_0-\\tau) \ \  x_0={0}$".format(t))
        plt.legend(loc="upper right", bbox_to_anchor=[1, 1],
                   ncol=1, fancybox=True)

        a = -50
        b = 50
        ix = np.linspace(a,b)
        iy = f_mul_g(ix,t)

        verts = [(a,0)] + list(zip(ix, iy)) + [(b,0)]
        poly = mPatches.Polygon(verts,color='deepskyblue')
        ax5.add_patch(poly)
        # plt.plot(x,g(x),label=g.__doc__)

        plt.text(30, 50, '$\int_a^b f\ (\\tau)*g(x_0-\\tau) \ \  x_0={0}$'.format(t), style='oblique',
                 bbox={'facecolor': 'orange', 'alpha': 0.5, 'pad': 5}, fontsize=15)

    fig4, ax5 = plt.subplots()
    int_fg(x, t, ax5)
    plt.legend(loc="upper right", bbox_to_anchor=[1, 1], ncol=1, fancybox=True)

    plt.ion()

    # for i in range(100):

    #     y = np.random.random()

    #     plt.autoscale()

    #     plt.scatter(i, y)

    #     plt.pause(0.01)

    fig4, ax5 = plt.subplots()
    tn = np.arange(-100,100,5)
    for t in tn:
        plt.cla()
        plt.grid(True)
        plt.autoscale()
        int_fg(x, t, ax5)
        plt.pause(0.01)

        plt.legend(loc="upper right", bbox_to_anchor=[1, 1], ncol=1, fancybox=True)

    plt.show()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章