傅里叶变换(DFT)从原理到代码

离散傅里叶变换(DFT)

一、傅里叶变换公式

  1. 连续傅里叶变换
    连续傅里叶变换是使用一对正交基 c o s ω t cosωt cosωt s i n ω t sinωt sinωt对时域序列进行“探测”(求时域序列与正交基的相关性),筛选出与原时域序列相关的正交基,即为原时域序列的频域序列。连续傅里叶变换公式如下:
    F ( ω ) = ∫ − ∞ + ∞ f ( t ) e − i ω t d t F(\omega ) = \int_{ - \infty }^{ + \infty } {f(t){e^{ - i\omega t}}} dt F(ω)=+f(t)eiωtdt
  2. 离散傅里叶变换
    离散傅里叶变换(DFT)方法,是以频率 f s f_s fs对输入信号对输入信号进行等间隔采样n次后,对这n个离散点进行DFT处理得到n个复数,每个复数表示以 f s / n f_s/n fs/n为间隔的频率成分的幅度和相位信息,即获得输入信号的频谱信息。
    对于一个长度为N的时域离散序列 x ( n ) x(n) x(n),DFT变换结果为 X ( k ) X(k) X(k),则:
    X ( k ) = D F T { x ( n ) } = 1 N ∑ n = 0 N − 1 x ( n ) × e − j 2 π N n k , k = 0 , 1 , 2 , . . . , N − 1 X(k)=DFT\{ x(n)\} = \frac{1}{N}\sum\limits_{n = 0}^{N - 1} {x(n) \times {e^{ - j\frac{{2\pi }}{N}nk}}} ,k = 0,1,2,...,N - 1 X(k)=DFT{x(n)}=N1n=0N1x(n)×ejN2πnk,k=0,1,2,...,N1
    转换为矩阵形式:
    D F T { x ( n ) } = 1 N [ 1 1 1 ⋯ 1 1 e − j 2 π N × 1 e − j 2 π N × 2 ⋯ e − j 2 π N × ( N − 1 ) 1 e − j 2 π N × 2 × 1 e − j 2 π N × 2 × 2 ⋯ e − j 2 π N × 2 × ( N − 1 ) ⋮ ⋮ ⋮ ⋱ ⋮ 1 e − j 2 π N × ( N − 1 ) × 1 e − j 2 π N × ( N − 1 ) × 2 ⋯ e − j 2 π N × ( N − 1 ) 2 ] [ x ( 0 ) x ( 1 ) x ( 2 ) ⋮ x ( N − 1 ) ] DFT\{ x(n) \}=\frac{1}{N} \begin{bmatrix} 1&1&1& \cdots &1\\ 1&{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times 1}}}&{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times 2}}}& \cdots &{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times (N - 1)}}}\\ 1&{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times 2 \times 1}}}&{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times 2 \times 2}}}& \cdots &{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times 2 \times (N - 1)}}}\\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1&{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times (N - 1) \times 1}}}&{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times (N - 1) \times 2}}}& \cdots &{{{\rm{e}}^{ - j\frac{{2\pi }}{N} \times {{(N - 1)}^2}}}} \end{bmatrix} \begin{bmatrix} {x(0)}\\ {x(1)}\\ {x(2)}\\ \vdots \\ {x(N - 1)} \end{bmatrix} DFT{x(n)}=N111111ejN2π×1ejN2π×2×1ejN2π×(N1)×11ejN2π×2ejN2π×2×2ejN2π×(N1)×21ejN2π×(N1)ejN2π×2×(N1)ejN2π×(N1)2x(0)x(1)x(2)x(N1)

二、离散傅里叶变换实践

设原信号为 f ( t ) = cos ⁡ ( 2 π t + 0.5 ) + 3.2 sin ⁡ ( π t + 1 ) + 0.3 f(t) = \cos (2\pi t + 0.5) + 3.2\sin (\pi t +1) + 0.3 f(t)=cos(2πt+0.5)+3.2sin(πt+1)+0.3,时间跨度为0到 T = 10 s T=10s T=10s,信号中最高频率为1Hz,则使用3Hz(两倍最高频率)对原信号进行采样,则:
注:角频率 ω = 2 π f ω=2\pi f ω=2πf f f f为频率。

import matplotlib.pyplot as plt
import numpy as np

fs = 30  # 采样频率为3Hz,可变更,但需要大于2(采样定理)
T = 10  # 时序样本长度为10s
N = T * fs  # 采样次数
t = np.arange(0, T, 1 / fs)
tim = np.cos(2 * np.pi * t + 0.5) + 3.2 * np.sin(np.pi * t + 1) + 0.3  # 生成时序样本

X = np.dot(np.arange(N).reshape((-1, 1)), np.arange(N).reshape((1, -1)))
X = np.exp(-1j * 2 * np.pi / N * X)
freq = np.dot(X, tim)

omega = np.arange(0, fs, fs / N)  # 生成频域图横坐标,单位Hz

filtering = np.where(abs(freq) > 0.000001, 1, 0)  # 过滤器,过滤数值奇异的零点
amplitude = abs(freq) / N * filtering  # 此处的幅度,直流项和非直流项存在差异,直流项为原值,非直流项为原幅值的一半
phase = np.angle(freq) * filtering

fig1, axes = plt.subplots(2, 1)
axes[0].bar(omega, height=amplitude, width=fs / N, label='amplitude')  # 幅值图
axes[0].legend()

axes[1].bar(omega, height=phase, width=fs / N, label='phase')  # 相位图
axes[1].legend()
plt.show()

fig2, axes = plt.subplots(4)
axes[0].plot(t, np.cos(2 * np.pi * t + 0.5))  # 原信号第一个分量
axes[1].plot(t, 3.2 * np.sin(np.pi * t + 1))  # 原信号第二个分量
axes[2].plot(t, np.ones_like(t) * 0.3)  # 原信号直流项
axes[3].plot(t, tim)

tim_back = np.zeros_like(t)
for i in range(len(omega)):
    tim_back = tim_back + amplitude[i] * np.cos(2 * np.pi * omega[i] * t + phase[i])

axes[3].plot(t, tim_back, '--', marker='*')  # 复原原图形
plt.show()

以上为根据公式,自行推导的傅里叶变换及逆变换代码,以下采用Python自带的fft工具对以上变换进行验证:

import matplotlib.pyplot as plt
import numpy as np

fs = 30  # 采样频率为3Hz,可变更,但需要大于2(采样定理)
T = 10  # 时序样本长度为10s
N = T * fs  # 采样次数
t = np.arange(0, T, 1 / fs)
tim = np.cos(2 * np.pi * t + 0.5) + 3.2 * np.sin(np.pi * t + 1) + 0.3  # 生成时序样本

"""
以下2行为Python自带FFT程序
"""
omega = np.fft.fftfreq(N, d=1 / fs)
freq = np.fft.fft(tim)

filtering = np.where(abs(freq) > 0.000001, 1, 0)  # 过滤器,过滤数值奇异的零点
amplitude = abs(freq) / N * filtering  # 此处的幅度,直流项和非直流项存在差异,直流项为原值,非直流项为原幅值的一半
phase = np.angle(freq) * filtering

fig1, axes = plt.subplots(2, 1)
axes[0].bar(omega, height=amplitude, width=fs / N, label='amplitude')  # 幅值图
axes[0].legend()

axes[1].bar(omega, height=phase, width=fs / N, label='phase')  # 相位图
axes[1].legend()
plt.show()

fig2, axes = plt.subplots(4)
axes[0].plot(t, np.cos(2 * np.pi * t + 0.5))  # 原信号第一个分量
axes[1].plot(t, 3.2 * np.sin(np.pi * t + 1))  # 原信号第二个分量
axes[2].plot(t, np.ones_like(t) * 0.3)  # 原信号直流项
axes[3].plot(t, tim)

tim_back = np.zeros_like(t)
for i in range(len(omega)):
    tim_back = tim_back + amplitude[i] * np.cos(2 * np.pi * omega[i] * t + phase[i])

"""
以下1行为Python自带FFT程序
"""
tim_back2 = np.fft.ifft(freq)

axes[3].plot(t, tim_back, '--', marker='*')  # 复原原图形
axes[3].plot(t, tim_back2.real, '--', marker='+')  # 复原原图形
plt.show()

以上代码,相较于自制的FFT程序,不同在于,Python自带np.fft.fft工具的频率从负到正,其余一致,如下:
np.fft.fft
图 : p y t h o n 自 带 n u m p y . f f t 工 具 图:python自带numpy.fft工具 pythonnumpy.fft
DIY_fft
图 : 自 编 程 序 的 f f t 工 具 图:自编程序的fft工具 fft
原因在于DFT公式,具有 N N N的周期性,如下:
e − j 2 π N n ( k − N ) = e − j 2 π N n k + j 2 π n = e − j 2 π N n k e j 2 π n = e − j 2 π N n k {e^{ - j\frac{{2\pi }}{N}n(k - N)}} = {e^{ - j\frac{{2\pi }}{N}nk + j2\pi n}} = {e^{ - j\frac{{2\pi }}{N}nk}}{e^{j2\pi n}} = {e^{ - j\frac{{2\pi }}{N}nk}} ejN2πn(kN)=ejN2πnk+j2πn=ejN2πnkej2πn=ejN2πnk
又因为DFT函数具有共轭对称性,因此可以将观测周期从 0 ∼ N 0\sim N 0N变更到 − N / 2 ∼ N / 2 -N/2\sim N/2 N/2N/2之间,结果不受影响。

三、numpy.fft应用

为简洁起见,编程中常使用numpy.fft,因此以下说明numpy.fft的使用方法。

  1. 离散序列输入
    设原始时序为:
    f ( t ) , t ∈ [ 0 , T ] f(t),t∈[0,T] f(t),t[0,T]
    采样频率为 f s f_s fs,则采样数目 N = T ∗ f s N=T*f_s N=Tfs
  2. 傅里叶变换
    采样numpy.fft进行傅里叶变换:
    获取频率分布: f r e q = n u m p y . f f t . f f t f r e q ( N , d = 1 / f s ) freq=numpy.fft.fftfreq( N, d=1 / f_s) freq=numpy.fft.fftfreq(N,d=1/fs)
    获取傅里叶变换结果,结果为复数: r e = n u m p y . f f t . f f t { f ( t ) } re=numpy.fft.fft\{f(t) \} re=numpy.fft.fft{f(t)}
    获取幅值分布: a m p l i t u d e = a b s ( r e ) / N amplitude = abs(re) / N amplitude=abs(re)/N
    获取初相分布: p h a s e = n p . a n g l e ( r e ) phase = np.angle(re) phase=np.angle(re)

注:
Ⅰ. 可以使用 f i l t e r i n g = n p . w h e r e ( a b s ( f r e q ) > 0.000001 , 1 , 0 ) filtering = np.where(abs(freq) > 0.000001, 1, 0) filtering=np.where(abs(freq)>0.000001,1,0)过滤掉数值奇异的数据:
p h a s e = p h a s e ∗ f i l t e r i n g phase = phase * filtering phase=phasefiltering
a m p l i t u d e = a m p l i t u d e ∗ f i l t e r i n g amplitude = amplitude * filtering amplitude=amplitudefiltering
Ⅱ. 幅值分布,需要÷N,即 a m p l i t u d e = a b s ( r e ) / N amplitude = abs(re) / N amplitude=abs(re)/N

  1. numpy.fft傅里叶结果
    numpy.fft结果,频率分布为 − N / 2 ∼ N / 2 -N/2\sim N/2 N/2N/2,序列为:
    f r e q = [ 0 , 1 , . . . , N − 1 2 , − N 2 , − N − 1 2 , . . . , − 1 ] freq=[0,1,...,\frac{N-1}{2},-\frac{N}{2},-\frac{N-1}{2},...,-1] freq=[0,1,...,2N1,2N,2N1,...,1]
    Python实践中的频率分布如下:在这里插入图片描述
    其中频率=0表示直流分量,其幅度为直流分量的大小;频率≠0时,幅度为相应频率下幅度的1/2。
    如下图:
    在这里插入图片描述
  2. numpy.fft.rfft结果
    如果只需要保留正频率部分,可以使用numpy.fft.rfft。
    rfft和fft的结果对比如下:
    在这里插入图片描述
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PyTorch中对于傅立变换的实现可以使用torch.fft模块。其中torch.fft.fft()函数可以用来进行一维傅立变换,torch.fft.fft2()函数可以用来进行二维傅立变换,而torch.fft.fftn()函数可以用来进行n维傅立变换。这些函数的输入应该是实数或复数张量,并且返回的结果也是复数张量。 如果需要计算傅立变换,可以使用torch.fft.ifft()函数进行一维逆变换,torch.fft.ifft2()函数进行二维逆变换,torch.fft.ifftn()函数进行n维逆变换。同样,这些函数的输入和输出都是复数张量。 在PyTorch中,可以使用torch.fft.fftshift()函数来对转换后的频域图像进行移动操作,将低频部分放到图像中间,以便于观察。这个函数仅仅起到了视觉上的作用。 另外,还有cv2.dft()函数可以用于图像的傅立变换,注意输入数据的格式应为float32。而cv2.idft()函数则可以进行图像的傅立变换。需要注意的是,使用np.fft.fft2()进行图像傅立变换时,数据应为非负,否则用np.fft.ifft2()无法还原。 除了傅立变换,还有一种类似于傅立变换变换方法叫做小波变换。小波变换也是将函数用一组正交基函数展开的方法,不同之处在于选取的基函数不同。 关于PyTorch中的傅立变换,在进行逆变换时,可以使用torch.irfftn()函数来计算出逆变换结果。然后,可以通过裁剪出多余的数组填充来得到最终的逆变换结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [分别使用numpy和pytorch进行图像傅里变换和频域分析](https://blog.csdn.net/Brikie/article/details/113004911)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [详解python实现小波变换的一个简单例子](https://download.csdn.net/download/weixin_38574132/13997641)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [PyTorch中的傅立卷积:通过FFT有效计算大核卷积的数学原理代码实现](https://blog.csdn.net/m0_46510245/article/details/109800521)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值