时频图是对将信号分割为离散片段(sliding),每个片段两端需要进行适当填充,以堆叠冗余信息,以保证信息的完整性
- 短时傅里叶变换
短时傅里叶变换 (Short-Time Fourier Transform, STFT) 是一种时频谱转换算法,它通过在时间上移动窗口函数并计算窗口内信号的频谱来获得信号在时间和频率上的信息。填充信号可以确保每个窗口都有足够的数据进行频谱计算,特别是在窗口函数的边缘。
- 窗口函数
主要用于信号处理中的短时傅里叶变换(STFT)、滤波器设计和其他需要对信号进行窗函数处理的场景, 可减少频谱泄漏,避免傅里叶变换中的频谱混叠
1. 音频维度变化
STFT变换过程中,数据的形状变化,同义词是维度 (Size / Shape)
magnitude_spectrum 是 STFT 结果的幅度谱,而 mel_spectrum 是经过梅尔滤波器组处理后的梅尔频谱,它们有形状区别即维度差异。
1.1 magnitude_spectrum 的形状
stft_result 的形状为 (num_frequency_bins, num_frames)
stft_result即 magnitude_spectrum ,但去除了复数部分,只保留了幅度信息。
- num_frequency_bins 是频率 bins 的数量(等于 n_fft // 2 + 1)。
- num_frames 是时间窗口的数量。
1.2 梅尔滤波器组的形状 (num_mels)
这里用librosa.filters.mel ,是 Librosa 库中的一个函数,生成梅尔滤波器(Mel filter bank),每个滤波器频率为三角型(三角滤波器)。
mel_numls 的 shape = (num_mels, num_frequency_bins) = (n_mels, n_fft//2+1)
- 输入参数:
sr (number [scalar]): 采样率。默认值为 22050。
n_fft (int > 0 [scalar]): FFT 的大小。默认值为 2048。
n_mels (int > 0 [scalar]): 梅尔滤波器的数量。默认值为 128。
fmin (float >= 0 [scalar]): 最低频率(赫兹)。默认值为 0.0。
fmax (float > 0 [scalar] or None): 最高频率(赫兹)。如果为 None,则使用 sr / 2.0。
htk (bool): 如果为 True,使用 HTK 公式来计算梅尔频率。默认值为 False。
norm (None or 'slaney'): 如果为 None,不进行归一化。如果为 'slaney',则归一化滤波器总和以满足 Slaney 的标准。默认值为 'slaney'。
dtype (np.dtype): 输出数组的数据类型。默认值为 np.float32。
- 返回值
mel_basis : shape=(n_mels, 1 + n_fft/2), 每一行代表一个梅尔滤波器。
1.3 mel_spectrum 的形状
mel_spectrum (矩阵乘法) = 梅尔滤波器组 × \times × 频谱, 即
(num_mels, num_frames) = (num_mels, num_frequency_bins) × \times × (num_frequency_bins, num_frames)^T
mel_spectrum 的 shape = (num_mels, num_frames)
2.频谱特征
这里指每个片段的频谱特征
2.1 主瓣(mainlobe)
频谱中最大的峰值区域,它代表了信号的主要频率成分,
在应用窗口函数后的频谱图中,主瓣通常位于频谱的中心位置,并包含了信号的主要能量。
主瓣宽度决定了频谱的分辨率。主瓣越窄,频谱分辨率越高,可以更好地区分相近的频率成分。
2.2 旁瓣(Sidelobe)
旁瓣是频谱中位于主瓣两侧的较小峰值区域。旁瓣表示频谱泄漏,即信号的能量扩散到主频率之外的频率分量。
旁瓣越高,频谱泄漏越严重,这会影响频谱分析的精度。
旁瓣高度决定了频谱泄漏的程度。旁瓣越低,频谱泄漏越小,频谱分析的准确性越高。
2.3 频谱泄漏(Spectral Leakage)
频谱泄漏是信号的能量未集中在原始频率位置,而是扩散到了其他频率位置的现象。
傅里叶变换是分段的,假设每段有一个主频率信号,且信号是周期的。但时间上截断信号会影响信号原有的周期性,这种非周期性会导致频域中主频能量扩散到其他频率分量。
频谱泄漏表现在频谱图中是 信号的主要频率成分(主瓣)周围会出现一些不希望出现的频率成分(旁瓣)。这些旁瓣是由于截断或不适当的窗口函数引起的,会影响频谱的清晰度和准确性。
窗口函数可以减少这种现象,通过对窗口内信号进行系数调整,窗口两端逐渐减小到零,从而平滑过渡,减少非周期性误差,即降低频率域中的副瓣(sidelobes)。
3. 填充 & 窗口函数
3.1 填充(Padding)
截断前的操作,把周期函数的 “补全” or “延续”
填充是在窗口信号的两端添加一些额外的数据,以便在进行窗口函数处理时,避免边界处的窗口截断问题 ,截断会导致边界处的能量损失和频谱伪像。
下面是将[0.1, 0.2, 0.3, 0.4, 0.5] 填充为 [0.4, 0.3, 0.2, 0.1, 0.2, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2]的代码:
signal = torch.tensor([0.1, 0.2, 0.3, 0.4, 0.5]) # 模拟信号
# STFT 参数
n_fft = 8
hop_size = 2
# 对信号进行填充
padded_signal = torch.nn.functional.pad(signal.unsqueeze(0), (int((n_fft-hop_size)/2), int((n_fft-hop_size)/2)), mode='reflect')
padded_signal = padded_signal.squeeze(0)
print("原始信号长度:", len(signal)) # 5
print("填充后的信号长度:", len(padded_signal)) # 11
print(padded_signal)
# tensor([0.4000, 0.3000, 0.2000, 0.1000, 0.2000, 0.3000, 0.4000, 0.5000, 0.4000, 0.3000, 0.2000])
3.2 窗口函数(window)
让窗口内的信号序列 乘以 一个系数,以调整信号的取值范围,
这个系数来自一个序列生成函数,即 窗口函数
3.3 汉宁窗口 (Hann Window)
汉宁窗公式如下:
w ( n ) = 0.5 ( 1 − c o s N − 1 2 π n ) w(n)=0.5(1−cos\frac{N−1}{2πn}) w(n)=0.5(1−cos2πnN−1)
-
n 是窗口内的样本索引, 0 ≤ n ≤ N − 1 0 \leq n \leq N−1 0≤n≤N−1
-
N是总长度,
-
w(n)是窗口函数的值, 它是一个序列,长度为N
3.3 窗口函数特性
-
中心对称性: w ( n ) w ˉ ( N − 1 − n ) w(n)\=w(N−1−n) w(n)wˉ(N−1−n)
-
窗口的两端值为零,这有助于减小边界效应(平滑过渡,增加连续性)
-
窗口函数在中间达到最大值
4. 代码实例:
代码1
对原始5Hz正弦波乘以汉宁窗口系数,降低旁瓣(sidelobe),拉窄主瓣(mainlobe):
结果如图:
旁瓣 1.0 => 0.4
主瓣 (50, 250) => (100, 200)
代码2
对一个440Hz信号转换为时频谱
- 没有padding和window的结果:
- 有padding,无window的结果:
效果好一些,没那么多隔断部分
- 有padding,有window的结果:
没有断层,特征反馈清晰
另外,傅里叶转换后结果是个复数,这个是复数实部的图:
这是复数虚部的:
5. 总结
频谱特性:汉宁窗口的频谱具有较低的旁瓣(sidelobe),这意味着它能够有效地抑制频谱泄漏,主瓣(mainlobe)较宽,这会降低频率分辨率。
短时傅里叶变换:STFT通过对信号进行分段,对每个片段应用汉宁窗口,能减少频谱泄漏,获得更准确的频谱信息。
本节代码.