是否有人尝试在Java (或至少在C ++中)实现matlab的filtfilt()
函数?如果你们有一个算法,这将是非常有帮助的。
答案 0 :(得分:5)
Here是我在MATLAB中实现的filtfilt
算法的C ++实现。希望这会对你有所帮助。
答案 1 :(得分:3)
好的,我知道这个问题很古老,但是也许我会对想问
(jQuery('#loginModal') as ModalElement).modal('show');
实际工作的其他人有所帮助。
尽管从文档中可以明显看出filtfilt
进行了正向(即零相位)过滤,但是对我来说,它不是如何处理 padding 和初始条件。
由于我在此处(其他地方)找不到其他答案,没有关于filtfilt
的这些实现细节的足够信息,因此我实现了filtfilt
'的简化版本s Python
,基于其来源和文档(因此,不是scipy.signal.filtfilt
也不是Java
,而是C++
)。我相信Python
版本works the same的方式与scipy
一样。
为简单起见,以下代码专门针对第二阶IIR filter编写,并且假定系数矢量Matlab
和a
已知(例如,从{{1 }}或calculated by hand)。
它使用b
长度scipy.signal.butter
的填充来匹配filtfilt
的默认行为,该填充在正向传递之前应用。可以使用odd
(docs)中的方法找到初始状态。
免责声明:此代码仅用于提供对3 * max(len(a), len(b))
某些实现细节的深入了解,因此目标是清晰而不是计算效率/性能。 scipy.signal.lfilter_zi
的实现要快得多(例如,根据我的系统上进行的快速而肮脏的filtfilt
测试,速度提高了100倍。
scipy.signal.filtfilt
请注意,此实现不需要不需要timeit
。此外,通过写出import numpy
def custom_filter(b, a, x):
"""
Filter implemented using state-space representation.
Assume a filter with second order difference equation (assuming a[0]=1):
y[n] = b[0]*x[n] + b[1]*x[n-1] + b[2]*x[n-2] + ...
- a[1]*y[n-1] - a[2]*y[n-2]
"""
# State space representation (transposed direct form II)
A = numpy.array([[-a[1], 1], [-a[2], 0]])
B = numpy.array([b[1] - b[0] * a[1], b[2] - b[0] * a[2]])
C = numpy.array([1.0, 0.0])
D = b[0]
# Determine initial state (solve zi = A*zi + B, see scipy.signal.lfilter_zi)
zi = numpy.linalg.solve(numpy.eye(2) - A, B)
# Scale the initial state vector zi by the first input value
z = zi * x[0]
# Apply filter
y = numpy.zeros(numpy.shape(x))
for n in range(len(x)):
# Determine n-th output value (note this simplifies to y[n] = z[0] + b[0]*x[n])
y[n] = numpy.dot(C, z) + D * x[n]
# Determine next state (i.e. z[n+1])
z = numpy.dot(A, z) + B * x[n]
return y
def custom_filtfilt(b, a, x):
# Apply 'odd' padding to input signal
padding_length = 3 * max(len(a), len(b)) # the scipy.signal.filtfilt default
x_forward = numpy.concatenate((
[2 * x[0] - xi for xi in x[padding_length:0:-1]],
x,
[2 * x[-1] - xi for xi in x[-2:-padding_length-2:-1]]))
# Filter forward
y_forward = custom_filter(b, a, x_forward)
# Filter backward
x_backward = y_forward[::-1] # reverse
y_backward = custom_filter(b, a, x_backward)
# Remove padding and reverse
return y_backward[-padding_length-1:padding_length-1:-1]
的解决方案并使用列表而不是numpy数组,可以轻松地使其适应于纯Python,甚至没有scipy
。这甚至带来巨大的性能优势,因为在python循环中访问单个numpy数组元素比访问列表元素要慢得多。
此处的过滤器本身是通过简单的numpy
循环实现的。它使用状态空间表示形式,因为无论如何它都用于确定初始条件(请参见zi
)。我相信线性滤波器的实际Python
实现(即scipy.signal.lfilter_zi
)在scipy
中会执行类似的操作,如here所示(感谢this answer )。
以下代码提供了(非常基本的)检查scipy.signal.sigtools._linear_filter
输出和C
输出是否相等的代码:
scipy
此基本比较得出的数字如下所示,对于这种特定情况(机器精度,我想是吗?),建议等于至少14位小数: