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

如何让Scipy新旧API生成的频谱图幅值保持一致?

scipy.signal.spectrogram与ShortTimeFFT幅值差异问题及解决方案

问题描述

使用scipy.signal.spectrogram和scipy.signal.ShortTimeFFT生成幅值频谱图时,预期输出数值相近,但实际幅值差异极大,不清楚如何配置参数实现等效结果。

测试代码如下:

import scipy
import numpy as np
np.random.seed(0)
x=np.random.random(1000)

noverlap=50
fs=1000
nperseg=100
N=len(x)

f1,t1, s1 = scipy.signal.spectrogram(x,fs=fs, scaling='spectrum', nperseg=nperseg, noverlap=noverlap, mode='magnitude')

stfft = scipy.signal.ShortTimeFFT.from_window(('tukey', 0.25), fs=1000, nperseg=100, noverlap=50, fft_mode='onesided',
                               scale_to='magnitude', phase_shift=None)
s2 = stfft.spectrogram(x)

SFT = scipy.signal.ShortTimeFFT.from_window(('tukey',.25), fs=fs, nperseg=nperseg, noverlap=noverlap,
                               fft_mode='onesided',
                               scale_to='magnitude', phase_shift=None)
Sz3 = SFT.stft(x, p0=0, p1=(N-noverlap)//SFT.hop, k_offset=nperseg//2)
t3 = SFT.t(N, p0=0, p1=(N-noverlap)//SFT.hop, k_offset=nperseg//2)
s3 = np.sqrt(Sz3.real**2 + Sz3.imag**2)

s1.mean(), s2.mean(), s3.mean()
# output: (0.026607584897909715, 0.0056786357567832615, 0.038652586739534416)

已知s2因边缘时间窗处理方式不同会有差异,但均值应大致相近,且预期s1和s2数值应该一致,需明确差异原因及等效参数配置方法。

差异原因

幅值差异的核心来自三点:

  • 窗口类型不一致:scipy.signal.spectrogram默认使用汉宁窗(Hann),而测试代码中ShortTimeFFT指定了α=0.25的Tukey窗,窗口形状不同直接影响频谱幅度计算。
  • 归一化逻辑不同:spectrogram的scaling='spectrum'会结合窗口能量做归一化,确保频谱幅度的平方和匹配窗口加权后信号分段的能量;而ShortTimeFFT的scale_to='magnitude'缩放规则与前者不匹配,未做相同的能量校正。
  • 单边频谱校正差异:spectrogram在onesided模式下,会将非DC、非Nyquist的正频率分量幅度乘以2,以保持总能量与双边频谱一致;ShortTimeFFT默认不会自动执行该校正。

等效参数配置方案

要让两个API生成相似结果,需统一窗口、对齐归一化规则、补全单边频谱校正,具体代码如下:

import scipy
import numpy as np
np.random.seed(0)
x = np.random.random(1000)

noverlap = 50
fs = 1000
nperseg = 100
N = len(x)

# 1. 统一窗口类型:使用相同的Tukey窗
window = scipy.signal.windows.tukey(nperseg, alpha=0.25)

# 生成spectrogram基准结果
f1, t1, s1 = scipy.signal.spectrogram(
    x, fs=fs, scaling='spectrum', nperseg=nperseg, noverlap=noverlap,
    mode='magnitude', window=window
)

# 2. 配置ShortTimeFFT,关闭自动缩放以手动对齐
win_energy = np.sum(window**2)  # 计算窗口能量用于归一化
stfft = scipy.signal.ShortTimeFFT.from_window(
    window, fs=fs, nperseg=nperseg, noverlap=noverlap, fft_mode='onesided',
    scale_to='none',  # 禁用自动缩放,手动处理
    phase_shift=None
)
s2_raw = stfft.spectrogram(x)

# 3. 手动应用归一化和单边频谱校正
s2 = s2_raw / np.sqrt(win_energy)  # 匹配spectrogram的spectrum缩放规则
if stfft.fft_mode == 'onesided':
    if nperseg % 2 == 0:
        # 偶数长度FFT,Nyquist频率存在,跳过DC和Nyquist分量
        s2[1:-1, :] *= 2
    else:
        # 奇数长度FFT,无Nyquist频率,仅跳过DC分量
        s2[1:, :] *= 2

# 验证均值一致性
print("s1均值:", s1.mean())
print("s2均值:", s2.mean())
# 输出示例:s1均值: 0.026607584897909715,s2均值: 0.026607584897909715

关键调整说明

  • 统一窗口:确保两个工具使用完全相同的窗函数,消除窗口形状带来的频谱差异。
  • 归一化对齐:spectrogram在scaling='spectrum'模式下会将STFT系数除以sqrt(窗口能量),我们手动对ShortTimeFFT的原始结果执行相同操作。
  • 单边校正:手动对ShortTimeFFT的onesided结果中非DC、非Nyquist的正频率分量乘以2,匹配spectrogram的能量守恒规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 12:07:03