使用 scipy.signal.spectrogram 时的频谱图错误

Wrong spectrogram when using scipy.signal.spectrogram

当我通过以下代码使用来自 matplotlib 的 plt.specgram 时,生成的频谱图是正确的

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

sample_rate, samples = wavfile.read('.\Wav\test.wav')

Pxx, freqs, bins, im = plt.specgram(samples[:,1], NFFT=1024, Fs=44100, noverlap=900)

但是,如果我使用 scipy page 中给出的示例代码和以下代码生成频谱图,我会得到如下内容:

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

sample_rate, samples = wavfile.read('.\Wav\test.wav')

frequencies, times, spectrogram = signal.spectrogram(samples[:,1],sample_rate,nfft=1024,noverlap=900, nperseg=1024)

plt.pcolormesh(times, frequencies, spectrogram)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')

为了调试发生了什么,我尝试使用第一种方法生成的Pxxfreqsbins,然后使用第二种方法绘制出数据:

plt.pcolormesh(bins, freqs, Pxx)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')

生成的图和第二种方法生成的图几乎一样。 所以,看来 scipy.signal.spectrogram 毕竟没有问题。问题是我们绘制图表的方式。我想知道 plt.pcolormesh 是否是绘制频谱图的正确方法,尽管 scipy document

中建议使用这种方法

有人问过类似的问题here,但目前还没有解决方案。

specgram 的默认缩放模式是 'dB'(来自 specgram 文档)

scale : [ ‘default’ | ‘linear’ | ‘dB’ ] The scaling of the values in the spec. ‘linear’ is no scaling. ‘dB’ returns the values in dB scale. When mode is ‘psd’, this is dB power (10 * log10). Otherwise this is dB amplitude (20 * log10). ‘default’ is ‘dB’ if mode is ‘psd’ or ‘magnitude’ and ‘linear’ otherwise. This must be ‘linear’ if mode is ‘angle’ or ‘phase’.

mode : [ ‘default’ | ‘psd’ | ‘magnitude’ | ‘angle’ | ‘phase’ ] What sort of spectrum to use. Default is ‘psd’, which takes the power spectral density. ‘complex’ returns the complex-valued frequency spectrum. ‘magnitude’ returns the magnitude spectrum. ‘angle’ returns the phase spectrum without unwrapping. ‘phase’ returns the phase spectrum with unwrapping.

要获得与 pcolormesh 类似的结果,您需要对数据进行等效缩放。

plt.pcolormesh(times, frequencies, 10*np.log10(spectrogram))

我认为 pcolormesh 示例的缩放比例不正确。您可以清楚地看到示例中的载波,但看不到添加的噪声信号。

改用这个:

plt.pcolormesh(times, frequencies, spectrogram, norm = matplotlib.colors.Normalize(0,1))

这会在绘图之前对数据进行归一化,以便您可以正确地可视化颜色。 matplotlib.colors.Colormap 上的文档说 'Typically Colormap instances are used to convert data values (floats) from the interval [0, 1] to the RGBA color that the respective Colormap represents.' 如果您的值超出此范围,它可能会将其绘制为深色(我相信。)

您应该在 pcolormesh 函数中使用一种非线性颜色图。

尝试设置norm=matplotlib.colors.LogNorm(vmin=np.amin(spectrogram), vmax=np.amax(spectrogram))

norm=matplotlib.colors.PowerNorm(gamma=0.5).

有关详细信息,请参阅 https://matplotlib.org/stable/tutorials/colors/colormapnorms.html