• Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • email

"""
Cookbook / FiltFilt : http://www.scipy.org/Cookbook/FiltFilt
 
This sample code implements a zero phase delay filter that processes
the signal in the forward and backward direction removing the phase
delay. The order of the filter is the double of the original filter
order. The function also computes the initial filter parameters in
order to provide a more stable response (via lfilter_zi). The
following code has been tested with Python 2.4.4 and Scipy 0.5.1.
"""
 
from numpy import (vstack, hstack, eye, ones, zeros, linalg,
                   newaxis, r_, flipud, convolve, matrix, array)
 
from scipy.signal import lfilter
 
def lfilter_zi(b,a):
 
    #compute the zi state from the filter parameters. see [Gust96].
 
    #Based on: [Gust96] Fredrik Gustafsson, Determining the initial
    # states in forward-backward filtering, IEEE Transactions on
    # Signal Processing, pp. 988--992, April 1996, Volume 44, Issue 4
 
    n=max(len(a),len(b))
 
    zin = (  eye(n-1) - hstack( (-a[1:n,newaxis],
                                 vstack((eye(n-2), zeros(n-2))))))
 
    zid =  b[1:n] - a[1:n]*b[0]
 
    zi_matrix=linalg.inv(zin)*(matrix(zid).transpose())
    zi_return=[]
 
    #convert the result into a regular array (not a matrix)
    for i in range(len(zi_matrix)):
      zi_return.append(float(zi_matrix[i][0]))
 
    return array(zi_return)
 
 
def filtfilt(b,a,x):
    #For now only accepting 1d arrays
    ntaps=max(len(a),len(b))
    edge=ntaps*3
 
    if x.ndim != 1:
        raise ValueError("Filiflit is only accepting 1 dimension arrays.")
 
    # x must be bigger than edge
    if x.size < edge:
        e="Input vector needs to be bigger than 3 * max(len(a),len(b)."
        raise ValueError(e)
 
    if len(a) < ntaps:
        a=r_[a,zeros(len(b)-len(a))]
 
    if len(b) < ntaps:
        b=r_[b,zeros(len(a)-len(b))]
 
    zi=lfilter_zi(b,a)
 
    #Grow the signal to have edges for stabilizing
    #the filter with inverted replicas of the signal
    s=r_[2*x[0]-x[edge:1:-1],x,2*x[-1]-x[-1:-edge:-1]]
    #in the case of one go we only need one of the extrems
    # both are needed for filtfilt
 
    (y,zf)=lfilter(b,a,s,-1,zi*s[0])
 
    (y,zf)=lfilter(b,a,flipud(y),-1,zi*y[-1])
 
    return flipud(y[edge-1:-edge+1])
 
 
 
if __name__=='__main__':
 
    import scipy.signal as signal
    from scipy import sin, arange, pi, randn
 
    from pylab import plot, legend, show, hold
 
    t = arange(-1,1,.01)
    x = sin(2*pi*t*.5+2)
 
    # add some noise to the signa
    xn = x+randn(len(t))*0.05
 
    # parameters for a butterworth lowpass filter
    [b,a] = signal.butter(3,0.05)
 
    z = lfilter(b, a, xn)
    y = filtfilt(b, a, xn)
 
    plot(x, 'c', label='original')
    plot(xn, 'k', label='noisy signal')
    plot(z, 'r', label='lfilter - butter 3 order')
    plot(y, 'g', label='filtfilt - butter 3 order')
    legend(loc='best')
    show()