如何更改pyplot.specgram x和y轴缩放?

时间:2015-11-12 20:23:31

标签: matplotlib signal-processing spectrogram

我之前从未使用过音频信号,但我对信号处理知之甚少。不过,我需要使用 matplotlib 库中的pyplot.specgram函数来表示和音频信号。我就是这样做的。

import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile

rate, frames = wavfile.read("song.wav")
plt.specgram(frames)

我得到的结果是下面这个很好的频谱图: enter image description here

当我看到x轴和y轴,我认为频率时间域时,我无法理解频率缩放的事实从 0到1.0 ,时间从 0到80k 。 背后的直觉是什么,更重要的是,如何以人性化的格式表示它,频率为0到100k,时间是以秒为单位?

2 个答案:

答案 0 :(得分:4)

  • 首先,频谱图表示信号的频谱内容随时间的变化 - 这是时域的频域表示波形(例如正弦波,您的文件“song.wav”或其他任意波形 - 即振幅随时间的变化)。

  • 频率值(y轴,赫兹)完全取决于波形的采样频率(“song.wav”),范围从“0”到“采样频率/ 2”,上限是“奈奎斯特频率”或“折叠频率”(https://en.wikipedia.org/wiki/Aliasing#Folding)。如果没有另外指定,matplotlib specgram函数将自动确定输入波形的采样频率,定义为1 / dt,其中dt是波形的离散采样之间的时间间隔。您可以将选项Fs ='sampling rate'传递给specgram函数以手动定义它是什么。如果你弄清楚并将这些变量自己传递给specgram函数,你就会更容易理解正在发生的事情

  • 时间值(x轴,秒)完全取决于“song.wav”的长度。如果使用较大的窗口长度来计算每个光谱切片,您可能会注意到一些空白或填充(想想 - 垂直排列并水平平铺以创建光谱图像的单个光谱)

  • 要使图中的轴更直观,请使用x轴和y轴标签,还可以使用类似this的方法缩放轴值(即更改单位)

带回家的消息 - 尝试使用您的代码更加冗长:请参阅下面的示例。

    import matplotlib.pyplot as plt
    import numpy as np

    # generate a 5Hz sine wave
    fs = 50
    t = np.arange(0, 5, 1.0/fs)
    f0 = 5
    phi = np.pi/2
    A = 1
    x = A * np.sin(2 * np.pi * f0 * t +phi)

    nfft = 25

    # plot x-t, time-domain, i.e. source waveform
    plt.subplot(211)
    plt.plot(t, x)
    plt.xlabel('time')
    plt.ylabel('amplitude')

    # plot power(f)-t, frequency-domain, i.e. spectrogram
    plt.subplot(212)
    # call specgram function, setting Fs (sampling frequency) 
    # and nfft (number of waveform samples, defining a time window, 
    # for which to compute the spectra)
    plt.specgram(x, Fs=fs, NFFT=nfft, noverlap=5, detrend='mean', mode='psd')
    plt.xlabel('time')
    plt.ylabel('frequency')
    plt.show()

5Hz_spectrogram:

enter image description here

答案 1 :(得分:4)

正如其他人所指出的那样,您需要指定采样率,否则您将获得标准化频率(介于0和1之间)和样本索引(0到80k)。幸运的是,这很简单:

plt.specgram(frames, Fs=rate)

扩展Nukolas的答案并合并我的Changing plot scale by a factor in matplotlibmatplotlib intelligent axis labels for timedelta 我们不仅可以在频率轴上获得kHz,还可以在时间轴上获得分钟和秒数。

import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile

cmap = plt.get_cmap('viridis') # this may fail on older versions of matplotlib
vmin = -40  # hide anything below -40 dB
cmap.set_under(color='k', alpha=None)

rate, frames = wavfile.read("song.wav")
fig, ax = plt.subplots()
pxx, freq, t, cax = ax.specgram(frames[:, 0], # first channel
                                Fs=rate,      # to get frequency axis in Hz
                                cmap=cmap, vmin=vmin)
cbar = fig.colorbar(cax)
cbar.set_label('Intensity dB')
ax.axis("tight")

# Prettify
import matplotlib
import datetime

ax.set_xlabel('time h:mm:ss')
ax.set_ylabel('frequency kHz')

scale = 1e3                     # KHz
ticks = matplotlib.ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))
ax.yaxis.set_major_formatter(ticks)

def timeTicks(x, pos):
    d = datetime.timedelta(seconds=x)
    return str(d)
formatter = matplotlib.ticker.FuncFormatter(timeTicks)
ax.xaxis.set_major_formatter(formatter)
plt.show()

<强>结果:

resulting image