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

音频转频谱图及逆向转换:解决窗口过长报错问题

频谱图转音频报错解决与实现方法

背景

已实现音频转频谱图的功能,代码如下:

'''
Function for converting all the waves files to image files.
'''
import matplotlib.pyplot as plt
import librosa

def wavesToSpecs(audio_path:str, spec_path:str):
    fname = str(audio_path).split('/')[-1].split('.')[0]

    samples, sample_rate = librosa.load(audio_path, sr=None)

    fig, ax = plt.subplots(figsize=(5,5))
    ax.set_axis_off()
    # 注意:原代码中Fs=2是错误的,应使用实际采样率sample_rate,否则频谱图频率轴完全错误
    ax.specgram(samples, Fs=sample_rate)
    fig.savefig(f'{fname}.png', bbox_inches="tight", pad_inches=0)
    plt.close(fig)
    del sample_rate, samples, fig, ax

wavesToSpecs("/content/wav/test.wav", "test.jpg")

尝试将频谱图转回音频时,运行以下代码出现报错:

import librosa
from scipy import signal 
import scipy.io.wavfile as sf    

file = r"C:\desktop\file.wav"
sample_rate, samples = sf.read(file)

nperseg = int(sample_rate * 0.001 * 20)
frequencies, times, spectrogram = signal.spectrogram(samples, 
                                                     sample_rate, 
                                                     nperseg=nperseg, 
                                                     window=signal.hann(nperseg))

audio_signal = librosa.griffinlim(spectrogram)
print(audio_signal, audio_signal.shape)

sf.write('test.wav', audio_signal, sample_rate)

报错信息

Exception has occurred: ValueError window is longer than input signal

问题分析与解决方案

1. 先解决报错问题

报错原因是nperseg(窗口长度)计算值超过了音频信号的总采样数。比如你的音频时长不足20ms,sample_rate * 0.02的结果会大于len(samples)。

解决方法:

nperseg = int(sample_rate * 0.02)  # 20ms窗口
nperseg = min(nperseg, len(samples))  # 避免窗口过长
# 如果nperseg还是过小,调整窗口时长,比如改成10ms
if nperseg < 32:
    nperseg = 32

2. 核心问题:你没有从生成的频谱图(PNG)还原,而是重新计算了原音频的频谱

你当前的代码是直接对原音频做频谱分析再还原,这不是“从频谱图转回音频”的正确流程。正确的流程分两种情况:

情况A:转频谱图时同时保存数值化频谱数据(推荐)

修改音频转频谱图的代码,同时保存频谱的数值数据,这样还原时能保证精度:

import matplotlib.pyplot as plt
import librosa
import numpy as np
from scipy import signal

def wavesToSpecs(audio_path:str):
    fname = str(audio_path).split('/')[-1].split('.')[0]
    samples, sample_rate = librosa.load(audio_path, sr=None)

    # 计算频谱(匹配matplotlib specgram的默认参数)
    frequencies, times, spectrogram = signal.spectrogram(
        samples, 
        fs=sample_rate,
        nperseg=256,  # specgram默认值
        window='hann',
        scaling='spectrum'
    )
    # 保存数值化频谱和参数
    np.save(f'{fname}_spec.npy', spectrogram)
    np.save(f'{fname}_params.npy', np.array([sample_rate, frequencies, times]))

    # 生成可视化频谱图(可选)
    fig, ax = plt.subplots(figsize=(5,5))
    ax.set_axis_off()
    ax.specgram(samples, Fs=sample_rate)
    fig.savefig(f'{fname}.png', bbox_inches="tight", pad_inches=0)
    plt.close(fig)
    return spectrogram, sample_rate

wavesToSpecs("/content/wav/test.wav")

然后从保存的数值数据还原音频:

import numpy as np
import librosa
import scipy.io.wavfile as sf

# 加载保存的频谱数据
fname = "test"
spectrogram = np.load(f'{fname}_spec.npy')
params = np.load(f'{fname}_params.npy')
sample_rate = params[0]

# 使用Griffin-Lim算法还原音频
audio_signal = librosa.griffinlim(spectrogram)

# 保存还原后的音频
sf.write(f'{fname}_recovered.wav', audio_signal, int(sample_rate))

情况B:从PNG图像还原音频(精度较低)

如果只能从已生成的PNG图像还原,需要将图像像素值转换为频谱幅度,但会丢失大量信息:

import numpy as np
import matplotlib.pyplot as plt
import librosa
import scipy.io.wavfile as sf

# 读取频谱图图像
img = plt.imread("test.png")
# 转换为灰度图(如果是彩色)
if len(img.shape) == 3:
    img = np.mean(img, axis=2)
# 反转颜色(因为specgram默认是深色背景,亮色代表高幅度)
img = 1 - img
# 调整为合适的频谱幅度范围(系数需根据实际情况调整)
spectrogram = img * 1000

# 使用Griffin-Lim还原,替换为原音频的采样率
sample_rate = 44100
audio_signal = librosa.griffinlim(spectrogram)

sf.write('recovered_from_img.wav', audio_signal, sample_rate)

内容的提问来源于stack exchange,提问作者jangles

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:11:16