【问题标题】:Lowpass filter with a time-varying cutoff frequency, with Python具有时变截止频率的低通滤波器,使用 Python
【发布时间】:2019-02-23 11:51:52
【问题描述】:

如何应用低通滤波器,截止频率线性变化(或曲线比线性更一般),例如10000hz 到 200hz 的时间,使用 numpy/scipy,可能没有其他库?

例子:

  • 在 00:00,000,低通截止 = 10000hz
  • 在 00:05,000,低通截止 = 5000hz
  • 在 00:09,000,低通截止 = 1000hz
  • 然后截止频率在 10 秒内保持在 1000hz,然后截止频率降低到 200hz

这里是如何做一个简单的 100hz 低通:

from scipy.io import wavfile
import numpy as np
from scipy.signal import butter, lfilter

sr, x = wavfile.read('test.wav')
b, a = butter(2, 100.0 / sr, btype='low')  # Butterworth
y = lfilter(b, a, x)
wavfile.write('out.wav', sr, np.asarray(y, dtype=np.int16))

但是如何使截止值变化?

注意:我已经阅读了Applying time-variant filter in Python,但答案相当复杂(通常适用于多种过滤器)。

【问题讨论】:

  • 永远是二阶黄油?
  • @StephenRauch 任何 IIR 或 FIR 都可以,只要截止 C(t) 可以沿时间 t 平滑变化(即 C(t) 不是分段常数函数)。
  • 听起来像是wavelet transforms 的工作,但我对他们的经验还不够,无法给出一个好的答案。不幸的是 pywaveletspywt 似乎是死标签。
  • 初始采样率是多少?
  • @DanielF 44.1 Khz 或 48 Khz 或 96 Khz 是有用的典型值。

标签: python numpy scipy signal-processing


【解决方案1】:

一种相对简单的方法是保持滤波器固定并改为调制信号时间。例如,如果信号时间快 10 倍,则 10KHz 低通在标准时间中的作用类似于 1KHz 低通。

为此,我们需要求解一个简单的 ODE

dy       1
--  =  ----
dt     f(y)

这里t 是调制时间y 实时,f 是时间y 所需的截止时间。

原型实现:

from __future__ import division
import numpy as np
from scipy import integrate, interpolate
from scipy.signal import butter, lfilter, spectrogram

slack_l, slack = 0.1, 1
cutoff = 50
L = 25

from scipy.io import wavfile
sr, x = wavfile.read('capriccio.wav')
x = x[:(L + slack) * sr, 0]
x = x

# sr = 44100
# x = np.random.normal(size=((L + slack) * sr,))

b, a = butter(2, 2 * cutoff / sr, btype='low')  # Butterworth

# cutoff function
def f(t):
    return (10000 - 1000 * np.clip(t, 0, 9) - 1000 * np.clip(t-19, 0, 0.8)) \
        / cutoff

# and its reciprocal
def fr(_, t):
    return cutoff / (10000 - 1000 * t.clip(0, 9) - 1000 * (t-19).clip(0, 0.8))

# modulate time
# calculate upper end of td first
tdmax = integrate.quad(f, 0, L + slack_l, points=[9, 19, 19.8])[0]
span = (0, tdmax)
t = np.arange(x.size) / sr
tdinfo = integrate.solve_ivp(fr, span, np.zeros((1,)),
                             t_eval=np.arange(0, span[-1], 1 / sr),
                             vectorized=True)
td = tdinfo.y.ravel()
# modulate signal
xd = interpolate.interp1d(t, x)(td)
# and linearly filter
yd = lfilter(b, a, xd)
# modulate signal back to linear time
y = interpolate.interp1d(td, yd)(t[:-sr*slack])

# check
import pylab
xa, ya, z = spectrogram(y, sr)
pylab.pcolor(ya, xa, z, vmax=2**8, cmap='nipy_spectral')
pylab.savefig('tst.png')

wavfile.write('capriccio_vandalized.wav', sr, y.astype(np.int16))

样本输出:

BWV 826 Capriccio 前 25 秒的频谱图,通过时间弯曲实现了时变低通滤波。

【讨论】:

  • 感谢您的回答。当我们在处理音频时,让信号运行速度提高 10 倍,然后再放慢它会造成严重破坏(这类似于 44.1 Khz => 4.1 Khz => 44.1 Khz 重新采样)。如果我们无论如何都应用 1000 Hz 低通,这可能不是什么大问题,但一般来说,这种方法不适用于更通用的时变滤波器(例如:你想和原来的问题一样,但是with highpass or passband! 那么这个解决方案的破坏性太大)
  • @Basj 三件事:(1)你的问题是关于低通的。 (2) 即使不是,我认为您的问题可以通过 (a) 将过滤器固定在最低位置来解决。然后只会放慢速度,即会发生信号的上采样或(b)在其他所有事情之前对信号进行上采样或(c)(a)和(b)的组合(等效于(a)但通过安全性将滤波器固定得更慢因子)(3)可以通过两次应用该方法来完成具有独立变化的截止的带通。
  • 你的言论是对的@PaulPanzer。尽管如此,在音频的上下文中,重采样是一件非常非常关键的事情(即使只完成一次,甚至对于接近 48 Khz => 44.1 khz 的采样率),创建混叠或其他伪影等,并且需要特定的工具(对此有很多深入的研究,现在最流行的解决方案之一是这个库:mega-nerd.com/SRC)。这就是为什么我宁愿不需要这种破坏性工具,而只是应用高通或低通的原因。
  • @Basj 我在这里可能错了,但你不能把 (2c) 中的安全系数设置得这么大,以至于你几乎不会丢失任何信息吗?混叠应该不是问题,因为最后我们要回到原来的时间网格。然后它变成了计算成本的问题。在某些时候,通过与稀疏的 total_no_samples x total_no_samples 矩阵相乘来强制它实际上可能会变得更便宜。
  • 这是您的代码应用于巴赫的 Golbberg 变奏的音频结果:gget.it/8sqvn/out.wav。如您所见,它相当失真(但未过滤!)...是的古尔德;)
【解决方案2】:

您可以使用 scipy.fftpack.fftfreq 和 scipy.fftpack.rfft 设置阈值

fft = scipy.fftpack.fft(sound)
freqs = scipy.fftpack.fftfreq(sound.size, time_step)

对于 time_step,我做了两倍的声音采样率

fft[(freqs < 200)] = 0

这会将所有小于 200 赫兹的频率设置为零

对于时变截止,我会拆分声音并应用过滤器。假设声音的采样率为 44100,则 5000hz 滤波器将从样本 220500(5 秒)开始

10ksound = sound[:220500]
10kfreq = scipy.fftpack.fftreq(10ksound.size, time_step)
10kfft = scipy.fftpack.fft(10ksound)
10kfft[(10kfreqs < 10000)] = 0

那么对于下一个过滤器:

5ksound = sound[220500:396900]
5kfreq = scipy.fftpack.fftreq(10ksound.size, time_step)
5kfft = scipy.fftpack.fft(10ksound)
5kfft[(5kfreqs < 5000)] = 0

编辑:要使其“滑动”或逐渐过滤而不是分段,您可以使“片段”更小,并将越来越大的频率阈值应用于相应的片段(5000 -> 5001 -> 5002)

【讨论】:

  • 感谢您的回答,几点说明:1)归零箱不是很好地进行过滤(但它有点工作),请参阅有关此的 DSP 主题,2)低通意味着我们 @987654321 @ 并删除较高的频率(您已经做了相反的事情,这很容易解决)。最大的问题:3)您的过滤器实际上是声音每个部分的 2 或 3 个标准过滤器。你有一个很容易的“分段常数截止”。困难在于“滑动”截止(见问题)。
  • 你是对的,在小块上执行此操作(例如 1024 个样本,即在 44.1Khz 采样率下约为 23ms)使其在“滑动案例”中工作:stackoverflow.com/a/52469397/1422096。重要的部分是关心初始条件参数。
猜你喜欢
  • 2012-09-02
  • 2014-08-29
  • 1970-01-01
  • 2012-08-19
  • 2017-09-22
  • 2013-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多