卷積,這個詞大家應該都不陌生,數學中傅立葉變換的時候,物理中信號處理的時候,圖像處理中濾波的時候、提取邊緣的時候,還有深度學習中卷積神經網絡的時候,處處可見卷積的影子。卷積在圖像處理中的應用非常廣泛,可以說理解了卷積,就可以理解圖像處理算法的半壁江山,也不知道這個說法是否誇張了。
但是都說卷積卷積,那捲積到底是怎麼個卷法呢?本文嘗試解答這一問題。
理解的卷積計算過程
想要理解卷積,一些必要的數學公式是少不了的,放心吧,就下面這一個公式了,所有討論圍繞這一個公式展開。
我們從維基百科中對於卷積的解釋引入:
設:, 是 上的兩個可積函數,作積分:
可以證明,關於幾乎所有的 ${\displaystyle x\in (-\infty ,\infty )} $,上述積分是存在的。這樣,隨着 的不同取值,這個積分就定義了一個新函數$ {\displaystyle h(x)}$ ,稱爲函數 與 的卷積,記爲 。
我們提取下重點公式寫在下面,記爲公式1:
以上公式1最令人迷惑也是最需要注意的部分在於,在等式的左邊,自變量是,然而等式的右邊自變量卻變成了,更令人疑惑的是——右邊自變量不是是也就算了,竟然還出現了一個。
那麼問題來了,和,到底哪個在變?還是兩個都在變?如果是都在變,那到底是怎麼個變法?
這些問題還是需要慢慢道來。我們先看一個卷積稍微通俗一點的解釋。
卷積
(1)即是通過兩個函數和生成第三個函數的一種數學算子。
(2)表徵函數f與經過翻轉和平移的g的乘積函數所圍成的的曲邊梯形的面積。
上面兩句話都非常重要,我們從第二句話開始看,第二句話中包含了以下四個重點信息:
- 翻轉
- 平移
- 乘積
- 積分(函數圍成的面積不就是積分麼?)
我們一個一個來看。先看右邊,我們不妨先令, 也就是不變而變的情況。於是公式1就變成了公式2:
1. 翻轉
先看翻轉,怎麼翻轉一個函數呢,想一下最簡單的,不難發現,翻轉之後即爲。我用Python畫出了這倆函數的圖像,看起來更爲直觀。
2. 平移
然後看一下一個函數如何平移,仍然以爲例,回一下我們中學學過的數學知識,也許還能記起來,就是由向右平移得到的。我們仍然以圖說話,用Python作圖如下,分別取值爲。
3. 乘積
現在我們只看公式的右邊部分:
KaTeX parse error: \tag works only in display equations
現在我們可以知道就是翻轉之後又向右平移了個單位。這時候需要考慮另一個函數了。這裏 我們繼續舉個例子,不妨令。
我們繼續用Python畫出如下圖所示:
4. 積分
現在是較爲完整的公式3的樣子了,這裏爲了能夠更好地表達,我們把區間從改爲,即畫出
KaTeX parse error: \tag works only in display equations
注意了,在上面的所有過程中,一直是不變的,變的是。即我們上面一直是在做的是公式2右邊的計算,公式2如下:
不論怎麼變化,最後一旦積分,等式右邊就成了一個確定的數字,一個常量。一個對應一個嘛。此時我們可以繼續看公式左邊了,我們直接看公式1:
左邊換下位置我們也許會更好理解,即。也即之前提到的一句話:卷積即是通過兩個函數和生成第三個函數的一種數學算子。
總結一下,卷積計算過程可以分解爲四步:翻轉、平移、乘積、積分。
卷積爲什麼叫“卷積”?
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()