日萌社
人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)
快速瞭解傅立葉變換(播放PPT即能動態地顯示gif圖)
有一種運算,把微積分變成加減乘除,它叫傅立葉變換。
傅里葉變換將信號分解爲一組餘弦函數的過程。
那傅立葉變化到底怎麼解決問題的呢?
其實,傅立葉變換(的三角函數形式)的基本原理是:多個正餘弦波疊加(紅色)可以用來近似任何一個原始的周期函數(藍色)。
其實,當我們去買菜的時候,各種蔬菜都不一樣,但都能轉換成“n個1斤砝碼+m個1兩砝碼”的組合。
此時,那我們把上圖末尾處藍色的豎線就想象成3個1號波+5個2號波的組合等等。
一下子計算就簡單許多了,使得積分,微分,成了最簡易的計算:加減乘除。在處理上有多方便就不用說了。
因此,傅立葉變換在數學裏面,這本身就是一種解微分方程的方法。
也正因爲傅立葉變換有趣的簡化方式,使得傅立葉變換成爲工程和物理領域裏最重要的數學公式之一。
3.7 圖像變換-傅里葉變換
學習目標
- 理解傅里葉變換
- 知道傅里葉變換的相關概念
- 知道傅里葉變換的分類
- 知道怎麼在圖像中進行傅里葉變換
- 瞭解傅里葉變換在圖像應用中的意義
- 知道在OPenCV中怎麼實現傅里葉變換
- 瞭解頻域濾波的分類
1 傅里葉變換的理解
傅里葉變換是由法國的一位數學家Joseph Fourier在18世紀提出來的,他認爲:任何連續週期的信號都可以由一組適當的正弦曲線組合而成。
傅里葉變換是描述信號的需要,它能夠反映信號的特徵,並可以使用特徵值進行量化,比如正弦波可以使用幅值和頻率進行描述。下面這幅圖是變壓器空載電流的輸入波形:
它看起來和正弦波很相近,但很難定量的描述其特徵,採用傅里葉變換後,得到下述的頻譜圖(幅值):
從該頻譜圖中可以清楚的看到,主要包括3,5,7,9次諧波,我們就可以對原信號進行描述。
傅里葉變換是一種信號分析方法,它使我們能夠對信號的構成和特點進行深入和定量的研究,把信號通過頻譜的方式進行準確的、定量的描述。
那我們爲什麼要把信號分解爲正弦波的組合,而不是其他波形呢?
傅里葉變換是信號的分析方法,目的就是要簡化問題,而不是將其變複雜,傅里葉選擇了正弦波,而沒有選擇其他波形,是因爲正弦波有任何其他波形不具有的特點:正弦波輸入至任何線性系統中,不會產生新的頻率成分,輸出的仍是正弦波,改變的僅僅是幅值和相位。用單位幅值的不同頻率的正弦波輸入至某線性系統,記錄其輸出正弦波的幅值和頻率的關係,就得到該系統的幅頻特性,記錄輸出正弦波的相位和頻率的關係,就得到該系統的相頻特性。線性系統是自動控制研究的主要對象,我們只要研究系統對正弦波的輸入輸出關係,就可以知道該系統對任意輸入信號的響應。這是傅里葉變換的最主要的意義。
2 傅里葉變換中相關概念
2.1 時域和頻域
傅里葉變換是將難以處理的時域信號轉換成易於分析的頻域信號,那頻域和時域到底是什麼呢?
時域:時域是真實的世界,是唯一存在的域。從我們出生開始,所接觸的這個世界就是隨着時間在變化的,如花開花落,四季變換,生老病死等。以時間作爲參照來分析動態世界的方法我們稱其爲時域分析。
比如說一段音樂,就是一個隨時間變化的震動,這就是時域的表示,如下圖:
頻域:頻域它不是真實的,而是一個數學構造。頻域是一個遵循特定規則的數學範疇,也被一些學者稱爲上帝視角。結合上面對時域的理解,如果時域是運動永不停止的,那麼頻域就是靜止的。 正弦波是頻域中唯一存在的波形,這是頻域中最重要的規則,即正弦波是對頻域的描述,因爲頻域中的任何波形都可用正弦波合成。
在看上面那段音樂,我們可以將其表示成頻域形式,就是一個永恆的音符。
而對於信號來說,信號強度隨時間的變化規律就是時域特性,信號是由哪些單一頻率的信號合成的就是頻域特性,傅里葉變換實質就是是頻域函數和時域函數的轉換。
那頻域與時域之間的關係是什麼樣的呢?利用正弦函數的疊加成一個矩形,不僅僅是矩形,你能想到的任何波形都是可以如此方法用正餘弦波疊加起來的。如下圖所示,時域是永遠隨着時間的變化而變化的,而頻域就是裝着裝着正餘弦波的空間。
從時域來看,我們會看到一個近似爲矩形的波,而我們知道這個矩形的波可以拆分爲一些正弦波的疊加。而從頻域方向來看,我們就看到了每一個正餘弦波的幅值,每兩個正弦波之間都還有一條直線,那並不是分割線,而是振幅爲 0 的正弦波!也就是說,爲了組成特殊的曲線,有些正弦波成分是不需要的。隨着疊加的遞增,所有正弦波中上升的部分逐漸讓原本緩慢增加的曲線不斷變陡,而所有正弦波中下降的部分又抵消了上升到最高處時繼續上升的部分使其變爲水平線。一個矩形就這麼疊加而成了。
我們看下面的動圖理解,如下所示:
2.2 頻譜和相位譜
在傅里葉變換中怎麼描述變換後的結果呢?有兩個概念:頻譜和相位譜。
頻譜:將信號分解爲若干不同頻率的正弦波,那麼每一個正弦波的幅度,就叫做頻譜,也叫做幅度譜。
相位譜:頻譜只代表了一個正弦函數的幅值,而要準確描述一個正弦函數,我們不僅需要幅值,還需要相位,不同相位決定了波的位置,所以對於頻域分析,僅僅有頻譜(振幅譜)是不夠的,我們還需要一個相位譜
如上圖所示:投影點我們用粉色點來表示,紅色的點表示離正弦函數頻率軸最近的一個峯值,而相位差就是粉色點和紅色點水平距離。將相位差畫到一個座標軸上就形成了相位譜
3 傅里葉變換
根據原信號的屬性,我們可以將傅里葉變換分爲以下幾種:
在實際應用較多的是傅里葉變換、傅里葉級數離散傅里葉變換,我們對其進行分別介紹。
任意波形都可以通過正弦波的疊加來表示,正弦波可以通過歐拉公式寫成指數的形式,歐拉公式如下:
所以以下內容都是以指數形式進行展示。
3.1 傅里葉級數
任意的週期連續信號都可以使用正弦波疊加而成,這叫做傅里葉級數,寫成指數形式如下所示:
3.2 傅里葉變換
對於非週期的連續信號,也可以使用正弦信號來逼近,這時我們將非週期信號看做週期無限大的週期信號,則有:
3.3 離散傅里葉變換(DFT)
由於數字信號處理是希望在計算機上實現各種運算和變換,其所涉及的變量和運算都是離散的,因此對於數字信號處理,應該找到在時域和頻域都是離散的傅里葉變換,即離散傅里葉變換。
對於非週期的離散信號進行傅里葉變換就是離散傅里葉變換,其計算方法如下所示:
4 傅里葉變換在圖像中的應用
4.1 圖像中的傅里葉變換
圖像是二維的離散信號,所以我們在對圖像進行二維傅里葉變換。對於M*N的一幅圖像的離散二維傅里葉變換,公式如下:
4.2 圖像傅里葉變換的物理意義
圖像的頻率是表徵圖像中灰度變化劇烈程度的指標,是灰度在平面空間上的梯度。如:大面積的沙漠在圖像中是一片灰度變化緩慢的區域,對應的頻率值很低;而對於地表屬性變換劇烈的邊緣區域在圖像中是一片灰度變化劇烈的區域,對應的頻率值較高。傅里葉變換在實際中有非常明顯的物理意義,從物理效果看,傅里葉變換是將圖像從空間域轉換到頻率域,其逆變換是將圖像從頻率域轉換到空間域。換句話說,傅里葉變換的物理意義是將圖像的灰度分佈函數變換爲圖像的頻率分佈函數。
傅里葉逆變換是將圖像的頻率分佈函數變換爲灰度分佈函數。
我們在做DFT時是將圖像的空域和頻域沿x和y方向進行無限週期拓展的,如下圖所示:
4.3 在opencv中實現圖像的傅里葉變換
在OPenCV中實現圖像的傅里葉變換,使用的是:
正變換:
dft = cv2.dft(src, dst=None)
參數:
- src: 輸入圖像,要轉換成np.float32格式
- dst:參數是可選的, 決定輸出數組的大小。默認輸出數組的大小和輸入圖像大小一樣。如果輸出結果比輸入圖像大,輸入圖像就需要在進行變換前補 0。如果輸出結果比輸入圖像小的話,輸入圖像就會被切割。
返回:
- dft: 傅里葉變換後的結果,有兩個通道,第一個通道是結果的實數部分,第二個通道是結果的虛數部分。我們需要在此基礎上計算傅里葉變換的頻譜和相位。
逆變換:
img = cv.idft(dft)
參數:
- dft: 圖像的頻域表示
返回:
- img: 圖像的空域表示
實現:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/deer.jpeg',0)
# 2 傅里葉變換
# 2.1 正變換
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
# 2.2 頻譜中心化
dft_shift = np.fft.fftshift(dft)
# 2.3 計算頻譜和相位譜
mag, angle = cv.cartToPolar(dft_shift[:,:,0], dft_shift[:,:,1], angleInDegrees=True)
mag=20*np.log(mag)
# 3 傅里葉逆變換
# 3.1 反變換
img_back = cv.idft(dft)
# 3.2 計算灰度值
img_back = cv.magnitude(img_back[:,:,0],img_back[:,:,1])
# 4 圖像顯示
plt.figure(figsize=(10,8))
plt.subplot(221),plt.imshow(img, cmap = 'gray')
plt.title('輸入圖像'), plt.xticks([]), plt.yticks([])
plt.subplot(222),plt.imshow(mag, cmap = 'gray')
plt.title('頻譜'), plt.xticks([]), plt.yticks([])
plt.subplot(223),plt.imshow(angle, cmap = 'gray')
plt.title('相位譜'), plt.xticks([]), plt.yticks([])
plt.subplot(224),plt.imshow(img_back, cmap = 'gray')
plt.title('逆變換結果'), plt.xticks([]), plt.yticks([])
plt.show()
結果展示:
4.4 頻域濾波
圖像變換到頻域後,就可以進行頻域濾波,主要包括:高通濾波,低通濾波,帶通濾波和帶阻濾波。
1 高通和低通濾波器
我們知道,圖像在經過傅里葉變換後,經頻譜中心化後,從中間到外面,頻率上依次是從低頻到高頻的。那麼我們假設把中間規定一小部分去掉,是不是相對於把低頻信號去掉了呢?這也就是相當於進行了高通濾波。
這個濾波模板如下圖所示:
其中黑色部分爲0,白色部分爲1,我們將這個模板與圖像的傅里葉變換相與就實現了高通濾波。如下所示:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/deer.jpeg',0)
# 2 設計高通濾波器(傅里葉變換結果中有兩個通道,所以高通濾波中也有兩個通道)
rows,cols = img.shape
mask = np.ones((rows,cols,2),np.uint8)
mask[int(rows/2)-30:int(rows/2)+30,int(cols/2)-30:int(cols/2)+30,:] = 0
# 3 傅里葉變換
# 3.1 正變換
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
# 3.2 頻譜中心化
dft_shift = np.fft.fftshift(dft)
# 3.3 濾波
dft_shift = dft_shift * mask
# 3.4 頻譜去中心化
dft_shift = np.fft.fftshift(dft_shift)
# 3 傅里葉逆變換
# 3.1 反變換
img_back = cv.idft(dft_shift)
# 3.2 計算灰度值
img_back = cv.magnitude(img_back[:,:,0],img_back[:,:,1])
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('輸入圖像'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('高通濾波結果'), plt.xticks([]), plt.yticks([])
plt.show()
從結果中可以看出,高通濾波器有利於提取圖像的輪廓,圖像的輪廓或者邊緣或者一些噪聲處,灰度變化劇烈,那麼在把它們經過傅里葉變換後。就會變成高頻信號(高頻是捕捉細節的),所以在把圖像低頻信號濾掉以後剩下的自然就是輪廓了。
現在我們看下低通濾波的效果,構造一個低通濾波器很簡單,只要把上述模板中的1改爲0,0改爲1即可。把設計高通濾波器部分的代碼改成如下所示:
rows,cols = img.shape
mask = np.zeros((rows,cols,2),np.uint8)
mask[int(rows/2)-30:int(rows/2)+30,int(cols/2)-30:int(cols/2)+30,:] = 1
低通濾波的效果如下圖所示:
從結果中可看到低通濾波後圖像輪廓變模糊了,圖像的大部分信息基本上都保持了。圖像的主要信息都集中在低頻上,所以低通濾波器的效果是這樣也是能夠理解的。上述的高通、低通濾波器的構造有0,1構成的理想濾波器,也是最簡單的濾波器,另一些其它的濾波器。比方說高斯濾波器,butterworth濾波器等等,如下圖所示:
2 帶通和帶阻濾波器
我們把高通和低通的一部分結合在模板中就形成了帶通濾波器,它容許一定頻率範圍信號通過, 但減弱(或減少)頻率低於於下限截止頻率和高於上限截止頻率的信號的通過,如下圖所示:
還是以理想的帶通濾波器演示如下,將構建的濾波的代碼修改如下:
rows,cols = img.shape
mask1 = np.ones((rows,cols,2),np.uint8)
mask1[int(rows/2)-8:int(rows/2)+8,int(cols/2)-8:int(cols/2)+8] = 0
mask2 = np.zeros((rows,cols,2),np.uint8)
mask2[int(rows/2)-80:int(rows/2)+80,int(cols/2)-80:int(cols/2)+80] = 1
mask = mask1*mask2
結果如下所示:
這就是帶通的效果,它既能保留一部分低頻,也能保留一部分高頻。至於保留多少,根據需求選擇就可以了。
帶阻濾波器減弱(或減少)一定頻率範圍信號, 但容許頻率低於於下限截止頻率和高於上限截止頻率的信號的通過,如下示:
在代碼中將設計濾波器部分改爲如下所示:
mask = np.ones((rows,cols,2),np.uint8)
mask[int(rows/2)+80:int(rows/2)+150,int(cols/2)-150:int(cols/2)+150] = 0
mask[int(rows/2)-150:int(rows/2)-80,int(cols/2)-150:int(cols/2)+150] = 0
mask[int(rows/2)-150:int(rows/2)+150,int(cols/2)+80:int(cols/2)+150] = 0
mask[int(rows/2)-150:int(rows/2)+150,int(cols/2)-150:int(cols/2)-80] = 0
plt.imshow(mask[:,:,1],cmap=plt.cm.gray)
結果如下所示:
從結果中可看到帶阻濾波器保持了原圖像的大部分信息,圖像的主要信息都集中在低頻上,而邊緣輪廓信息都在高頻位置。帶阻濾波器濾除了中頻信息,保留了低頻和高頻信息,所以對圖像的信息破壞是比較小的。:
總結:
-
傅里葉變換的理解
任何連續週期的信號都可以由一組適當的正弦曲線組合而成
-
相關概念:
時域:以時間作爲參照來分析動態世界的方法
頻域:頻域它不是真實的,而是一個數學構造
幅度譜:將信號分解爲若干不同頻率的正弦波,那麼每一個正弦波的幅度,就叫做頻譜,也叫做幅度譜
相位譜:每一個正弦波的相位,就叫做相位譜
-
傅里葉變換分類
傅里葉級數:任意的週期連續信號的傅里葉變換
傅里葉變換:非週期連續信號
離散傅里葉變換:非週期離散信號
-
圖像中的應用
-
二維傅里葉變換
-
意義:將圖像的灰度分佈函數變換爲圖像的頻率分佈函數。
-
API:
cv.dft()
cv.idft()
-
濾波:高通,低通,帶通,帶阻
-
In [1]:
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
In [2]:
img = cv.imread('./image/deer.jpeg',0)
In [3]:
plt.imshow(img,"gray")
Out[3]:
<matplotlib.image.AxesImage at 0x11c145090>
In [4]:
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
In [5]:
dft_shift = np.fft.fftshift(dft)
In [6]:
mag,angle = cv.cartToPolar(dft_shift[:,:,0],dft_shift[:,:,1])
In [7]:
mag = 20*np.log(mag)
In [8]:
plt.imshow(mag,'gray')
Out[8]:
<matplotlib.image.AxesImage at 0x11c337250>
In [9]:
plt.imshow(angle,'gray')
Out[9]:
<matplotlib.image.AxesImage at 0x11cea9e10>
In [10]:
img_back = cv.idft(dft)
In [11]:
img_back= cv.magnitude(img_back[:,:,0],img_back[:,:,1])
In [12]:
plt.imshow(img_back,"gray")
Out[12]:
<matplotlib.image.AxesImage at 0x105df2e90>
高通濾波器
In [13]:
rows,cols = img.shape
In [14]:
highmask = np.ones((rows,cols,2),np.uint8)
In [15]:
highmask[int(rows/2)-30:int(rows/2)+30,int(cols/2)-30:int(cols/2)+30]=0
In [16]:
highdft = dft_shift*highmask
In [17]:
highdft = np.fft.fftshift(highdft)
In [18]:
highimg = cv.idft(highdft)
In [19]:
highimg = cv.magnitude(highimg[:,:,0],highimg[:,:,1])
In [20]:
plt.imshow(highimg,"gray")
Out[20]:
<matplotlib.image.AxesImage at 0x11d778590>
低通濾波
In [21]:
lowmask = np.zeros((rows,cols,2),np.uint8)
In [22]:
lowmask[int(rows/2)-30:int(rows/2)+30,int(cols/2)-30:int(cols/2)+30]= 1
In [23]:
lowdft = dft_shift*lowmask
In [24]:
lowdft = np.fft.fftshift(lowdft)
In [25]:
lowimg = cv.idft(lowdft)
In [26]:
lowimg = cv.magnitude(lowimg[:,:,0],lowimg[:,:,1])
In [27]:
plt.imshow(lowimg,"gray")
Out[27]:
<matplotlib.image.AxesImage at 0x11d429490>