scipy 具有对数频率轴的频谱图?

scipy spectrogram with logarithmic frequency axis?

正在玩 scipy.signal.spectrogram。效果很好。

from scipy.io import wavfile
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt

sf, audio = wavfile.read('serious.wav')
sig = np.mean(audio, axis=1)
f, t, Sxx = signal.spectrogram(sig, sf, scaling='spectrum')

plt.pcolormesh(t, f, np.log10(Sxx))
plt.ylabel('f [Hz]')
plt.xlabel('t [sec]')
plt.show()

这是结果:

但是频率轴是线性的。对于音频,这通常不是我想要的 - 无论如何,这不是我想要的。

有没有办法哄scipy.signal.spectrogram输出对数频率刻度?

如果 scipy 无法做到这一点,您能否推荐一种同样简单的方法来获得此结果?


编辑:问题不在于图像的显示方式。问题在于 signal.spectrogram()

生成数据的方式

我把代码改成这样:

plt.pcolormesh(t, f, np.log10(Sxx))
plt.ylabel('f [Hz]')
plt.xlabel('t [sec]')
plt.yscale('log')
plt.savefig('spec.png')
plt.show()

现在图像看起来像这样:

f 向量(由 signal.spectrogram() 生成)如下所示:

array([    0.      ,   172.265625,   344.53125 ,   516.796875,
         689.0625  ,   861.328125,  1033.59375 ,  1205.859375,
        1378.125   ,  1550.390625,  1722.65625 ,  1894.921875,
        2067.1875  ,  2239.453125,  2411.71875 ,  2583.984375,
...
       19982.8125  , 20155.078125, 20327.34375 , 20499.609375,
       20671.875   , 20844.140625, 21016.40625 , 21188.671875,
       21360.9375  , 21533.203125, 21705.46875 , 21877.734375,
       22050.      ])

这是一个线性分布。我在低端需要更多的点数,在高端需要更少的点数。

我找到问题了。 FFT 是线性的。我的图像是对数的。默认的频率间隔在频谱的下半部分太大。

所以我只是通过 nperseg 参数增加了频率样本的数量。在此示例中,连续频率之间的距离为 1 Hz,这是非常好的分辨率。此外,符号日志缩放是最好的。

npts = int(sf)
f, t, Sxx = signal.spectrogram(sig, sf, nperseg=npts)
plt.yscale('symlog')

当然,范围顶部的频率太多,因此需要在 f 和 Sxx 数组中进行一些修剪(尺寸必须匹配,因此以相同的方式修剪它们)。此外,显示频率的范围必须限制在 10 - 20000 或一些合理的值。所有这些优化都超出了这个答案的范围。

但我把脚本带到了可以使用的地步,我把它放在 GitHub:

https://github.com/FlorinAndrei/soundspec

这是工作频谱图的示例: