简体   繁体   English

通过Scipy在Python中创建带通滤波器?

[英]Create a band-pass filter via Scipy in Python?

Is there a way to create a quick bandpass filter via scipy or librosa in Python 3.6 for a 16KHz wav file to filter noise outside of human voice band of 300-3400Hz ? 有没有一种方法可以在Python 3.6中通过scipylibrosa为16KHz wav文件创建快速带通滤波器,以滤除300-3400Hz的人声频带之外的噪声? Here is a sample wav file with background noise at low frequency. 这是一个样本wav文件 ,具有低频背景噪声。

UPDATE: Yes, I have already seen/tried How to implement band-pass Butterworth filter with Scipy.signal.butter . 更新:是的,我已经看到/尝试过如何使用Scipy.signal.butter实现带通Butterworth滤波器 Unfortunately, the filtered sound is horribly deformed. 不幸的是,过滤后的声音严重变形。 Essentially, the whole code does this: 本质上,整个代码执行此操作:

lo,hi=300,3400
sr,y=wavfile.read(wav_file)
b,a=butter(N=6, Wn=[2*lo/sr, 2*hi/sr], btype='band')
x = lfilter(b,a,y)
sounddevice.play(x, sr)  # playback

What am I doing wrong or how can this be improved so that the background noise is filtered out correctly. 我在做什么错,或者如何改善此问题,以便正确滤除背景噪音。

Here is the visualization of the original and filtered file using the link above. 这是使用上面的链接的原始文件和过滤文件的可视化。 The visualization looks reasonable, but it sounds horrible :( How can this be fixed? 可视化看起来很合理,但是听起来很可怕:(如何解决?

在此处输入图片说明

Apparently the problem occurs when writing unnormalized 64 bit floating point data. 显然,写入未标准化的64位浮点数据时会发生此问题。 I get an output file that sounds reasonable by either converting x to 16 bit or 32 bit integers, or by normalizing x to the range [-1, 1] and converting to 32 floating point. 通过将x转换为16位或32位整数,或将x标准化为[-1,1]范围并转换为32个浮点数,我得到了一个听起来合理的输出文件。

I'm not using sounddevice ; 我不使用sounddevice instead, I'm saving the filtered data to a new WAV file and playing that. 相反,我将过滤后的数据保存到新的WAV文件中并进行播放。 Here are the variations that worked for me: 以下是对我有用的变体:

# Convert to 16 integers
wavfile.write('off_plus_noise_filtered.wav', sr, x.astype(np.int16))

or... 要么...

# Convert to 32 bit integers
wavfile.write('off_plus_noise_filtered.wav', sr, x.astype(np.int32))

or... 要么...

# Convert to normalized 32 bit floating point
normalized_x = x / np.abs(x).max()
wavfile.write('off_plus_noise_filtered.wav', sr, normalized_x.astype(np.float32))

When outputting integers, you could scale up the values to minimize the loss of precision that results from truncating the floating point values: 输出整数时,可以按比例放大这些值,以最大程度地减少由于截断浮点值而导致的精度损失:

x16 = (normalized_x * (2**15-1)).astype(np.int16)
wavfile.write('off_plus_noise_filtered.wav', sr, x16)

The following code is for generating band pass filter from here : https://scipy.github.io/old-wiki/pages/Cookbook/ButterworthBandpass 以下代码用于从此处生成带通滤波器: https : //scipy.github.io/old-wiki/pages/Cookbook/ButterworthBandpass

     from scipy.signal import butter, lfilter


def butter_bandpass(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(data, lowcut, highcut, fs, order=5):
    b, a = butter_bandpass(lowcut, highcut, fs, order=order)
    y = lfilter(b, a, data)
    return y


if __name__ == "__main__":
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.signal import freqz

    # Sample rate and desired cutoff frequencies (in Hz).
    fs = 5000.0
    lowcut = 500.0
    highcut = 1250.0

    # Plot the frequency response for a few different orders.
    plt.figure(1)
    plt.clf()
    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)

    plt.plot([0, 0.5 * fs], [np.sqrt(0.5), np.sqrt(0.5)],
             '--', label='sqrt(0.5)')
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain')
    plt.grid(True)
    plt.legend(loc='best')

    # Filter a noisy signal.
    T = 0.05
    nsamples = T * fs
    t = np.linspace(0, T, nsamples, endpoint=False)
    a = 0.02
    f0 = 600.0
    x = 0.1 * np.sin(2 * np.pi * 1.2 * np.sqrt(t))
    x += 0.01 * np.cos(2 * np.pi * 312 * t + 0.1)
    x += a * np.cos(2 * np.pi * f0 * t + .11)
    x += 0.03 * np.cos(2 * np.pi * 2000 * t)
    plt.figure(2)
    plt.clf()
    plt.plot(t, x, label='Noisy signal')

    y = butter_bandpass_filter(x, lowcut, highcut, fs, order=6)
    plt.plot(t, y, label='Filtered signal (%g Hz)' % f0)
    plt.xlabel('time (seconds)')
    plt.hlines([-a, a], 0, T, linestyles='--')
    plt.grid(True)
    plt.axis('tight')
    plt.legend(loc='upper left')

    plt.show() 

See if this helps your cause. 看看这是否有助于您的事业。 you can specify the desired frequencies here : 您可以在此处指定所需的频率:

# Sample rate and desired cutoff frequencies (in Hz).
        fs = 5000.0
        lowcut = 500.0
        highcut = 1250.0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM