音频处理7_MFCCs

在mel时频图的基础上,介绍MFCCs转换的代码和可视化

继上一节,这里将MFCCs变换分为以下6步

1.预处理滤波

这里是放大信号高频分量:

  • 用于提高音频信噪比,增强主要信号成分的幅度,相对于噪声,主信号能量比例更高,从而提高信号的质量和清晰度,使信号更容易被检测和分析。

  • 若高频分量的幅度较低,会导致信号在频谱上看起来不均衡。通过对信号进行滤波或调整,可以增强高频分量的幅度,使其与低频分量更平衡,提高信号的频谱均衡性。

公式如下:

			$y(t) =x(t)−\alpha⋅x(t−1), \alpha = 0.97$ 

波形图对比(有剪枝的效果):
在这里插入图片描述

y, sr = librosa.load(librosa.example('trumpet'))
y  = y[0:int(3.5 * sr)] # 取前3.5秒(之后没声音)
pre_emphasis = 0.97
y_preemphasized = np.append(y[0], y[1:] - pre_emphasis * y[:-1])

注:均值归一化可实现大部分预加重效果

2. 音频切片

信号中的频率会随着时间而变化,因此,对整个信号进行傅立叶变换是没有意义的,会随着时间推移而丢失信号的频率。假设信号中的频率在很短的时间内是稳定的,对短时间的信号片段(帧)进行傅立叶变换,再通过连接相邻帧来获得信号的频率轮廓,可以得到良好的信号频率近似值。

在语音处理中,典型的帧大小通常在20毫秒(ms)到40ms之间,相邻帧之间有50%的重叠(+/-10%)。比较常见的设置是帧大小为25ms(frame_size = 0.025 sec = 25ms),帧间隔为10ms(15ms,frame_stride = 0.01 sec)。

  • trumpet的一个frame(这里除去重叠,约15ms)如图:
    在这里插入图片描述

  • 显示完整wave和frame如图:
    在这里插入图片描述

代码如下:

frame_size = 0.025
frame_stride = 0.01 # 10ms stide 帧移时间,非重叠部分。意味着frame之间间隔(重叠)有 25- 10 = 15ms

frame_length = frame_size * sample_rate  # seconds to samples
frame_step = frame_stride * sample_rate

signal_length = len(y_preemphasized) # 117601 个采样点
frame_length = int(round(frame_length)) # 每个frame 551个采样点,每个0.025秒
frame_step = int(round(frame_step)) # 帧移采样点部分,非重合部分为220个采样点,即10ms
num_frames = int(numpy.ceil(float(numpy.abs(signal_length - frame_length)) / frame_step))  # 533,Make sure that we have at least 1 frame


pad_signal_length = num_frames * frame_step + frame_length # 117260(没有覆盖全部杨本点) + 511 = 117811 
z = numpy.zeros((pad_signal_length - signal_length)) # 117811 - 117601 = 210 覆盖全部样本点需要额外210个点
pad_signal = numpy.append(y_preemphasized, z) # 填充210个点,保证覆盖全部样本点

indices = numpy.tile(numpy.arange(0, frame_length), (num_frames, 1)) + numpy.tile(numpy.arange(0, num_frames * frame_step, frame_step), (frame_length, 1)).T
# frames的索引 [0, 550], [220, 770], [440, 990], [660,1210]…, [117040, 117590]

第一个frame前220( frame_stride * sample_rate)个点是独立的,即10ms不是重叠的;

另外330即15ms是重叠的, 最后一个frame的后 pad_signal(210个点)是0填充的;

剩下的frame都是前后重叠的。

3. 窗口函数

对每一帧应用一个窗口函数,这里应用hamming窗,类似正态分布的加权函数。

图中展示了200个样本(x轴),对应加权系数(y轴)的hamming函数:
在这里插入图片描述

代码如下:

# 生成200个样本的Hamming窗口
hamming_window = np.hamming(200) # frame_length = 200
# frames *= 0.54 - 0.46 * numpy.cos((2 * numpy.pi * n) / (frame_length - 1))  # Explicit Implementation **

# 归一化使得振幅为1
hamming_window /= np.max(hamming_window)

4. 傅里叶变换

对每一帧进行快速傅里叶变换(Fast Fourier Transform,FFT), 更具体来说是离散傅里叶变换(DFT)的一种方法: 短时傅里叶变换(Short-Time Fourier-Transform, STFT),

这里的FFT函数为:

F F T ( x k ) = ∑ n = 0 N − 1 x k e − i 2 π N k ∗ n FFT(x_k) = \sum_{n=0}^{N-1} x_k e^{-i \frac{2\pi}{N} k*n} FFT(xk)=n=0N1xkeiN2πkn

这里的k是第k个frame,N是n_fft, 即一个frame所含的样本点(通常为256 or 512), 代码如下:

mag_frames = numpy.absolute(numpy.fft.rfft(frames, N)) # Magnitude of the FFT

之后计算功率谱:

pow_frames = ((1.0 / N) * ((mag_frames) ** 2)) # Power Spectrum

5. 滤波器组(Filter Bank)

选择滤波器组的数量(通常是40个),并计算各个滤波器的频率。

滤波器组是一组三角形波,用于拟合声波

  • 为了迎合人的感知,滤波器在低频区域较密,在高频区域则较散
  • 每个滤波器是一个三角型频率波,线性下降至相邻滤波器的中心频率处时,响应为 0
  • 每个滤波器对一定频率范围内的频率成分有最大的灵敏度,而对超出该范围的频率成分灵敏度逐渐减小

如图展示 10个三角滤波器(num_mel = 10),为适应人类感知,低频密,且振幅高:
在这里插入图片描述

下图是另一种展示方式:
在这里插入图片描述

  • 滤波器组输出函数:

1. librosa.filters.mel

	librosa.filters.mel 是 Librosa 库中的一个函数,用于生成梅尔滤波器(Mel filter bank)。

梅尔滤波器是一组滤波器,用于将频谱转换为梅尔频谱,这是语音和音频信号处理中常用的一种表示方法。

2. 输入参数:

	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。

3. 返回值

	mel_basis :  shape=(n_mels, 1 + n_fft/2), 每一行代表一个梅尔滤波器。

6. Mel频谱

有了特定数量的梅尔组频率,转为对应的梅尔谱步骤如下

  • 转换频率轴:首先将频率轴从赫兹转换为 Mel 频率轴。

high_freq_mel = (2595 * numpy.log10(1 + (sample_rate / 2) / 700)) # Convert Hz to Mel

  • 定义滤波器中心频率:在 Mel 频率轴上等间隔划分40个点作为滤波器的中心频率。

mel_points = numpy.linspace(low_freq_mel, high_freq_mel, num_mel + 2)

  • 构建滤波器:将这些中心频率转换回赫兹,然后在赫兹频率轴上构建三角滤波器。

fbank,通过for循环完成三角滤波器设置

  • 应用滤波器:将三角滤波器应用于功率谱,以提取各个滤波器的频带能量。

filter_banks = numpy.dot(pow_frames, fbank.T)

整体代码如下:


num_mel = 40
low_freq_mel = 0
N = 512 # NFFT

frames = pad_signal[indices.astype(numpy.int32, copy=False)] # (533, 551)
frames *= numpy.hamming(frame_length)
mag_frames = numpy.absolute(numpy.fft.rfft(frames, N))  # Magnitude of the FFT (533, 257)
pow_frames = ((1.0 / N) * ((mag_frames) ** 2))  # Power Spectrum (533, 257)

high_freq_mel = (2595 * numpy.log10(1 + (sample_rate / 2) / 700))  # Convert Hz to Mel
mel_points = numpy.linspace(low_freq_mel, high_freq_mel, num_mel + 2)  # Equally spaced in Mel scale
hz_points = (700 * (10**(mel_points / 2595) - 1))  # Convert Mel to Hz
bin = numpy.floor((N + 1) * hz_points / sample_rate)

fbank = numpy.zeros((num_mel, int(numpy.floor(N / 2 + 1)))) # (num_mel, N /2 + 1)
for m in range(1, num_mel + 1):   # 依次制作不同频率的三角滤波器
    f_m_minus = int(bin[m - 1])   # left
    f_m = int(bin[m])             # center
    f_m_plus = int(bin[m + 1])    # right

    for k in range(f_m_minus, f_m):
        fbank[m - 1, k] = (k - bin[m - 1]) / (bin[m] - bin[m - 1])
    for k in range(f_m, f_m_plus):
        fbank[m - 1, k] = (bin[m + 1] - k) / (bin[m + 1] - bin[m])

filter_banks = numpy.dot(pow_frames, fbank.T) # (533,257) x (257, num_mel) = (533, 257)
filter_banks = numpy.where(filter_banks == 0, numpy.finfo(float).eps, filter_banks)  # 替换0值,方便转db
filter_banks = 20 * numpy.log10(filter_banks)  # 通过log将梅尔组各个三角频率的能量值转换为分贝(dB)
  • filter_banks可视化:
    在这里插入图片描述

  • 均值归一化 (Normalize)

filter_banks -= (numpy.mean(filter_banks, axis=0) + 1e-8)

代码就一行,用于提升信噪比效果,区别在y轴底部区域:
在这里插入图片描述

7. 离散余弦变换(DCT)

mel谱的滤波组内,各滤波器相关性强,即存在冗余。

离散余弦变换(DCT)是将滤波器组的信号转换为不同频率的余弦函数,均匀各频率的信息分布,降低相关性。

DCT变换后能量更紧凑,通常集中在前面几个离散系数中,即mel频率倒谱系数(MFCCs)。

  • MFCCs的倒谱系数

对于语音识别,通常保留第2-13个倒谱系数。

第0个系数代表整体信息,无法区分音素。

第1个系数代表低频信息,通常是噪声。

第2-13个倒谱系数:包含音素的重要特征。

第14个以上:捕捉细节和滤波器的快速变化,受噪音和瞬变影响较大,贡献小。

0-12的MFCCs系数可视化:

在这里插入图片描述

代码:

from scipy.fftpack import dct  # Apply DCT to the filter banks
num_ceps = 12
mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1:(num_ceps + 1)]
  • 正弦提升

以弱化更高的 MFCC,提高噪声信号中的语音识别能力。

效果如图:
在这里插入图片描述

代码如下,这里cep_lifter用于控制提升程度

cep_lifter = 22 # 22-27
(nframes, ncoeff) = mfcc.shape
n = numpy.arange(ncoeff)
lift = 1 + (cep_lifter / 2) * numpy.sin(numpy.pi * n / cep_lifter)
mfcc *= lift  

Reference

  • https://haythamfayek.com/2016/04/21/speech-processing-for-machine-learning.html
  • https://github.com/disanda/d_code/tree/master/3.Audio/5_mel_num
  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值