1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| import numpy as np import librosa from typing import Tuple
class SERFeatureExtractor: """ 语音情感特征提取器 ================== 论文使用四种手工特征: 1. MFCC (Mel-Frequency Cepstral Coefficients) 2. ZCR (Zero Crossing Rate) 3. RMSE (Root Mean Square Energy) 4. Chroma STFT 座舱适配: - 采样率: 16kHz (座舱麦克风) - 帧长: 25ms, 帧移: 10ms - 环境噪声: 引擎/风噪/路噪 """ def __init__(self, sample_rate: int = 16000): self.sr = sample_rate self.n_mfcc = 40 self.n_fft = 2048 self.hop_length = 512 def extract(self, audio: np.ndarray) -> dict: """ 提取语音情感特征 Args: audio: [N] 音频波形, 16kHz Returns: features dict """ mfcc = librosa.feature.mfcc( y=audio, sr=self.sr, n_mfcc=self.n_mfcc, n_fft=self.n_fft, hop_length=self.hop_length ) mfcc_mean = mfcc.mean(axis=1) mfcc_std = mfcc.std(axis=1) zcr = librosa.feature.zero_crossing_rate( audio, frame_length=self.n_fft, hop_length=self.hop_length ) rmse = librosa.feature.rms( y=audio, frame_length=self.n_fft, hop_length=self.hop_length ) chroma = librosa.feature.chroma_stft( y=audio, sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length ) tempo, _ = librosa.beat.beat_track(y=audio, sr=self.sr) f0 = librosa.yin(audio, fmin=80, fmax=400, sr=self.sr) f0_valid = f0[f0 > 0] centroid = librosa.feature.spectral_centroid( y=audio, sr=self.sr, n_fft=self.n_fft, hop_length=self.hop_length ) return { 'mfcc_mean': mfcc_mean, 'mfcc_std': mfcc_std, 'zcr_mean': float(zcr.mean()), 'zcr_std': float(zcr.std()), 'rmse_mean': float(rmse.mean()), 'rmse_std': float(rmse.std()), 'chroma_mean': chroma.mean(axis=1), 'tempo': float(tempo), 'f0_mean': float(f0_valid.mean()) if len(f0_valid) > 0 else 0, 'f0_std': float(f0_valid.std()) if len(f0_valid) > 0 else 0, 'centroid_mean': float(centroid.mean()), 'feature_dim': self.n_mfcc * 2 + 2 + 2 + 12 + 1 + 2 + 1 }
FEATURE_EMOTION_MAP = { "愤怒": {"f0": "高且变化大", "rmse": "高", "zcr": "高", "tempo": "快"}, "悲伤": {"f0": "低且平稳", "rmse": "低", "zcr": "低", "tempo": "慢"}, "高兴": {"f0": "高且变化", "rmse": "中高", "zcr": "中", "tempo": "快"}, "恐惧": {"f0": "极高", "rmse": "低", "zcr": "高", "tempo": "不规则"}, "惊讶": {"f0": "突变", "rmse": "突高", "zcr": "突高", "tempo": "暂停后快"}, "中性": {"f0": "平稳", "rmse": "低", "zcr": "低", "tempo": "正常"}, "疲劳": {"f0": "低且单调", "rmse": "低", "zcr": "低", "tempo": "慢"}, }
print("语音情感特征映射:") for emotion, features in FEATURE_EMOTION_MAP.items(): print(f"\n{emotion}:") for feat, desc in features.items(): print(f" {feat}: {desc}")
|