手动实现STFT与scipy.signal.stft首段DFT差异的技术求助
嘿,我懂你遇到的问题了——自己手搓的STFT和scipy的对比下来,就多了t=0时刻的一段DFT结果,其余部分完全对齐。这大概率是窗口起始索引的生成逻辑或者时间轴计算和scipy不一致导致的,我给你调整一下关键部分的代码,应该就能解决:
修正后的STFT核心实现
import numpy as np from scipy.signal import get_window def my_stft(samples, fs, wind_len_time=0.5, overlap_factor=0.5, zero_padding_factor=4): wind_len = int(fs * wind_len_time) # 注意:scipy的重叠长度要求是整数,这里转成int避免步长计算误差 overlap = int(wind_len * overlap_factor) step = wind_len - overlap nfft = wind_len * zero_padding_factor # 对齐scipy的窗口起始索引逻辑:最后一个窗口必须完整覆盖信号片段 # 原来的代码可能把起始索引范围设到了len(samples),导致多生成一个窗口 section_starts = np.arange(0, len(samples) - wind_len + 1, step) # 用scipy默认的hann窗,避免窗口类型差异 window = get_window('hann', wind_len) stft_result = [] for start in section_starts: end = start + wind_len segment = samples[start:end] # 补零到指定长度 padded_segment = np.pad(segment, (0, nfft - wind_len), mode='constant') # 加窗后做DFT windowed_segment = padded_segment * window dft = np.fft.rfft(windowed_segment) stft_result.append(dft) # 时间轴用窗口中心时间,和scipy保持一致(之前可能用了窗口起始时间,导致出现t=0) t = (section_starts + wind_len / 2) / fs f = np.fft.rfftfreq(nfft, 1/fs) return f, t, np.array(stft_result).T
关键修正点
- 窗口起始索引范围:scipy的STFT只会生成能完整覆盖信号的窗口,所以起始索引的上限设为
len(samples) - wind_len + 1,避免多生成一个超出信号范围的窗口(哪怕你做了补零,scipy也不会处理这种不完整的窗口)。 - 重叠长度转整数:scipy的
noverlap参数要求是整数,之前如果用浮点数计算重叠,可能导致步长出现小数,进而生成多余的起始点。 - 时间轴对齐:scipy默认用窗口的中心时间作为每个DFT结果对应的时间点,而不是窗口起始时间——如果之前你用起始时间计算,就会多出t=0这个点,和scipy的时间轴错位。
你可以用这段代码和scipy的signal.stft对比,应该就能完全匹配了。
内容的提问来源于stack exchange,提问作者Gideon Kogan




