实际应用中的FFT、标准FFT的理解、PyTorch中的FFT

FFT基本理解

一个信号是多个单频信号(如正弦信号)的叠加,FFT就是将一个复杂信号通过频域分解为多个单频分量进行分析的手段。每个分量都有自己的频率、幅值和相位。
FFT将信号从时域转化到频域,一个信号FFT的结果叫做这个信号的频谱,分为幅度谱和相位谱。

离散傅里叶变换DFT的公式如下:(实际应用中常采用快速傅里叶变换FFT进行加速)

在这里插入图片描述

常见的音频信号是实数信号,语音信号是实数信号,这里主要讨论实数信号的FFT。

实数信号的FFT是复数形式的,其正频率和负频率部分是共轭对称的,所以一般只需要考虑频谱的一半即可完整描述信号的频域特性。

正频率与负频率

在傅里叶变换中,正频率和负频率的概念是由于复指数函数的性质所引入的。在频域中,频率可以是正的(表示正向旋转)也可以是负的(表示反向旋转)。

具体来说,负频率并不意味着真正存在一个负的频率,而是表示信号在频域中的相位信息。在复指数形式的傅里叶变换中,正频率对应于频率轴上的正方向,而负频率则对应于频率轴上的负方向。负频率实际上是信号中某种旋转或振荡的表示,与正频率在数学上是等效的。

在实数信号的情况下,正频率和负频率的幅度是对称的,但相位是互为相反的。(即共轭对称,虚部包含相位信息)

振幅和相位

在频域中,实数信号的傅立叶变换包含了关于每个频率成分的振幅和相位信息。

通常,我们只对实数信号的振幅(幅频图)比较感兴趣。振幅即模值,反映了各频率成分的贡献。

实数信号本身是缺乏明确的全局相位信息的,因为实数信号在时间域上是偶函数,其幅度对于正频率和负频率部分是对称的。这导致无法唯一确定实数信号的全局相位。

然而,即使实数信号本身没有全局相位信息,它仍然可以在局部区域内具有相对的相位信息。实数信号的相位信息表示了信号中不同频率成分之间的相对时间偏移或者说相位差。

具体来说,信号的相位信息可以提供有关信号中周期性波形的开始点和结束点之间的时间偏移信息。这在许多领域中都很重要,例如音频处理、通信系统、图像处理和控制系统等。在音频处理中,相位信息可以影响声音的定位和声音合成的质量。在通信系统中,相位信息是解调过程中的重要组成部分。在图像处理中,相位信息可以用于图像恢复和图像合成。在控制系统中,相位信息可以影响系统的稳定性和响应速度。

下面通过参考MATLAB中的fft函数进行理解

参考MATLAB中fft()

官方文档:https://ww2.mathworks.cn/help/matlab/ref/fft.html

基本用法:Y=fft(X)

假设L=len(X)=len(Y),即做信号长度点FFT(实际会将信号长度补零至大于等于N且最接近的2的幂次方做FFT,但输出仍是等于信号长度,除非指定FFT点数则输出相应点数)

Y=fft(X)的输出Y是L个点,频谱的第一个点是直流分量,而接下来的前一半是正频率,后一半是负频率。如上所述,通常舍弃负频率。

官方给出的计算标准FFT的MATLAB代码如下,下面进行解释

Y = fft(S);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);

plot(f,P1,"LineWidth",3) 
title("Single-Sided Amplitude Spectrum of S(t)")
xlabel("f (Hz)")
ylabel("|P1(f)|")

频率分辨率

根据奈奎斯特采样定理,对于连续时间信号进行采样,采样率必须大于等于信号最高频率的2倍,否则会产生频谱混叠。
所以Y有L个点,其最高频率是采样率的一半,即fs/2,而Y取一半即L/2个点,所以频率分辨率(即频谱图两个相邻频率之间的间隔)是fs/L

同时,标准FFT结果的横坐标应当是频率,fs/L*(1:L/2)

幅值归一化

归一化的具体原因是因为FFT算法本身会对结果进行缩放,而且由于傅里叶变换的性质,信号在时域与频域的能量总和应该是相等的。如果不对幅度进行归一化操作,不同长度的信号在频域中幅度值的大小会不同,比如若用较长的信号做FFT,一般各个频率的幅值就会更大,这样就无法直观地比较它们的频谱信息。

简单做fft(x)会发现幅值很大,远超时域幅值,理论上应当是各个频率的贡献,不应超过时域值,这里是有叠加。

具体来说,对于长度为N的FFT结果,通过将FFT结果除以信号长度(通常是N点FFT)来进行归一化操作。一般只采用正频率,此时除了直流分量和Nyquist(中间点)外(这两个点是实数),其余非直流部分需要乘以2。(因为舍弃了负频率)

这样做可以确保在不同信号长度下,幅度值的单位是相同的,方便进行比较和分析。因此,归一化是为了保持信号能量不变,并使得不同长度的信号在频域中的幅度信息具有可比性。

另外,幅值的单位和时域幅值的单位相同,通常为电压V(伏)或者mV

python

正在用pytorch,所以直接用torch.fft.fft()torch.fft.rfft()举例

torch.fft.fft()接受一个实数或复数张量作为输入,并返回一个复数张量。输出与MATLAB中fft()输出相同,是完整的L个点,包含正频率和负频率。
torch.fft.rfft()是针对实数信号的FFT,接受一个实数张量作为输入,并返回一个复数张量。输出是直流部分和正频率,即L/2+1个点(就是上面标准FFT的点)

注意:两者都不会对数据进行标准化

对应地,torch.fft.ifft()接受一个复数张量作为输入,并返回一个复数张量。
torch.fft.irfft()接受一个复数张量作为输入(通常是通过rfft()得到的结果),并返回一个实数张量。

import numpy as np
import torch
import librosa
import matplotlib.pyplot as plt
import sounddevice as sd

raw_speech, sr = librosa.load(test_file_path, sr=None)
S = raw_speech[256:256*2]  # sr=16000,S为16ms
L = len(S)
S = torch.tensor(S)
Y1 = torch.fft.fft(S)  # 256
Y2 = torch.fft.rfft(S)  # 129
P1 = torch.abs(Y1/L)
P2 = torch.abs(Y2/L)

ss = torch.fft.ifft(Y1)  # 256
sp = ss.real
ss2 = torch.fft.irfft(Y2)  # 256

plt.plot(np.arange(len(P1))/sr, P1)
plt.show()
plt.plot(np.arange(len(P2))/sr, P2)
plt.show()

sd.play(sp, sr)
sd.play(ss2, sr)

注意:如果你首先使用a = torch.zeros(x.shape)初始化一个张量a,然后将复数张量torch.fft.ifft(x)赋值给它,结果取决于被赋值的复数张量的虚部是否为0。
如果torch.fft.ifft(x)的虚部全为0,则最终a会被写入成实数部分。
如果torch.fft.ifft(x)的虚部不全为0,则最终a会保持复数类型,包含虚部信息。
但是,当执行frame_s2 = torch.zeros(speech_ssw.shape[0], speech_ssw.shape[1], frame_length.item()) 然后 frame_s2[:,i,:] = torch.fft.ifft(x)这样的操作时,最终结果会被写入成实数部分。这是因为PyTorch的索引赋值操作可能会导致数据类型转换,使得结果变成与目标张量相同的数据类型。

在PyTorch中,索引赋值和变量直接赋值操作对数据类型的转换操作有一些区别:

索引赋值(Indexing Assignment):
当执行索引赋值操作时,例如tensor[index] = value,PyTorch会尝试将value转换为tensor的数据类型。如果value的数据类型与tensor不匹配,PyTorch会进行自动类型转换。
索引赋值可能会导致数据类型转换,因为PyTorch会根据目标张量的数据类型来确定最终结果的数据类型。

变量直接赋值(Variable Assignment):
当执行变量直接赋值操作时,例如a = value,如果未指定数据类型,则value将保持其原始数据类型。只有当明确指定了数据类型时,才会进行类型转换。
变量直接赋值通常不会导致数据类型转换,除非明确要求或者需要。
因此,在实际应用中,需要注意索引赋值可能会引起数据类型转换的情况,而变量直接赋值更容易保持数据类型的稳定性。

其它

1.一般情况下,我认为,在分帧信号长度相同或者做频谱映射等的情况下,不需要计算标准FFT。计算标准FFT所做的处理在进行逆变换时也要先恢复,比较麻烦。

2.如果你想在频域上进行乘法操作以代替时域卷积,通常建议使用完整的傅里叶变换(torch.fft.fft)而不是实部的傅里叶变换(torch.fft.rfft)。

部分参考B站up:爱上显微镜的少年 有关傅里叶变换频谱的讲解

  • 33
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值