傅立葉轉換(三)


在〈傅立葉轉換(二)〉的最後談到,接下來要透過 Perlin 雜訊,來認識訊號與影像處理間的關係,這邊先從〈NumPy 與 Perlin 雜訊〉談到的一維 Perlin 雜訊開始。

首先,可以利用〈NumPy 與 Perlin 雜訊〉,來產生一段訊號:

import numpy as np
from math import floor
from random import randint
import matplotlib.pyplot as plt

def blending(t):
    return 6 * (t ** 5) - 15 * (t ** 4) + 10 * (t ** 3)
blending = np.frompyfunc(blending, 1, 1)

def lerp(s1, s2, t):
    return s1 + t * (s2 - s1)
lerp = np.frompyfunc(lerp, 3, 1)

def grad1(n, s):
    return s if n % 2 == 0 else -s
grad1 = np.frompyfunc(grad1, 2, 1)

np.random.seed(10) # 為了每次取得相同雜訊,這邊設定了亂數種子
rand_table = np.random.randint(255, size = 256)
def perlin1(x):
    xi = np.floor(x)
    s = x - xi
    a = rand_table[(xi % 256).astype(np.int)]     
    b = rand_table[((xi + 1) % 256).astype(np.int)] 
    return lerp(grad1(a, s), grad1(b, s - 1), blending(s)) # 全部的雜訊

def singal(t, sample_rate):
    noise_level = 10  # 雜訊的變化程度
    x = np.linspace(0, noise_level * t, int(t * sample_rate), endpoint = False)
    return perlin1(x)

t = 2
sample_rate = 800

samples = singal(t, sample_rate)
x = np.linspace(0, t, int(t * sample_rate), endpoint = False)

plt.title('Perlin noise') 
plt.xlabel('x') 
plt.ylabel('y')
plt.gca().set_aspect(1)
plt.plot(x, samples)  
plt.show()

基本上,你不用特別理會 noise_level,它只是為了取得更多樣的頻率,這邊主要還是關心,對訊號取樣的結果:

傅立葉轉換(二)

那麼這段訊號,是由哪些頻率組成的呢?透過〈傅立葉轉換(二)〉中談到的方式,調整一下頻率觀察範圍,可以得到以下的譜域圖形:

傅立葉轉換(二)

若忽略振幅低的頻率,例如,標準化後小於 0.05 的頻率:

傅立葉轉換(二)

上圖並沒有相位資訊,其實在〈傅立葉轉換(一)〉與〈傅立葉轉換(二)〉中的範例,訊號的相位也都特意設計為 0,必要時也可以畫出相位資訊,例如透過以下的程式碼:

import numpy as np
from math import floor
from random import randint
import matplotlib.pyplot as plt

...signal 等函式的定義同上…略

t = 2
sample_rate = 800

samples = singal(t, sample_rate)
sp = np.fft.fft(samples) 

freq = np.fft.fftfreq(samples.size, d = 1 / sample_rate) # 頻率
amp = np.abs(sp)                   # 振幅
phi = np.arctan2(sp.real, sp.imag) # 相位

amp_normal = amp / np.max(amp)     # 振幅標準化

# 振幅大於 0.05 的部份
amp_greater_005 = np.where(amp_normal > 0.05)
freq2 = freq[amp_greater_005]
amp2 = amp[amp_greater_005]
phi2 = phi[amp_greater_005]

# 正頻率的部份
freq_positive = np.where(freq2 > 0)
freq3 = freq2[freq_positive]
amp3 = amp2[freq_positive]
amp3_normal = amp3 / np.max(amp3)
phi3 = phi2[freq_positive]

# 頻率-振幅
plt.subplot(2, 1, 1)
plt.stem(freq3, amp3_normal)

# 頻率-相位
plt.subplot(2, 1, 2)
plt.stem(freq3, phi3)

plt.show()

可以到這張圖,圖的上方是頻率與振幅,下方是頻率與相位:

傅立葉轉換(二)

也就是說,若透過以下的程式碼:

import numpy as np
from math import floor
from random import randint
import matplotlib.pyplot as plt

...signal 等函式的定義同上…略

t = 2
sample_rate = 800

samples = singal(t, sample_rate)
sp = np.fft.fft(samples) 

freq = np.fft.fftfreq(samples.size, d = 1 / sample_rate) # 頻率
amp = np.abs(sp)                   # 振幅
phi = np.arctan2(sp.real, sp.imag) # 相位

amp_normal = amp / np.max(amp)     # 振幅標準化

# 振幅大於 0.05 的部份
amp_greater_005 = np.where(amp_normal > 0.05)
freq2 = freq[amp_greater_005]
amp2 = amp[amp_greater_005]
phi2 = phi[amp_greater_005]

# 正頻率的部份
freq_positive = np.where(freq2 > 0)
freq3 = freq2[freq_positive]
amp3 = amp2[freq_positive]
amp3_normal = amp3 / np.max(amp3)
phi3 = phi2[freq_positive]

# 繪圖
ax = plt.axes(projection='3d')
ax.set_xlabel('t')
ax.set_ylabel('f')
ax.set_zlabel('amp')

stop = 1
t = np.linspace(0, stop, int(stop * sample_rate), endpoint = False)
zero = np.zeros(t.size)
for fi in range(0, len(freq3)):
    f = zero + freq3[fi]
    amp = np.sin(phi3[fi] + freq3[fi] * 2 * np.pi * t) * amp3_normal[fi]
    ax.plot(t, f, amp)

plt.show()

在取樣的資料中,可以分離出以下的弦波:

傅立葉轉換(二)

這邊顯示這張圖的目的,是想討論一個問題,就一個二維的圖像來說,頻率代表了什麼?

在〈Matplotlib 圖片、等值輪廓線〉談過二維 Perlin 雜訊,若將雜訊值作為灰階值,看來就像起伏的地形:

傅立葉轉換(二)

如果在圖上與 x 軸平行畫一條線,線通過的像素,其灰階值就是一維 Perlin 雜訊,以雜訊作為 y,像素 x 座標作為 x,畫出來的曲線圖,就類似這篇文件開頭的第一張圖,是隨機而又連續的曲線,而這樣的曲線,可以看成是數個頻率弦波疊加後的結果。

也就是說,就圖像來說,取樣距離內的灰階度會有數個週期性變化,與這些週期相對的就是頻率,當然,圖像是二維的,不過,可以來產生 x 方向有灰度變化,而 y 方向沒有的圖案來先做個說明,例如就以下的程式來說:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

freq = 5          
stop = .25        
sample_rate = 800

x = np.linspace(0, stop, int(stop * sample_rate), endpoint = False)
gray = 125 + np.sin(freq * 2 * np.pi * x) + 125

plt.imshow(gray.reshape(1, 200).repeat(200, axis = 0), cmap = cm.gray)
plt.show()

產生的圖案為:

傅立葉轉換(二)

若將 freq 調為 50,會得到以下的圖案,這時可以說,相對而言,上圖是低頻的訊號組成,也就是灰階度變化相對來說緩和,而下圖是是高頻的訊號組成,也就是灰階度變化相對來說劇烈:

傅立葉轉換(二)

也就是說,頻率的高低是相同的,從上圖也可以發現,兩圖相對來說,低頻的那張,黑與白的界線較不明顯,高頻的那張看來,黑與白的界線較為明確,這是不是意謂著,若想要尋找圖像中的邊緣,可以設法保留高頻的部份?

基本上這想法沒錯…不過,實際的圖像就像方才的二維 Perlin 雜訊圖片那樣,是二維的,因此必須能夠計算二維的頻率…這就是下一篇的主題了…