STFT和声谱图,梅尔频谱(Mel Bank Features)与梅尔倒谱(MFCCs)

最近小编在做ASC(Acoustic Scene Classification)问题,不管是用传统的GMM模型,还是用机器学习中的SVM或神经网络模型,提取声音特征都是第一步。梅尔频谱和梅尔倒谱就是使用非常广泛的声音特征形式,小编与它们斗争已有一段时间了,在此总结一下使用它们的经验。

STFT和声谱图(Spectrogram)

声音信号本是一维的时域信号,直观上很难看出频率变化规律。如果通过傅里叶变换把它变到频域上,虽然可以看出信号的频率分布,但是丢失了时域信息,无法看出频率分布随时间的变化。为了解决这个问题,很多时频分析手段应运而生。短时傅里叶,小波,Wigner分布等都是常用的时频域分析方法。

短时傅里叶变换(STFT)是最经典的时频域分析方法。傅里叶变换(FT)想必大家都不陌生,这里不做专门介绍。所谓短时傅里叶变换,顾名思义,是对短时的信号做傅里叶变化。那么短时的信号怎么得到的? 是长时的信号分帧得来的。这么一想,STFT的原理非常简单,把一段长信号分帧、加窗,再对每一帧做傅里叶变换(FFT),最后把每一帧的结果沿另一个维度堆叠起来,得到类似于一幅图的二维信号形式。如果我们原始信号是声音信号,那么通过STFT展开得到的二维信号就是所谓的声谱图。
声谱图示意图

有很多工具方便地支持STFT展开,如果你是和小编一样是python爱好者,可以使用scipy库中的signal模块。如果你想做STFT分解的音频信号(wav文件)的路径存在path变量中,可通过下面的代码得到STFT数据。

import wavio
import numpy as np
from scipy import signal

wav_struct=wavio.read(path)
### 梅尔频谱图的计算公式 梅尔频谱图(Mel Spectrogram)是通过对音频信号进行短时傅里叶变换(STFT),并将其映射到梅尔频率尺度上来实现的一种表示方法。以下是其详细的计算过程: #### 1. 短时傅里叶变换 (Short-Time Fourier Transform, STFT) 首先,对输入的音频信号 \(x[n]\) 进行分帧处理,并施加窗口函数 \(w[n]\),得到每帧的时间序列数据。随后,对该时间序列执行离散傅里叶变换(DFT)。这一步的结果是一个二维矩阵,其中每一列代表某一时刻的频谱幅值。 \[ X[k, t] = \sum_{n=0}^{N-1} x[n+mL] w[n] e^{-j\frac{2\pi}{N}kn}, \quad k = 0, 1, ..., N/2 \] 这里: - \(X[k,t]\): 表示第\(t\)帧在频率索引\(k\)处的幅度; - \(m\): 帧号; - \(L\): 帧移长度; - \(N\): FFT 的点数; 此部分涉及基础的频谱分析理论[^1]。 #### 2. 转换至功率 接着,将复数值的频谱转化为功率形式,即取模平方运算: \[ P[k, t] = |X[k, t]|^2 \] #### 3. 应用梅尔滤波器组 为了将线性频率轴上的频谱转换为梅尔频率尺度,需设计一组三角形带通滤波器构成的梅尔滤波器组。假设共有 \(M\) 个滤波器,则对于每一个滤波器 \(i\) 和对应的频率索引 \(k\),有如下关系: \[ H_i(k) = \begin{cases} \frac{k - f(l)}{f(c) - f(l)}, & f(l) \leq k < f(c), \\ \frac{f(h) - k}{f(h) - f(c)}, & f(c) \leq k < f(h), \\ 0, & \text{otherwise}, \end{cases} \] 此处 \(l,c,h\) 分别指代当前滤波器覆盖范围内的左边界、中心以及右边界频率位置,在赫兹单位下由梅尔尺度反向映射得出[^2]。 最终通过这些滤波器作用于原始功率上获得新的特征维度集合: \[ M[i, t] = \log{\left( \sum_k H_i(k)P[k, t] + \epsilon \right)} \] 这里的 \(\epsilon\) 是一个小正数用来防止对零求自然对数操作引发错误。 以上便是完整的梅尔频谱图构建流程及其背后的数学表达式描述。 ```python import numpy as np from scipy.fftpack import fft def compute_mel_spectrogram(signal, sample_rate, n_fft, hop_length, num_mels): # Step 1: Compute the Short-time Fourier transform (STFT). stft_result = [] window_size = n_fft for i in range(0, len(signal)-window_size+1, hop_length): frame = signal[i:i+window_size]*np.hamming(window_size) spectrum = abs(fft(frame))[:int(n_fft//2)+1] stft_result.append(spectrum) # Convert to power spectrogram. power_spec = np.square(np.array(stft_result)) # Define Mel filterbank and apply it on the power spectrogram. mel_filters = create_mel_filterbank(sample_rate, n_fft, num_mels) mel_spec = np.dot(power_spec, mel_filters.T) # Apply logarithmic compression. log_mel_spec = np.log(mel_spec + 1e-8) return log_mel_spec def create_mel_filterbank(sr, n_fft, num_mels): min_freq = 0 max_freq = sr / 2 mels = np.linspace(frequency_to_mel(min_freq), frequency_to_mel(max_freq), num_mels + 2) freqs = mel_to_frequency(mels).astype(int) fb_matrix = np.zeros((num_mels, int(1+n_fft//2))) for i in range(1, num_mels + 1): left = freqs[i-1] center = freqs[i] right = freqs[i+1] tri_func = lambda k : ((k-left)/(center-left))*(k>=left)*(k<center)\ +((right-k)/(right-center))*(k>=center)*(k<right) fb_matrix[i-1,:] += tri_func(np.arange(len(fb_matrix[i-1,:]))) return fb_matrix # Helper functions for converting between Hz and Mel scales. def frequency_to_mel(freq): return 2595 * np.log10(1 + freq / 700.) def mel_to_frequency(mel): return 700*(10**(mel/2595.)-1) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值