关于标准化绘制爆炸捕鱼声音梅尔频谱图的技术问询
梅尔频谱图背景声标准化问题
我尝试绘制爆炸捕鱼声音的梅尔频谱图,所有录音的背景声水平相对一致。但绘制包含爆炸声的文件时,背景声显示得很微弱,而无爆炸声的文件中背景声则明显响亮得多。我猜测这是由于爆炸声属于高强度事件,抬高了文件的整体振幅。请问如何对所有频谱图进行标准化处理,使背景声的振幅保持相似(让无爆炸声的频谱图呈现含爆炸声频谱图的背景声效果)?例如,能否从含爆炸声的文件中提取某个参数作为所有文件的参考标准?
当前使用的代码
# calculate mel features audio, sr = librosa.load(path=audio_path, sr=sample_rate) mels = librosa.feature.melspectrogram(y=audio, sr=sr) # 修正原代码中y=file_1的错误 mels_db = librosa.power_to_db(S=mels, ref=1.0) # plot fig = plt.figure(figsize=(8,5)) ax = fig.add_subplot(111) cax = ax.imshow(mels_db, interpolation='nearest', cmap='coolwarm', origin='lower') ax.set_title('Mel spec') plt.show()
示例频谱图


解决方案
你的猜测完全正确:爆炸声属于高强度瞬态信号,会拉高整个音频文件的峰值振幅,导致power_to_db计算分贝时,参考基准被爆炸声带偏,最终压缩了背景声的动态范围,使其在频谱图中显得微弱。以下是几种可行的标准化方法:
1. 基于统一背景参考功率归一化
由于所有文件的背景声水平一致,你可以从无爆炸声的文件中提取背景声的平均功率,作为全局参考值,所有文件的分贝计算都使用这个参考值:
import numpy as np import librosa import matplotlib.pyplot as plt # 第一步:从无爆炸声的参考文件中获取背景基准功率 ref_audio_path = "path/to/your/reference_file.wav" sample_rate = 44100 ref_audio, ref_sr = librosa.load(ref_audio_path, sr=sample_rate) ref_mels = librosa.feature.melspectrogram(y=ref_audio, sr=ref_sr) ref_power = np.mean(ref_mels) # 取整个文件的平均功率(无爆炸声,全是背景) # 第二步:处理所有文件时统一使用该参考值 def process_audio(audio_path, ref_power, sample_rate): audio, sr = librosa.load(audio_path, sr=sample_rate) mels = librosa.feature.melspectrogram(y=audio, sr=sr) mels_db = librosa.power_to_db(S=mels, ref=ref_power) # 用背景参考代替固定值1.0 # 绘图 fig = plt.figure(figsize=(8,5)) ax = fig.add_subplot(111) cax = ax.imshow(mels_db, interpolation='nearest', cmap='coolwarm', origin='lower') ax.set_title('Mel spec') plt.colorbar(cax, format='%+2.0f dB') plt.show() # 调用函数处理含爆炸声的文件 process_audio("path/to/explosion_file.wav", ref_power, sample_rate)
2. 从含爆炸声文件中提取背景参考
如果无法获取纯背景文件,可以从含爆炸声的文件中过滤掉爆炸片段,提取背景声的功率作为参考:
def get_background_ref(audio, sr): frame_length = 2048 hop_length = 512 # 计算每帧的能量 energy = librosa.feature.rms(y=audio, frame_length=frame_length, hop_length=hop_length)[0] # 取能量最低的10%作为背景帧(可根据实际调整百分位) background_threshold = np.percentile(energy, 10) background_mask = energy < background_threshold # 提取背景对应的梅尔频谱 mels = librosa.feature.melspectrogram(y=audio, sr=sr, frame_length=frame_length, hop_length=hop_length) background_mels = mels[:, background_mask] return np.mean(background_mels) # 处理含爆炸声文件 audio_path = "path/to/explosion_file.wav" audio, sr = librosa.load(audio_path, sr=sample_rate) ref_power = get_background_ref(audio, sr) mels = librosa.feature.melspectrogram(y=audio, sr=sr) mels_db = librosa.power_to_db(S=mels, ref=ref_power) # 绘图固定动态范围 fig = plt.figure(figsize=(8,5)) ax = fig.add_subplot(111) cax = ax.imshow(mels_db, interpolation='nearest', cmap='coolwarm', origin='lower', vmin=-80, vmax=0) ax.set_title('Mel spec') plt.colorbar(cax, format='%+2.0f dB') plt.show()
3. 固定频谱图的显示动态范围
直接在绘图时固定颜色条的上下限,强制所有频谱图使用相同的分贝区间,这样背景声的亮度会统一:
# 先确定合适的分贝范围(比如-80dB到0dB,可根据背景声调整) db_min = -80 db_max = 0 # 处理音频并绘图 audio, sr = librosa.load(audio_path, sr=sample_rate) mels = librosa.feature.melspectrogram(y=audio, sr=sr) mels_db = librosa.power_to_db(S=mels, ref=np.max(mels)) # 按文件自身峰值归一化后,固定显示范围 fig = plt.figure(figsize=(8,5)) ax = fig.add_subplot(111) # 绘图时指定vmin和vmax cax = ax.imshow(mels_db, interpolation='nearest', cmap='coolwarm', origin='lower', vmin=db_min, vmax=db_max) ax.set_title('Mel spec') plt.colorbar(cax, format='%+2.0f dB') plt.show()
内容的提问来源于stack exchange,提问作者TheyTakingTheHobbitsToIsengard
相关产品推荐
相关产品推荐

