You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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文件,获取必要的相位信息来还原音频?


解决方案

核心问题

你当前的做法有两个无法回避的问题:

  1. PNG是压缩后的可视化图像,不仅完全丢失了相位信息(你根本没保存相位数据),还破坏了原始频谱的数值精度和维度映射关系。
  2. 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:补全相位并还原音频

无论用哪种方式得到幅度频谱,都需要补全相位才能还原音频,常用两种方法:

  1. 复用原始音频相位:如果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)
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:57:41