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

11
当我使用以下代码中的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)

spectrogram generated by using matplotlib

然而,如果我使用scipy页面中提供的示例代码生成声谱图,使用以下代码,我会得到类似于这样的结果:
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]')

enter image description here

为了查明发生了什么,我尝试使用第一个方法生成的Pxxfreqsbins,然后使用第二个方法绘制出数据:
plt.pcolormesh(bins, freqs, Pxx)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')

enter image description here

生成的图形与第二种方法生成的图形几乎相同。 因此,看来scipy.signal.spectrogram没有问题。问题在于我们绘制图形的方式。 我想知道plt.pcolormesh是否是绘制频谱图的正确方法,尽管这种方法在scipy文档中被建议使用。 类似的问题已经被提出here,但目前还没有解决方案。

这是一个已经有一年历史的问题,但是scipy文档页面上仍然没有改变。 - Ahmad Moussa
3个回答

18

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

scale:[‘default’ | ‘linear’ | ‘dB’] spec 中值的缩放。'linear' 表示无缩放。'dB' 返回 dB 缩放中的值。当 mode 是 'psd' 时,这是 dB 功率(10 * log10)。否则,这是 dB 幅度(20 * log10)。'default' 是 'dB' 如果 mode 是 'psd' 或 'magnitude',否则是 'linear'。如果 mode 是 'angle' 或 'phase',则必须为 'linear'

mode:[‘default’ | ‘psd’ | ‘magnitude’ | ‘angle’ | ‘phase’] 要使用的频谱类型。默认是 'psd',它代表功率谱密度。'complex' 返回复数值的频率谱,'magnitude' 返回幅度谱,'angle' 返回未展开的相位谱,'phase' 返回展开的相位谱。

要使用 pcolormesh 获得类似的结果,需要等效地缩放数据。

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

我认为pcolormesh的例子在缩放方面不正确。你可以清楚地看到例子中的载波,但添加的噪声信号不可见。


为什么我们在这里乘以10?我把它去掉了,它仍然可以工作。 - Ahmad Moussa
1
这只是我们用分贝来表示功率的方式。请参阅此文章。通常我们使用10作为比例因子,但有时当我们谈论功率传输(或者准确地说,我们使用需要平方才能得到功率的量度)时,我们使用比例因子20。换句话说,不乘以10也可以工作,但不能代表有效的量度。 - LemurPwned

1

-1
请使用以下代码:
plt.pcolormesh(times, frequencies, spectrogram, norm = matplotlib.colors.Normalize(0,1))

这将在绘图之前对数据进行归一化,以便您可以正确地可视化颜色。matplotlib.colors.Colormap 的文档说:“通常,Colormap 实例用于将数据值(浮点数)从区间 [0, 1] 转换为相应 Colormap 表示的 RGBA 颜色。”如果您的值超出了此范围,它可能会绘制为深色(我认为是这样的)。


我尝试了这个,但它没有起作用,它只绘制了一个黄色的背景,而不是我最初得到的紫色背景。你能发布你整个生成光谱图的代码吗? - Ahmad Moussa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接