| title | 傅里叶变换 | ||
|---|---|---|---|
| date | 2019-06-10 23:46 | ||
| categories | 数学 | ||
| tags |
|
||
| keywords | FFT, 傅里叶变换, 图像处理 | ||
| mathjax | true | ||
| description | 图像处理中, 为了方便处理,便于抽取特征,数据压缩等目的,常常要将图形进行变换,这篇文章介绍一下傅里叶变换 |
图像处理中, 为了方便处理,便于抽取特征,数据压缩等目的,常常要将图像进行变换。 一般有如下变换方法
- 傅立叶变换Fourier Transform
- 离散余弦变换Discrete Cosine Transform
- 沃尔希-哈德玛变换Walsh-Hadamard Transform
- 斜变换Slant Transform
- 哈尔变换Haar Transform
- 离散K-L变换Discrete Karhunen-Leave Transform
- 奇异值分解SVD变换Singular-Value Decomposition
- 离散小波变换Discrete Wavelet Transform
这篇文章介绍一下傅里叶变换
积分形式 如果一个函数的绝对值的积分存在,即 $$ \int_{-\infty} ^\infty |h(t)|dt<\infty $$ 并且函数是连续的或者只有有限个不连续点,则对于 x 的任何值, 函数的傅里叶变换存在
- 一维傅里叶变换 $$ H(f)=\int_{-\infty} ^\infty h(t)e^{-j2\pi ft}dt $$
- 一维傅里叶逆变换 $$ H(f)=\int_{-\infty} ^\infty h(t)e^{j2\pi ft}dt $$ 同理多重积分
实际应用中,多用离散傅里叶变换 DFT.
- 一维傅里叶变换 $$ F(u)=\sum_{x=0} ^{N-1} f(x)e^{\frac{-2\pi j}{N} ux} $$
- 一维傅里叶逆变换
$$
f(x)=\frac{1}{N}\sum_{u=0} ^{N-1} F(u)e^{\frac{2\pi j}{N} ux}
$$
需要注意的是, 逆变换乘以
$\frac{1}{N}$ 是为了归一化,这个系数可以随意改变, 即可以正变换乘以$\frac{1}{N}$ , 逆变换就不乘,或者两者都乘以$\frac{1}{\sqrt{N}}$等系数。 - 二维傅里叶变换 $$ F(u,v)=\frac{1}{N}\sum_{x=0}^{N-1}\sum_{y=0} ^{N-1} f(x,y)e^{\frac{-2\pi j}{N} (ux+vy)} $$
- 二维傅里叶逆变换
幅度
$$
|F(u,v)| = \sqrt{real(F)^2+imag(F)^2}
$$
相位
$$
arctan{\frac{imag(F)}{real(F)}}
$$
对于图像的幅度谱显示,由于 |F(u,v)| 变换范围太大,一般显示
用 <=> 表示傅里叶变换对
$$
f(x)<=>F(u)\
f(x,y)<=>F(u,v)
$$
f,g,h 对应的傅里叶变换 F,G,H
$$ \begin{aligned} &F(x,v)=\sum_{y=0} ^{N-1} f(x,y)e^{\frac{-2\pi j}{N} vy}\ &F(u,v)=\frac{1}{N}\sum_{x=0}^{N-1}F(x,v)e^{\frac{-2\pi j}{N}ux} \end{aligned} $$ 进行多维变换时,可以依次对每一维进行变换。 下面在代码中就是这样实现的。
若有 $$ f(r,\theta)<=>F(\omega,\phi) $$ 则 $$ f(r,\theta+t)<=>F(\omega,\phi+t) $$
尺度变换 $$ f(ax,by)<=>\frac{F(\frac{u}{a},\frac{v}{b})}{ab} $$
卷积定义 1d $$ f*g = \frac{1}{M}\sum_{m=0}^{M-1}f(m)g(x-m) $$ 2d $$ f(x,y)*g(x,y) = \frac{1}{MN}\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}f(m,n)g(x-m,y-n) $$
卷积定理 $$ f(x,y)*g(x,y) <=> F(u,v)G(u,v) $$ $$ f(x,y)g(x,y)<=>F(u,v)*G(u,v) $$
离散卷积 用 $$ \sum_{i=0}^{N-1}x(iT)h[(k-i)T] <=> X(\frac{n}{NT})H(\frac{n}{NT}) $$ 即两个周期为 N 的抽样函数, 他们的卷积的离散傅里叶变换等于他们的离散傅里叶变换的卷积
卷积的应用: 去除噪声, 特征增强 两个不同周期的信号卷积需要周期扩展的原因:如果直接进行傅里叶变换和乘积,会产生折叠误差(卷绕)。
下面用$ \infty$ 表示相关。 相关函数描述了两个信号之间的相似性,其相关性大小有相关系数衡量
- 相关函数的定义 离散 $$f(x,y)\quad \infty \quad g(x,y) = \frac{1}{MN}\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}f^(m,n)g(x+m,y+n) $$ 连续 $$z(t) = \int_{-\infty}^{\infty}x^(\tau) h(t+\tau)d\tau$$
- 定理 $$ f(x,y)\quad \infty \quad g(x,y)<=>F^*(u,v)G(u,v) $$
能量变换 对于有限区间非零函数 f(t), 其能量为 $$ E = \int_{-\infty}^{\infty}|f(t)|^2dt $$
其变换函数与原函数有相同的能量 $$ \int_{-\infty}^{\infty}|f(t)|^2dt = \int_{-\infty}^{\infty}|F(u)|^2dt $$
由上面离散傅里叶变换的性质易知,直接计算 1维 dft 的时间复杂度维
利用到单位根的对称性,快速傅里叶变换可以达到
我们知道, 在复平面,复数 $cos\theta +i\ sin\theta$k可以表示成
- 这个可以用 e 表示出来证明 $$ \omega_{2n}^{2k}=\omega_{n}^{k} $$
- 可以写成三角函数证明 $$ \omega_{n}^{k+\frac{n}{2}}=-\omega_{n}^{k} $$
容易看出
对于$ w_{n}^{k}$ , 它事实上就是
下面的推导假设
利用上面的对称性,
将傅里叶计算进行奇偶分组
$$
\begin{aligned}
F(u)&=\sum_{i=0}^{n-1}\omega_n ^{iu} a^i\
&= \sum_{i=0}^{\frac{n}{2}-1}\omega_n ^{2iu} a^{2i}+\sum_{i=0}^{\frac{n}{2}-1}\omega_n ^{(2i+1)u} a^{2i+1}\
&=\sum_{i=0}^{\frac{n}{2}-1}\omega_{\frac{n}{2}} ^{iu} a^{2i}+\omega_n^u\sum_{i=0}^{\frac{n}{2}-1}\omega_{\frac{n}{2}} ^{iu} a^{2i+1}\
& = F_{even}(u)+\omega_n^u F_{odd}(u)
\end{aligned}
$$
对于
下面是 python 实现 一维用 FFT 实现, 不过 只实现了 2 的幂。/ 对于非 2 的幂,用 FFT 实现有点困难,还需要插值,所以我 用$O(n^2)$ 直接实现。
二维的 DFT利用 分离性,直接调用 一维 FFT。 GitHub
import numpy as np
def _fft(a, invert=False):
N = len(a)
if N == 1:
return [a[0]]
elif N & (N - 1) == 0: # O(nlogn), 2^k
even = _fft(a[::2], invert)
odd = _fft(a[1::2], invert)
i = 2j if invert else -2j
factor = np.exp(i * np.pi * np.arange(N // 2) / N)
prod = factor * odd
return np.concatenate([even + prod, even - prod])
else: # O(n^2)
w = np.arange(N)
i = 2j if invert else -2j
m = w.reshape((N, 1)) * w
W = np.exp(m * i * np.pi / N)
return np.concatenate(np.dot(W, a.reshape(
(N, 1)))) # important, cannot use *
def fft(a):
'''fourier[a]'''
n = len(a)
if n == 0:
raise Exception("[Error]: Invalid length: 0")
return _fft(a)
def ifft(a):
'''invert fourier[a]'''
n = len(a)
if n == 0:
raise Exception("[Error]: Invalid length: 0")
return _fft(a, True) / n
def fft2(arr):
return np.apply_along_axis(fft, 0,
np.apply_along_axis(fft, 1, np.asarray(arr)))
def ifft2(arr):
return np.apply_along_axis(ifft, 0,
np.apply_along_axis(ifft, 1, np.asarray(arr)))
def test(n=128):
print('\nsequence length:', n)
print('fft')
li = np.random.random(n)
print(np.allclose(fft(li), np.fft.fft(li)))
print('ifft')
li = np.random.random(n)
print(np.allclose(ifft(li), np.fft.ifft(li)))
print('fft2')
li = np.random.random(n * n).reshape((n, n))
print(np.allclose(fft2(li), np.fft.fft2(li)))
print('ifft2')
li = np.random.random(n * n).reshape((n, n))
print(np.allclose(ifft2(li), np.fft.ifft2(li)))
if __name__ == '__main__':
for i in range(1, 3):
test(i * 16)