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

4

我以前从未处理过音频信号,对信号处理知之甚少。尽管如此,我需要使用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,时间为秒?

1
XY比例因子是未知的,如果不指定采样率的话。 - hotpaw2
2个回答

8

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

plt.specgram(frames, Fs=rate)

扩展Nukolas的答案,结合我的更改matplotlib中绘图比例的方法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()

结果:

生成的图片


这段内容涉及IT技术。

5
  • 首先,光谱图是一个随时间变化的信号频谱内容的表示方式 - 这是一个以频率域为基础的时域波形(例如正弦波、文件“song.wav”或其他任意波形-即振幅随时间变化)的表达。

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

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

  • 为了使绘图中的轴更直观,使用x轴和y轴标签,并且您还可以使用类似于这里的方法缩放轴值(即更改单位)。

最重要的是-尽量用代码详细解释:下面是我的示例。

    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

5Hz频谱图:

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