【问题标题】:How to get time/freq from FFT in Python如何在 Python 中从 FFT 获取时间/频率
【发布时间】:2016-05-02 00:33:20
【问题描述】:

我在管理 FFT 数据时遇到了一点问题。我一直在寻找许多有关如何进行 FFT 的示例,但我无法从其中任何一个中得到我想要的。我有一个采样率为 44kHz 的随机波文件,我想每 X 毫秒获得 N 个谐波的幅度,假设 100 毫秒就足够了。我试过这段代码:

import scipy.io.wavfile as wavfile
import numpy as np
import pylab as pl

rate, data = wavfile.read("sound.wav")
t = np.arange(len(data[:,0]))*1.0/rate
p = 20*np.log10(np.abs(np.fft.rfft(data[:2048, 0])))
f = np.linspace(0, rate/2.0, len(p))
pl.plot(f, p)
pl.xlabel("Frequency(Hz)")
pl.ylabel("Power(dB)")
pl.show()

这是我使用的最后一个示例,我在 stackoverflow 的某个地方找到了它。问题是,这得到了我想要的幅度,得到了频率,但根本没有时间。据我所知,FFT 分析是 3D 的,这是所有谐波的“合并”结果。我明白了:

X-axis = Frequency, Y-axis = Magnitude, Z-axis = Time (invisible)

根据我对代码的理解,t 是时间 - 看起来是这样,但在代码中不需要 - 不过我们可能会需要它。 p 是幂(或幅度)数组,但它似乎是每个频率 f 的所有幅度的平均值,这是频率数组。我不想要平均值/合并值,我想要每 X 毫秒 N 次谐波的幅度。

长话短说,我们可以得到:所有频率的 1 个量级。

我们想要:N 个频率的所有幅度,包括存在某个幅度的时间。

结果应如下所示:[时间、频率、幅度] 所以最后如果我们想要 3 个谐波,它看起来像:

[0,100,2.85489] #100Hz harmonic has 2.85489 amplitude on 0ms
[0,200,1.15695] #200Hz ...
[0,300,3.12215]
[100,100,1.22248] #100Hz harmonic has 1.22248 amplitude on 100ms
[100,200,1.58758]
[100,300,2.57578]
[200,100,5.16574]
[200,200,3.15267]
[200,300,0.89987]

不需要可视化,结果应该只是上面列出的数组(或哈希/字典)。

【问题讨论】:

  • 快速傅里叶变换 (FFT) 算法计算序列的离散傅里叶变换 (DFT) 或其逆。傅立叶分析将信号从其原始域(通常是时间或空间)转换为频域中的表示,反之亦然。一旦对原始信号应用傅立叶变换,我认为您不应该有时间。它被转换为频域。同样,当您对频域信号应用傅里叶逆变换时,您会得到时域信号。在这里阅读更多。 en.wikipedia.org/wiki/Fast_Fourier_transform
  • 感谢您的评论,尽管您向我解释了算法是如何工作的,但我仍然不知道是否可以从中获得这样的输出,或者是否需要完全不同的方式。分别如何,如果不使用 FFT,你能得到我描述的输出。知道精确到一点 FFT 的工作原理并不能解决问题。
  • 嗯..如果我逆傅立叶变换,我会得到时域信号,但那是原始的,不是吗?除此之外,我仍然不知道在哪里可以得到所有三个值。

标签: python numpy matplotlib scipy fft


【解决方案1】:

除了@Paul R 的回答,scipy.signal.spectrogramscipy's signal processing module 中的spectrogram function

上面链接的例子如下:

from scipy import signal
import matplotlib.pyplot as plt

# Generate a test signal, a 2 Vrms sine wave whose frequency linearly
# changes with time from 1kHz to 2kHz, corrupted by 0.001 V**2/Hz of
# white noise sampled at 10 kHz.

fs = 10e3
N = 1e5
amp = 2 * np.sqrt(2)
noise_power = 0.001 * fs / 2
time = np.arange(N) / fs
freq = np.linspace(1e3, 2e3, N)
x = amp * np.sin(2*np.pi*freq*time)
x += np.random.normal(scale=np.sqrt(noise_power), size=time.shape)


#Compute and plot the spectrogram.

f, t, Sxx = signal.spectrogram(x, fs)
plt.pcolormesh(t, f, Sxx)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.show()

【讨论】:

  • 从 f、t 和 Sxx 中获取我需要的所有三个值非常容易。问题是将wav文件导入其中,然后它应该可以顺利运行。但是,我认为 scipy 库将在所有组件中兼容,但似乎并非如此。来自 scipy.io 的 wavfile.read 从 wav 创建一个 ndarray,但不能作为 signal.spectrogram 的输入,即使上面代码中的 x 也是 ndarray。我完全不知道,因为文档似乎没有显示与 scipy.io.wavfile.read 的任何联系
【解决方案2】:

您似乎正在尝试实现spectrogram,它是一系列功率谱估计,通常通过一系列(通常是重叠的)FFT 来实现。由于您只有一个 FFT(频谱),因此您还没有时间维度。将您的 FFT 代码放入一个循环中,每次迭代处理一个样本块(例如 1024 个),连续块之间有 50% 的重叠。生成的光谱序列将是时间 v 频率 v 幅度的 3D 数组。

我不是 Python 人,但我可以给你一些伪代码,应该足以让你编码:

N = length of data input
N_FFT = no of samples per block (== FFT size, e.g. 1024)
i = 0 ;; i = index of spectrum within 3D output array
for block_start = 0 to N - block_start
    block_end = block_start + N_FFT
    get samples from block_start .. block_end
    apply window function to block (e.g. Hamming)
    apply FFT to windowed block
    calculate magnitude spectrum (20 * log10( re*re + im*im ))
    store spectrum in output array at index i
    block_start += N_FFT / 2            ;; NB: 50% overlap
    i++
 end

【讨论】:

  • 我知道你的意思,我可以确认频谱图就是我要找的。但是,作为一个新手,我不知道如何做到这一点。有没有人给我一些提示或完整的例子?
【解决方案3】:

编辑:哦,看来这会返回值,但它们根本不适合音频文件。即使它们可以用作频谱图上的幅度,它们也不会在您可以在许多音乐播放器中看到的那些经典音频可视化器中工作。我也尝试了matplotlib的pylab的谱图,但结果是一样的。

import os
import wave
import pylab
import math
from numpy import amax
from numpy import amin

def get_wav_info(wav_file,mi,mx):
    wav = wave.open(wav_file, 'r')
    frames = wav.readframes(-1)
    sound_info = pylab.fromstring(frames, 'Int16')
    frame_rate = wav.getframerate()
    wav.close()
    spectrum, freqs, t, im = pylab.specgram(sound_info, NFFT=1024, Fs=frame_rate)
    n = 0
    while n < 20:
        for index,power in enumerate(spectrum[n]):
            print("%s,%s,%s" % (n,int(round(t[index]*1000)),math.ceil(power*100)/100))
        n += 1

get_wav_info("wave.wav",1,20)

任何提示如何获得可用于可视化的 dB? 基本上,我们显然已经从上面的代码中得到了我们所需要的一切,只是如何让它返回正常值呢?忽略 mimx 因为它们只是调整数组中的值以适应 mi..mx 间隔 - 这将用于可视化使用。如果我是正确的,此代码中的spectrum 返回数组数组,其中包含来自freqs 数组的每个频率的幅度,这些幅度根据t 数组按时出现,但该值如何工作 - 它真的是幅度吗如果它返回这些奇怪的值,如果是,例如如何将其转换为 dB。

tl;dr 我需要像音乐播放器一样的可视化工具的输出,但它不应该实时工作,我只想要数据,但值不适合 wav 文件。

Edit2:我注意到还有一个问题。对于 90 秒 wav,t 数组包含直到 175.x 的时间,考虑到 frame_rate 与 wav 文件是正确的,这似乎很奇怪。所以现在我们有两个问题:spectrum 似乎没有返回正确的值(如果我们得到正确的时间,也许它会适合)和t 似乎返回的正是 wav 的两倍。

已修复:案件已完全解决。

import os
import pylab
import math
from numpy import amax
from numpy import amin
from scipy.io import wavfile
frame_rate, snd = wavfile.read(wav_file)
sound_info = snd[:,0]
spectrum, freqs, t, im = pylab.specgram(sound_info,NFFT=1024,Fs=frame_rate,noverlap=5,mode='magnitude')

Specgram 需要稍作调整,我只用 scipy.io 库(而不是 wave 库)加载了一个频道。同样没有将模式设置为幅度,它返回 10log10 而不是 20log10,这就是它没有返回正确值的原因。

【讨论】:

    猜你喜欢
    • 2011-12-02
    • 2019-03-14
    • 2011-07-16
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 2019-09-05
    • 1970-01-01
    • 2013-01-13
    相关资源
    最近更新 更多