时频分析方法:梅尔频谱图

        梅尔频谱图(Mel Spectrogram)是一种将音频信号转换为视觉表示的方法,常用于语音识别、音乐信息检索和音频分析等领域。它以人耳的感知方式为基础,通过将频谱转换为梅尔刻度(Mel scale)来更好地反映人类对不同频率的感知。

一、背景知识

1.频谱图(Spectrogram)

频谱图是一种视觉表示,用于展示信号在频率和时间上的变化情况。它通过将信号分解为不同频率成分并显示这些频率成分在整个信号中的变化来工作。

以下是频谱图的一些关键特点:

  • 时间轴:表示信号随着时间的演变。
  • 频率轴:表示信号中包含的不同频率成分。
  • 颜色或亮度:表示信号在特定时间和频率上的强度或振幅。颜色越亮(或越深),通常表示信号在该频率下的强度越高。

2.梅尔刻度(Mel Scale)

梅尔刻度是一种感知音频频率的标度,旨在更好地模拟人耳对频率的感知。与线性频率刻度不同,梅尔刻度反映了人类听觉系统在不同频率范围内的分辨能力。

以下是梅尔刻度的主要特性:

  • 感知线性:梅尔刻度在低频段近似于线性变换,而在高频段近似于对数变化。这符合人耳对低频更敏感、高频分辨率较低的特性。
  • 频率与梅尔频率的转换:可以通过公式将线性频率(赫兹)转换为梅尔频率(梅尔)$\mathrm{Mel}(f)=2595\cdot\log_{10}\left(1+\frac f{700}\right)$,反之可以将梅尔频率转换回线性频率$f=700\cdot\left(10^{\frac{\mathrm{Mel}}{2595}}-1\right)$
  • 语言信息处理:梅尔刻度常用于语音识别中的特征提取,例如梅尔频率倒谱系数(MFCC)。

二、具体操作

步骤1:预处理音频信号

  • 采样:音频信号通常以某个固定的采样率进行采样。
  • 分帧:音频信号被分成多个重叠的短时间帧,每帧通常有20-40毫秒的持续时间。
  • 加窗:对每一帧施加窗函数,以减少频谱泄露。

步骤2:短时傅里叶变换(STFT)

  • 对每一帧进行傅里叶变换,得到频谱图。这将信号从时间域转换为频率域,表示为频率和时间的二维数组。

步骤3:计算功率谱

  • 功率谱表示信号在不同频率上的功率分布。对于一个时间域信号,其功率谱是信号在各个频率成分上功率的一个表示。
  • 从 STFT 的复数矩阵中计算功率谱,即将每个频率成分的幅度平方,得到频率成分的能量。

步骤4:应用梅尔滤波器

  • 将频谱图转换为梅尔频谱图的关键步骤是应用梅尔滤波器组。这些滤波器是三角形的,重叠覆盖在频谱的不同频率区域。
  • 每个滤波器的中心频率根据梅尔刻度均匀分布。
  • 使用梅尔滤波器组将功率谱从线性频率尺度转换到梅尔频率尺度。梅尔滤波器组是基于梅尔尺度(对数尺度的频率尺度)设计的滤波器。
  • 通过将功率谱与梅尔滤波器组进行矩阵乘法,得到梅尔频谱图。

步骤5:对数压缩

  • 对通过滤波器的能量进行对数压缩,以模拟人耳的响度感知。通常使用对数函数或其他类似函数进行压缩。梅尔对数压缩公式如下,其中$\epsilon$是一个极小值,用于避免对数零的问题。

$\text{Mel Spectrogram}=\log(\text{Mel Spectrogram}+\epsilon)$

步骤6:生成梅尔频谱图

  • 将对数压缩后的梅尔滤波器输出排列成一个矩阵,形成梅尔频谱图。横轴是时间,纵轴是梅尔频率。

三、代码示例

Python代码

import numpy as np
import librosa.display
import matplotlib.pyplot as plt


# 生成两个正弦波并混合一些白噪声
def generate_signal(duration, sample_rate):
    t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
    sine_wave1 = 0.5 * np.sin(2 * np.pi * 440 * t)  # 440 Hz
    sine_wave2 = 0.25 * np.sin(2 * np.pi * 880 * t)  # 880 Hz
    noise = 0.05 * np.random.randn(len(t))
    return sine_wave1 + sine_wave2 + noise


if __name__ == '__main__':
    # ==== 生成示例信号 ====
    sr = 22050  # 采样率(Hz)
    dt = 2.0  # 采样时间(s)
    signal = generate_signal(duration=dt, sample_rate=sr)
    # ==== 参数设置 ====
    n_fft = 2048  # 短时傅里叶变换的窗长
    hop_length = 512  # 帧移
    n_mel = 128  # 梅尔频带数量

    # ==== 计算 STFT ====
    D = librosa.stft(signal, n_fft=n_fft, hop_length=hop_length)

    # ==== 计算功率谱 ====
    S = np.abs(D) ** 2

    # ==== 计算梅尔滤波器组 ====
    mel_filter = librosa.filters.mel(sr=sr, n_fft=n_fft, n_mels=n_mel)
    mel_spectrogram = np.dot(mel_filter, S)   # 计算梅尔频谱图

    # ==== 对数压缩 ====
    mel_spectrogram_db = librosa.power_to_db(mel_spectrogram, ref=np.max)   # 转换为对数频谱图

    # ==== 绘制梅尔频谱图 ====
    plt.figure(figsize=(10, 4))
    librosa.display.specshow(mel_spectrogram_db, sr=sr, hop_length=hop_length, x_axis='time', y_axis='mel', fmax=sr / 2)
    plt.colorbar(format='%+2.0f dB')
    plt.title('Mel Spectrogram')
    plt.show()

         值得注意的是,"librosa.feature.melspectrogram()"可以在函数内部自动完成 STFT 和梅尔尺度转换。因此,可以将代码修改为以下版本。

import numpy as np
import matplotlib.pyplot as plt
import librosa.feature
import librosa.display


def generate_signal(duration, sample_rate):
    t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
    # 生成两个正弦波并混合一些白噪声
    sine_wave1 = 0.5 * np.sin(2 * np.pi * 440 * t)  # 440 Hz
    sine_wave2 = 0.25 * np.sin(2 * np.pi * 880 * t)  # 880 Hz
    noise = 0.05 * np.random.randn(len(t))
    return sine_wave1 + sine_wave2 + noise


if __name__ == '__main__':
    # ==== 生成示例信号 ====
    sr = 22050  # 采样率(Hz)
    dt = 2.0  # 采样时间(s)
    signal = generate_signal(duration=dt, sample_rate=sr)

    # ==== 参数设置 ====
    n_fft = 2048  # 短时傅里叶变换的窗长
    hop_length = 512  # 帧移
    n_mel = 128  # 梅尔频带数量

    # ==== 计算梅尔频谱图 ====
    mel_spectrogram = librosa.feature.melspectrogram(y=signal, sr=sr, n_fft=n_fft, hop_length=hop_length, n_mels=n_mel)

    # ==== 将结果转换为对数刻度(分贝) ====
    mel_spectrogram_db = librosa.power_to_db(mel_spectrogram, ref=np.max)

    # ==== 绘制梅尔频谱图 ====
    plt.subplot(2, 1, 2)
    librosa.display.specshow(mel_spectrogram_db, sr=sr, hop_length=hop_length, x_axis='time', y_axis='mel', fmax=sr / 2)
    plt.colorbar(format='%+2.0f dB')
    plt.title('Mel Spectrogram')
    plt.xlabel('Time (s)')
    plt.ylabel('Frequency (Mel)')
    plt.tight_layout()
    plt.show()

        以上代码所绘结果图的y轴均表示为梅尔频率刻度,而不是线性频率。根据上文可知,梅尔频率刻度通过对数变换将频率压缩,使得低频部分更加细致,而高频部分相对粗糙。这种变化更好地模拟人类的听觉特性,但是如果你希望显示实际的频率,可以使用"librosa.display.specshow()"中的" y_axis='hz' "来转换。

librosa.display.specshow(mel_spectrogram_db, sr=sr, hop_length=hop_length, x_axis='time', y_axis='hz', fmax=sr / 2)

在MATLAB中绘制梅尔频谱图,可以按照以下步骤进行: 1. 首先,将音频信号加载到MATLAB中。你可以使用`audioread`函数来读取音频文件或直接使用已有的音频信号。 2. 将音频信号通过短时傅里叶变换(STFT)转换为时频表示。你可以使用`spectrogram`函数来实现这一步骤。 ```matlab [s, fs] = audioread('your_audio_file.wav'); % 读取音频文件,s为音频信号,fs为采样率 window = hamming(window_length); % 定义窗函数,例如汉明窗 noverlap = window_length - hop_size; % 计算重叠的样本数 [S, f, t] = spectrogram(s, window, noverlap, NFFT, fs); % 进行短时傅里叶变换 ``` 其中,`window_length`是窗口的长度,`hop_size`是帧移的样本数,`NFFT`是FFT的点数。 3. 计算梅尔滤波器组的中心频率。你可以使用以下代码计算梅尔滤波器组的中心频率: ```matlab num_filters = 26; % 梅尔滤波器的数量 f_min = 0; % 最低频率 f_max = fs/2; % 最高频率 mel_min = hz2mel(f_min); % 将最低频率转换为梅尔频率 mel_max = hz2mel(f_max); % 将最高频率转换为梅尔频率 mel_centers = linspace(mel_min, mel_max, num_filters+2); % 在梅尔频率上均匀分布滤波器中心 ``` 其中,`hz2mel`是将赫兹频率转换为梅尔频率的函数。 4. 将频谱转换为梅尔频谱。你可以使用以下代码将频谱转换为梅尔频谱: ```matlab mel_filters = melFilterBank(fs, NFFT, mel_centers); % 计算梅尔滤波器组 mel_spectrum = mel_filters * abs(S); % 将频谱乘以梅尔滤波器组 ``` 其中,`melFilterBank`是一个自定义函数,用于计算梅尔滤波器组。 5. 绘制梅尔频谱图。你可以使用以下代码绘制梅尔频谱图: ```matlab figure; imagesc(t, mel_centers, 10*log10(mel_spectrum)); % 绘制梅尔频谱图 axis xy; % 设置y轴方向为正方向 xlabel('时间(秒)'); ylabel('梅尔频率(Hz)'); colorbar; % 添加颜色条 ``` 这样,你就可以在MATLAB中绘制出梅尔频谱图了。注意,上述代码中的函数和变量名仅供参考,具体实现需要根据你的需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值