如何将Matplotlib保存的频谱图PNG还原为WAV音频?
问题描述
我正在执行一项任务:批量将音频通过Librosa转换为频谱图,用Matplotlib保存为PNG;之后训练GAN生成类似频谱图,最终要把生成的频谱图还原为WAV音频。
当前提取并保存频谱图的代码如下:
y, sr = librosa.load(wav_file, sr=rate, duration=1) stft = librosa.stft(y, n_fft=n_fft, hop_length=hop_length, win_length=win_length) mag_spectrogram, phase_spectrogram = librosa.magphase(stft) plt.figure(figsize=(10, 4)) librosa.display.specshow(librosa.amplitude_to_db(np.abs(mag_spectrogram), ref=np.max), sr=sr, hop_length=hop_length, x_axis='time', y_axis='linear') plt.gca().set_title('') plt.colorbar().remove() plt.axis('off') plt.savefig("saved_mag_spec.png", bbox_inches='tight', pad_inches=0)
我知道仅靠这类PNG频谱图没法准确还原音频,必须用ISTFT并保留相位信息。但问题是,原始幅度频谱图和STFT输出(含相位)的形状是(freq_bins, frames),经过Librosa.specshow转成PNG后,尺寸和形状都变了。请问怎么加载这类PNG文件,获取必要的相位信息来还原音频?
解决方案
核心问题
你当前的做法有两个无法回避的问题:
- PNG是压缩后的可视化图像,不仅完全丢失了相位信息(你根本没保存相位数据),还破坏了原始频谱的数值精度和维度映射关系。
specshow是专为可视化设计的工具,它会把(freq_bins, frames)的频谱矩阵转成适合屏幕显示的像素矩阵,同时做了分贝转换、颜色映射,输出的PNG和原始频谱数据完全不是同一维度的东西。
直接从PNG还原音频不现实,必须调整流程,以下是可行方案:
方案1:保存原始频谱数据(推荐)
训练GAN时,不要用PNG作为输入,直接保存原始的幅度频谱(或转换后的分贝矩阵)和相位数据,这样后续还原音频时能直接复用:
import numpy as np import librosa import matplotlib.pyplot as plt # 固定参数(需和后续还原时一致) rate = 22050 n_fft = 2048 hop_length = 512 win_length = 2048 # 加载音频 y, sr = librosa.load(wav_file, sr=rate, duration=1) # 计算STFT stft = librosa.stft(y, n_fft=n_fft, hop_length=hop_length, win_length=win_length) mag_spec, phase_spec = librosa.magphase(stft) # 转分贝(优化数值范围,适合GAN训练) db_mag_spec = librosa.amplitude_to_db(mag_spec, ref=np.max) # 保存原始数值数据(关键!) np.save("mag_spec.npy", mag_spec) np.save("phase_spec.npy", phase_spec) np.save("db_mag_spec.npy", db_mag_spec) # 可选:保存可视化PNG用于验证 plt.figure(figsize=(10, 4)) librosa.display.specshow(db_mag_spec, sr=sr, hop_length=hop_length, x_axis='time', y_axis='linear') plt.gca().set_title('') plt.colorbar().remove() plt.axis('off') plt.savefig("saved_mag_spec.png", bbox_inches='tight', pad_inches=0) plt.close()
方案2:从PNG逆向还原幅度频谱(应急)
如果已经生成了PNG格式的频谱图,只能尝试还原幅度信息(相位无法从PNG获取,需额外补全),步骤如下:
import cv2 import numpy as np import librosa # 加载PNG并转为灰度图 img = cv2.imread("saved_mag_spec.png", cv2.IMREAD_GRAYSCALE) # 反转颜色(匹配specshow的明暗映射:亮区对应高幅度) img = 255 - img # 必须使用和保存PNG时完全一致的参数 sr = 22050 n_fft = 2048 hop_length = 512 duration = 1 # 计算原始频谱的维度 freq_bins = n_fft // 2 + 1 frames = librosa.time_to_frames(duration, sr=sr, hop_length=hop_length) # 将图像尺寸调整为原始频谱的(freq_bins, frames) resized_img = cv2.resize(img, (frames, freq_bins)) # 像素值逆向映射为分贝值(假设保存时分贝范围是[-80, 0]) db_mag_spec = (resized_img / 255) * 80 - 80 # 分贝转幅度频谱 mag_spec = librosa.db_to_amplitude(db_mag_spec)
方案3:补全相位并还原音频
无论用哪种方式得到幅度频谱,都需要补全相位才能还原音频,常用两种方法:
- 复用原始音频相位:如果GAN生成的频谱和原始音频属于同一类,可直接用原始相位重构:
# 加载之前保存的原始相位 phase_spec = np.load("phase_spec.npy") # 组合幅度和相位,计算逆STFT stft_recon = mag_spec * phase_spec y_recon = librosa.istft(stft_recon, hop_length=hop_length, win_length=win_length) # 保存音频 librosa.output.write_wav("recon_audio.wav", y_recon, sr=sr)
- Griffin-Lim相位重构算法:没有原始相位时,用迭代算法生成近似相位:
# 使用Griffin-Lim迭代重构音频(迭代次数越多音质越好) y_recon = librosa.griffinlim(mag_spec, n_fft=n_fft, hop_length=hop_length, win_length=win_length, iterations=32) # 保存音频 librosa.output.write_wav("recon_audio.wav", y_recon, sr=sr)
总结
最可靠的方式是全程使用原始数值频谱训练GAN,避免用PNG作为训练输入,这样既能保留数据精度,也能直接获取相位信息。如果已经用了PNG,只能逆向还原幅度后用Griffin-Lim补相位,但音质会有明显损失。
内容的提问来源于stack exchange,提问作者mahnoor.fatima
相关产品推荐
相关产品推荐

