使用numpython的谱图

2024-06-17 15:44:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要用numpy制作光谱图。我把1s的音频分成0.02s的块。然后我使用numpy计算FFT,并将其放回一幅图像中。结果很差

以下是使用matplotlib specgram函数生成的光谱图:

enter image description here

这是我的“光谱图”:

enter image description here

这是我的密码:

spect_frags = []
transform = []

for x in range(0, 8000, 160):
  spect_frags.append(spect_sample[x:x + 160])

for sample in spect_frags:
  transform.append(abs(np.fft.fft(sample).real)[0:np.fft.fft(sample).real.size//4])

我削减了3/4的频率,因为我现在不需要它们。 我不知道为什么在分辨率上有如此多的差异。我怎样才能改进它


Tags: samplein图像fftnumpyfornptransform
1条回答
网友
1楼 · 发布于 2024-06-17 15:44:13

频谱图

您可以使用以下代码重新创建specgram的粗略估计值:

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

# Read some sample file (replace with your data):
rate, data = wavfile.read('./data/aaaah.wav')
# rate=48000, data.shape=(46447, 2) ~ almost 1s of stereo signal

# Spectrogram estimation:
N = 256
S = []
for k in range(0, data.shape[0]+1, N):
    x = fft.fftshift(fft.fft(data[k:k+N,0], n=N))[N//2:N]
    # assert np.allclose(np.imag(x*np.conj(x)), 0)
    Pxx = 10*np.log10(np.real(x*np.conj(x)))
    S.append(Pxx)
S = np.array(S)

# Frequencies:
f = fft.fftshift(fft.fftfreq(N, d=1/rate))[N//2:N]
# array([    0. ,   187.5,   375. , ..., 23625. , 23812.5])

# Spectrogram rendering:
plt.imshow(S.T, origin='lower')

它输出:

enter image description here

^{}呈现时:

_ = plt.specgram(data[:,0])

enter image description here

此MCVE与specgram不同,因为轴应缩放以正确反映时间和频率,并且没有移动窗口。更准确地说:

  • x轴代表长度N=256的时间chunck指数
  • y轴是正半平面FFT指数(N//2=128),注意使用fftshiftfft之后组合频谱
  • 使用采样率和fftfreq可获得真实频率,在specgram中,其范围为0到1,因为此方法不一定知道信号采样率
  • 没有窗口重叠(使用独立的连续通道),这就是为什么MCVE比specgram稍不平滑的原因

功率估算

还要注意的是,取复数的实部与取大小不同。主要是,当你写作时:

abs(np.fft.fft(sample).real)

您没有使用复数的范数,但是由于.real调用,您完全删除了复数部分

您应该使用product of conjugatesestimate the power

10*np.log10(np.real(x*np.conj(x)))

然后使用abscomplex类型转换为float(或者只保留real部分,因为复杂部分必须为null)。最后,您可以使用十进制对数在Decibel中进行缩放

健康检查

您可以检查FFT的结果是否确实是复杂类型,并带有一个有意义的复杂部分(删除它会导致信息丢失):

x
# array([-1.56000000e+02-0.00000000e+00j, -3.94271344e+01+1.17935735e+02j,
#         4.03754070e+01+4.14695163e+01j,  1.71510716e+01+1.26920718e+01j,
#         2.15523795e+01-2.07362424e+00j, -3.03847433e+00-1.22767815e+01j,
#        -4.56347533e+00-7.36380957e-01j, -1.28048283e+01-6.80931256e+00j,
#        -2.22781473e+01+1.12096897e+01j, -1.13788549e+01+2.54314337e+01j,
#        ...])

共轭产物确实有一个空的复合部分(但仍然是complex型):

x*np.conj(x)
# array([2.43360000e+04+0.j, 1.54633365e+04+0.j, 3.34989427e+03+0.j,
#        4.55247945e+02+0.j, 4.68804979e+02+0.j, 1.59951690e+02+0.j,
#        2.13675640e+01+0.j, 2.10330365e+02+0.j, 6.21972990e+02+0.j,
#        7.76236159e+02+0.j, 1.05846430e+03+0.j, 6.54663598e+02+0.j,
#        6.95792718e+01+0.j, 6.03013130e+01+0.j, 1.11620428e+01+0.j,
#        ...])

通过断言以下内容,您可以确保这始终是正确的(健全性检查):

assert np.allclose(np.imag(x*np.conj(x)), 0)

相关问题 更多 >