如何从WAV文件提取特征片段识别葡萄牙语数字并对比
问题描述
我通过HTTP请求获取到由谷歌语音朗读的6位葡萄牙语数字音频WAV文件,数字间以静音分隔。本地已存储0-9每个数字的WAV音频,需从本地文件提取代表性原始数据片段,与请求得到的音频分片对比。现有Python代码通过二进制片段匹配识别数字,但不知如何选择本地WAV的代表性片段;尝试用pydub的split_on_silence拆分音频并导出、生成MD5哈希对比均失败,求解决方案。
现有代码
def discaptcher(file_content): # legacy code, other audio footprints_to_numbers = { b"\x72\x00\x77\x00\x7a\x00\x4a\x00\x58\x00\x7a\x00\x55\x00\x53\x00": "0", # noqa b"\x86\x00\x7f\x00\x8c\x00\x7f\x00\x71\x00\x62\x00\x52\x00\x52\x00": "1", # noqa b"\x15\x00\x1e\x00\x1b\x00\x05\x00\x16\x00\x1b\x00\x20\x00\x17\x00": "2", # noqa b"\x8e\xff\x85\xff\x8c\xff\x85\xff\x79\xff\xb6\xff\xbd\xff\xaa\xff": "3", # noqa b"\xfd\xff\x0b\x00\x0f\x00\xf6\xff\xd7\xff\xcf\xff\xbb\xff\xb2\xff": "4", # noqa b"\x87\x00\x85\x00\x7c\x00\x77\x00\x7b\x00\x81\x00\x79\x00\x7f\x00": "5", # noqa b"\x65\xff\x87\xff\xb1\xff\xa3\xff\x6a\xff\x20\xff\x26\xff\x2c\xff": "6", # noqa b"\xb9\xfe\xbb\xfe\xae\xfe\xa6\xfe\xa1\xfe\xb8\xfe\xd2\xfe\xfb\xfe": "7", # noqa b"\x5b\xff\x66\xff\x90\xff\x85\xff\x76\xff\x75\xff\x79\xff\x75\xff": "8", # noqa b"\x16\x01\x1e\x01\x29\x01\x13\x01\x0b\x01\x05\x01\x06\x01\xf8\x00": "9", } # noqa captcha_dict = {} captcha = "" for snd, lt in footprints_to_numbers.items(): pos = [m.start() for m in regex.finditer(regex.escape(snd), file_content)] for i in pos: captcha_dict[i] = lt for key in sorted(captcha_dict.keys()): captcha += captcha_dict[key] return captcha
解决方案
一、修复split_on_silence音频拆分问题
split_on_silence失败大多是阈值或格式不匹配导致,调整参数并统一音频格式:
from pydub import AudioSegment from pydub.silence import split_on_silence # 加载目标音频并统一格式(和本地音频保持一致,比如单声道、16000Hz采样率、16bit位深) target_audio = AudioSegment.from_wav("captcha.wav") target_audio = target_audio.set_frame_rate(16000).set_channels(1).set_sample_width(2) # 调整拆分参数:根据实际音频的静音长度和音量设置阈值 chunks = split_on_silence( target_audio, min_silence_len=80, # 静音段最小时长(ms),按需调整 silence_thresh=-35, # 静音音量阈值(dBFS),数值越小越敏感 keep_silence=40 # 保留片段前后少量静音,避免切到有效音频 ) # 导出拆分后的单个数字片段 for idx, chunk in enumerate(chunks): chunk.export(f"split_digit_{idx}.wav", format="wav")
二、提取本地音频的代表性片段
放弃固定二进制片段,改用更稳定的中间段采样或特征提取:
1. 中间稳定片段采样
跳过音频开头结尾的过渡音,取中间100-200ms的原始数据作为特征:
def get_representative_chunk(audio_path, duration_ms=150): audio = AudioSegment.from_wav(audio_path) audio = audio.set_frame_rate(16000).set_channels(1).set_sample_width(2) # 计算中间位置,避免开头结尾的杂音 start_ms = (len(audio) - duration_ms) // 2 end_ms = start_ms + duration_ms return audio[start_ms:end_ms].raw_data # 预生成本地0-9数字的特征片段 local_digit_features = {} for num in range(10): local_digit_features[str(num)] = get_representative_chunk(f"local_{num}.wav")
2. MFCC特征匹配(更高容错性)
用音频特征替代二进制匹配,避免格式差异导致的失败:
import librosa import numpy as np def get_mfcc_feature(audio_path): y, sr = librosa.load(audio_path, sr=16000) mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13) # 取特征均值作为数字的代表特征 return np.mean(mfcc, axis=1) # 预生成本地数字的MFCC特征 local_mfcc_features = {} for num in range(10): local_mfcc_features[str(num)] = get_mfcc_feature(f"local_{num}.wav") # 对比片段与本地特征的相似度 def match_digit(chunk_path): chunk_feature = get_mfcc_feature(chunk_path) min_distance = float("inf") matched_num = "" for num, feat in local_mfcc_features.items(): # 计算欧氏距离,越小越相似 distance = np.linalg.norm(chunk_feature - feat) if distance < min_distance: min_distance = distance matched_num = num return matched_num
三、替代MD5哈希的匹配方案
MD5要求完全匹配容错性为0,改用滑动窗口+差异容忍匹配:
def sliding_window_match(target_data, local_data, tolerance=2, max_diff_ratio=0.1): target_len = len(target_data) local_len = len(local_data) if target_len < local_len: return False # 滑动窗口遍历目标数据 for i in range(target_len - local_len + 1): window = target_data[i:i+local_len] # 统计差异字节数(允许小范围内的数值差异) diff_count = sum(1 for a, b in zip(window, local_data) if abs(a - b) > tolerance) if diff_count < len(local_data) * max_diff_ratio: return True return False
内容的提问来源于stack exchange,提问作者Kafka4PresidentNow
相关产品推荐
相关产品推荐

