因此,我正在尝试对wav PCM 24位44.1khz文件进行带通滤波。我想做的是从0Hz-22Khz的每个频率带通。
到目前为止,我已经加载了数据并可以在Matplot上显示它,如下所示。
但是当我应用从这里得到的带通滤波器
http://scipy-cookbook.readthedocs.io/items/ButterworthBandpass.html
因此,我正在尝试以100-101Hz的频率通过测试,这是我的代码:
from WaveData import WaveData
import matplotlib.pyplot as plt
from scipy.signal import butter, lfilter, freqz
from scipy.io.wavfile import read
import numpy as np
from WaveData import WaveData
class Filter:
def __init__(self, wav):
self.waveData = WaveData(wav)
def butter_bandpass(self, lowcut, highcut, fs, order=5):
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(order, [low, high], btype='band')
return b, a
def butter_bandpass_filter(self, data, lowcut, highcut, fs, order):
b, a = self.butter_bandpass(lowcut, highcut, fs, order=order)
y = lfilter(b, a, data)
return y
def getFilteredSignal(self, freq):
return self.butter_bandpass_filter(data=self.waveData.file['Data'], lowcut=100, highcut=101, fs=44100, order=3)
def getUnprocessedData(self):
return self.waveData.file['Data']
def plot(self, signalA, signalB=None):
plt.plot(signalA)
if signalB != None:
plt.plot(signalB)
plt.show()
if __name__ == "__main__":
# file = WaveData("kick.wav")
# fileA = read("kick0.wav")
f = Filter("kick.wav")
a, b = f. butter_bandpass(lowcut=100, highcut=101, fs=44100)
w, h = freqz(b, a, worN=22000) ##Filted signal is not working?
f.plot(h, w)
print("break")
我不明白我哪里出了问题。
谢谢
答案 0 :(得分:1)
因此,您的代码存在一些问题,这意味着您无法正确绘制结果,尽管我认为这不是您的主要问题。
在您链接的示例中,它们精确地显示了计算和以不同顺序绘制过滤器的过程:
for order in [3, 6, 9]:
b, a = butter_bandpass(lowcut, highcut, fs, order=order)
w, h = freqz(b, a, worN=2000)
plt.plot((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)
您当前无法正确缩放频率轴,或者无法调用绝对值来从h
获取真实的信息,就像上面的正确代码一样。
但是,您的主要问题是您的陡峭带通(即只有100Hz-101Hz)。我很少见到如此锐利的滤波器,因为这非常耗费处理能力(将需要很多滤波器系数),并且因为您仅查看1Hz的范围,所以它将完全摆脱所有其他频率。
因此,您所显示的增益为0的图形可能是正确的。如果您使用their example并将带通截止频率更改为100Hz-> 101Hz,则输出结果是零(几乎,如果不是完全的话)的数组。这是因为它只会查看1Hz范围内的信号能量,如果考虑的话,该信号非常 非常
如果您这样做是为了进行分析,则频率间隔往往会大得多,即Octave Bands(或更小的八度音阶划分)。
由于我不确定您的最终目的,因此无法确切说明您应该采用哪种路线。但是,在当今时代,在高达20kHz的每个单个频率上使用带通滤波器似乎是很愚蠢的。
如果我没记错的话,在纸上用针头进行的第spectrogram次尝试中,有一些将这种技术与模拟带通滤波器组结合使用来分析频率含量。因此,这使我认为您可能正在寻找与频谱图有关的东西?它使您可以分析整个信号的频率信息与时间的关系,并且仍然具有所有信号的幅度信息。 Python已将频谱图功能作为scipy或Matplotlib的一部分包括在内。
答案 1 :(得分:1)
@WoodyDev所说的是正确的:44.1 kHz中的1 Hz对任何类型的滤波器而言, way way 都太小了。只需查看滤波器系数butter
返回:
In [3]: butter(5, [100/(44.1e3/2), 101/(44.1e3/2)], btype='band')
Out[3]:
(array([ 1.83424060e-21, 0.00000000e+00, -9.17120299e-21, 0.00000000e+00,
1.83424060e-20, 0.00000000e+00, -1.83424060e-20, 0.00000000e+00,
9.17120299e-21, 0.00000000e+00, -1.83424060e-21]),
array([ 1. , -9.99851389, 44.98765092, -119.95470631,
209.90388506, -251.87018009, 209.88453023, -119.93258575,
44.9752074 , -9.99482662, 0.99953904]))
看看b
系数(第一个数组):它们在1e-20处的值,这意味着滤波器设计完全无法收敛,如果将其应用于任何信号,输出将为零,即是找到的东西。
您没有提到您的应用程序,但是如果您真的想将信号的频率范围保持在100到101 Hz之间,则可以对信号进行零填充FFT,将该频段之外的频谱部分归零,以及IFFT(请查看numpy.fft
模块中的rfft
,irfft
和rfftfreq
)。
以下是使用FFT在傅立叶域中应用砖墙式带通滤波器的功能:
import numpy.fft as fft
import numpy as np
def fftBandpass(x, low, high, fs=1.0):
"""
Apply a bandpass signal via FFTs.
Parameters
----------
x : array_like
Input signal vector. Assumed to be real-only.
low : float
Lower bound of the passband in Hertz. (If less than or equal
to zero, a high-pass filter is applied.)
high : float
Upper bound of the passband, Hertz.
fs : float
Sample rate in units of samples per second. If `high > fs / 2`,
the output is low-pass filtered.
Returns
-------
y : ndarray
Output signal vector with all frequencies outside the `[low, high]`
passband zeroed.
Caveat
------
Note that the energe in `y` will be lower than the energy in `x`, i.e.,
`sum(abs(y)) < sum(abs(x))`.
"""
xf = fft.rfft(x)
f = fft.rfftfreq(len(x), d=1 / fs)
xf[f < low] = 0
xf[f > high] = 0
return fft.irfft(xf, len(x))
if __name__ == '__main__':
fs = 44.1e3
N = int(fs)
x = np.random.randn(N)
t = np.arange(N) / fs
import pylab as plt
plt.figure()
plt.plot(t, x, t, 100 * fftBandpass(x, 100, 101, fs=fs))
plt.xlabel('time (seconds)')
plt.ylabel('signal')
plt.legend(['original', 'scaled bandpassed'])
plt.show()
您可以将其放置在文件fftBandpass.py
中,然后仅用python fftBandpass.py
运行它即可看到它创建了以下图形:
请注意,我必须将1 Hz的带通信号缩放100,因为在带通很多之后,信号中的能量很小。还请注意,在如此小的通带内的信号几乎只是100 Hz左右的正弦波。
如果将以下内容放在自己的代码中:from fftBandpass import fftBandpass
,则可以使用fftBandpass
函数。
您可以尝试的另一种方法是将信号100x抽取,因此将其转换为以441 Hz采样的信号。 441 Hz中的1 Hz仍然是一个疯狂的窄通带,但是比起尝试使原始信号带通,您可能有更好的运气。请参见scipy.signal.decimate
,但不要尝试使用q=100
对其进行调用,而应递归地对信号进行2,然后2、5,然后5的抽取(总抽取100x)。