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
这是工作频谱图的示例:
正在玩 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
这是工作频谱图的示例: