filter phase delays / Fidlib, MNE-Python

wjcroftwjcroft Mount Shasta, CA
edited March 2015 in Software
This discussion was created from comments split from: large millivolt data values / FbEEG Full Band EEG.

A well respected lib that is used by BioEra and BrainBay is,


Comments

  • Thank you William. I am comparing the scipy filters that python-mne uses to the fidlib filters used by BrainBay. The FIR filters used by scipy/mne claim a zero-phase response. How do IIR bessel- or butterworth - type filters from fidlib work out in terms of phase distortion?
  • edited March 2015
    What are you trying to do that is so sensitive to phase distortion?  Doing a bessel or butterworth IIR high-pass filter will have phase distortion (ie, different frequency components will pass through with different latencies) but that's not really a problem for many applications.

    To get no phase distortion, the typical alternative is to do a linear phase (ie, constant latency) FIR filter.  Being an FIR filter, though, it will need to be much longer (ie, have many more coefficients) that an IIR filter with the same roll-off.

    If a linear-phase filter is unacceptable, and you really want to do azero-phase filter, be aware that it is non-causal, so it can only be applied through post-processing and not applied on the fly.  If you're accepting that you are only going to be working via post-processing, you can actually get a zero-phase filter with either a simple IIR or FIR filter...you run the filter once normally in the forward direction, then you flip the filtered signal around and run through the filter again...this'll get you zero phase lag and zero latency at all frequencies.   Or, you can get a zero-phase filter via an FFT, though look out for circular wrap-around effects on the FFT, if you don't window the data or zero-pad it. 

    Even with zero phase lag/distortion, the filters will still have a non-delta impulse response (ie, they will ring).  A zero-phase filter pre-ring and post-ring, whereas a strictly causal filter (such as traditional single-pass IIR) will just post-ring.  Either way, the ringing is often what messes up people's processing approaches, not the phase lag.  The ringing is hard to avoid.  There is not perfect solution.

    So, if I may offer to you an recommendation, it is to try one of the simple, already-implemented IIR filters and see just if it works for what you need.  If it does, you're golden.  If not, then you'll get to dig into something fancier.

    Of course, if your goal is to have an excuse to learn one of the fancier techniques (which is an awesome goal), then certainly dive right in.  I fully support doing something hard just because you want to.  Rock it!

    Chip 


  • edited March 2015
    I looked into python-mne...that's cool!  I hadn't seen it before.  It definitely looks to me like a post-processing tool, hence it's ability to do zero-phase filtering.

    They give the option of two types of zero-phase filtering, IIR or FFT...


    You'll note that they say that their "IIR" method uses "forward-backward" filtering, which is what I mentioned in my post above...run a basic IIR forward, then run it through the filter a second time backward...this cancels out any of the phase lag/distortion.

    Then, not that they say that thier "FFT" is overlap-add FIR filtering, which unfortunately no sense unless they actually mean overlap-add FFT processing and "FIR" was just a typo.  Overlap-add is definitely how one uses FFT for filtering, so that's what they must mean.

    Regardless, both of these zero-phase approaches are very sensible, but only possible because they're focused on post-processing, not real-time on-the-fly processing.

    It's definitely cool to see what others are doing.  Thanks for pointing to python-mne.

    Chip
  • Thanks all. This is a topic I am very interested in understanding better. It sounds to me like fidlib's high performance filters will be great for data visualization, in terms of zeroing the DC offset and perhaps eliminating the line noise. From an analytical perspective, perhaps I will try looking for signal through the noise without applying filters. Or trying a variety filters to see what works best. 

    @wjcroft, Thanks much for sharing the fidlib library. I'll ask you the same questions I asked Jim Peters on a whim...

    I am writing go bindings to the fidlib c code for use in an EEG data visualization project:


    How are fid_run_new() and fid_run_newbuf() properly used? My reading of the documentation leads me to believe they should be called once per channel to get a buffer and pointer to a filter function. From then on out the call looks like:

    out = funcp(fbuf, in);

    Why does each channel require it's own fbuf and funcp? Is state maintained in the run struct to filter the signal correctly? I do not know much about filter algorithms, but I imagine to do filtering we must know more than the sample rate and one data point in the signal. However, it seems the function pointer created by fid_run_new takes a single data point as an argument. Does fidlib maintain some history of data points that have come before in order to filter the present data point? If so, how?

    Thanks again,
    Kevin
  • wjcroftwjcroft Mount Shasta, CA
    edited March 2015
    Kevin, hi.

    I'm just going off Jim's documentation below. If you want to see a real example in BrainBay, see ob_filter.cpp in this folder.

    Example given in http://uazu.net/fidlib/fidlib.txt ,

    Here is an illustration of typical code:

      FidRun *run;
      FidFunc *funcp;
      void *fbuf1, *fbuf2;
     
      run= fid_run_new(filt, &funcp);
      fbuf1= fid_run_newbuf(run);
      fbuf2= fid_run_newbuf(run);
      while (...) {
         out_1= funcp(fbuf1, in_1);
         out_2= funcp(fbuf2, in_2);
         if (restart_required) fid_run_zapbuf(fbuf1);
         ...
      }
      fid_run_freebuf(fbuf2);
      fid_run_freebuf(fbuf1);
      fid_run_free(run);

    fid_run_new() is just called ONCE, with your parsed filter (from ascii filter spec) as argument. It returns a FidFunc function pointer and a FidRun structure pointer. This is not called once per channel, but only once per filter type.

    Then for each channel, you setup a new fid_run_newbuf(). This takes the FidRun pointer as arg and returns a void* (opaque data type) for the buffer pointer.

    Then as you process each sample point for each channel, you call that FidFunc function pointer with the buffer pointer and the sample.

    Each channel needs it's own buffer because that is how digital filters work, they need a history of past activity, basically. It's a feedback process. State is not maintained in the FidRun struct, but instead in the buffers.

    > However, it seems the function pointer created by fid_run_new takes a single data point as an argument.

    [revision:] sorry, I misunderstood your original comment, various C compilers have differing requirements for strict argument typing on function pointers. I think you just have to work with the compiler requirements you have. If that needs fiddling some declarations, do that.

    William

  • I see. This becomes more clear, knowing the buffer holds the state of the filter. Thus, one buffer per signal.
Sign In or Register to comment.