Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation about processing steps #309

Merged
merged 3 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/_static/resources/epochs-processing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/_static/resources/stream-processing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/changes/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Version 1.5
- Add tutorial about :class:`~mne.Annotations` replayed on a :class:`~mne_lsl.player.PlayerLSL` (:pr:`305` by `Mathieu Scheltienne`_)
- Expose the abstract :class:`~mne_lsl.stream.BaseStream` object to create custom stream objects relying on different communication protocols (:pr:`302` by `Mathieu Scheltienne`_)
- Add argument ``source_id`` to :class:`~mne_lsl.player.PlayerLSL` to specify the source_id of the stream (:pr:`305` by `Mathieu Scheltienne`_)
- Add documentation about the processing applied to a :class:`~mne_lsl.stream.StreamLSL` and :class:`~mne_lsl.stream.EpochsStream` object (:pr:`309` by `Mathieu Scheltienne`_)

Infrastructure
--------------
Expand Down
3 changes: 2 additions & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fetched from the `liblsl release page <lsl lib release_>`_ if possible.

.. tab-item:: MNE installers

As of MNE-Python 1.6, ``mne-lsl`` is distributed in the
As of `MNE-Python <mne stable_>`_ 1.6, ``mne-lsl`` is distributed in the
`MNE standalone installers <mne installers_>`_.

The installers create a conda environment with the entire MNE-ecosystem
Expand Down Expand Up @@ -66,6 +66,7 @@ The development of ``MNE-LSL`` is supported by the
resources/install.rst
api/index.rst
resources/command_line.rst
resources/implementations.rst
generated/tutorials/index.rst
generated/examples/index.rst
changes/index.rst
76 changes: 76 additions & 0 deletions doc/resources/implementations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.. include:: ../links.inc

Implementation details
======================

Processing
----------

The :class:`~mne_lsl.stream.StreamLSL` and :class:`~mne_lsl.stream.EpochsStream` object
support processing in real-time of their internal buffers. The processing is done
sequentially and this page presents the available processing and their application
order.

StreamLSL
~~~~~~~~~

Processing is applied to new samples available in the
:class:`~mne_lsl.lsl.StreamInlet` before rolling the buffer of the
:class:`~mne_lsl.stream.StreamLSL` object and adding those new processed samples to it.
The processing is defined in the private ``_acquire`` method of the class
:class:`~mne_lsl.stream.StreamLSL`, method called by the background acquisition thread
(automatic acquisition) or by the method :meth:`mne_lsl.stream.StreamLSL.acquire`
(manual acquisition).

1. Add channels that were added with
:meth:`mne_lsl.stream.StreamLSL.add_reference_channels`. The channels are added as an
array of zeros at the end of the buffer along the channel axis.
2. Apply the rereferencing schema requested with
:meth:`mne_lsl.stream.StreamLSL.set_eeg_reference`.
3. Apply filters added with :meth:`mne_lsl.stream.StreamLSL.filter` and
:meth:`mne_lsl.stream.StreamLSL.notch_filter`. The filters are applied one at a time,
in the order they were added.

.. image:: ../_static/resources/stream-processing.svg
:align: right
:alt: Stream processing flowchart
:width: 100%

EpochsStream
~~~~~~~~~~~~

Processing is applied to new events and samples available to a
:class:`~mne_lsl.stream.EpochsStream` before rolling the buffer of the
:class:`~mne_lsl.stream.EpochsStream` object and adding new epochs to it.
The processing is defined in the private ``_acquire`` method of the class
:class:`~mne_lsl.stream.EpochsStream`, method called by the background acquisition
thread (automatic acquisition) or by the method
:meth:`mne_lsl.stream.EpochsStream.acquire` (manual acquisition); and in the private
``_process_data`` function which operates on the newly acquired data array of shape
``(n_epochs, n_samples, n_channels)``.

.. note::

`MNE-Python <mne stable_>`_ offers similar processing to a :class:`~mne.io.Raw` and
:class:`~mne.Epochs` object. However, ``mne-lsl`` differs in this regard by offering
some processing at the :class:`~mne_lsl.stream.StreamLSL` level and some at the
:class:`~mne_lsl.stream.EpochsStream` level. For instance, filters are applied
to a :class:`~mne_lsl.stream.StreamLSL` object while baseline correction is
applied to a :class:`~mne_lsl.stream.EpochsStream` object.

1. Select which events are new since the last acquisition and which events are retained.
For instance, an event too close to the end of the buffer is discarded for now since
it is not possible to cut an entire epoch from it. This event will be acquired and
added to the buffer as soon as it will be possible to cut an entire epoch from it.
2. Process the newly acquired data array of shape ``(n_epochs, n_samples, n_channels)``.

a. Apply PTP and flatness rejection defined by the arguments ``reject``, ``flat``,
``reject_tmin`` and ``reject_tmax``.
b. Apply baseline correction defined by the arguments ``baseline``, ``tmin`` and
``tmax``.
c. Apply detrending defined by the argument ``detrend``.

.. image:: ../_static/resources/epochs-processing.svg
:align: right
:alt: Stream processing flowchart
:width: 100%
2 changes: 1 addition & 1 deletion doc/resources/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ downloaded from the release page on compatible platforms.

.. tab-item:: MNE installers

As of MNE-Python 1.6, ``mne-lsl`` is distributed in the
As of `MNE-Python <mne stable_>`_ 1.6, ``mne-lsl`` is distributed in the
`MNE standalone installers <mne installers_>`_.

The installers create a conda environment with the entire MNE-ecosystem
Expand Down
8 changes: 4 additions & 4 deletions doc/resources/pylsl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Differences with pylsl
======================

Faster chunk pull
~~~~~~~~~~~~~~~~~
-----------------

Arguably the most important difference, pulling a chunk of numerical data with
:meth:`~mne_lsl.lsl.StreamInlet.pull_chunk` is much faster than with its
Expand Down Expand Up @@ -62,7 +62,7 @@ Note that this performance improvement is absent for ``string`` based streams. F
:issue:`225` for more information.

Convenience methods
~~~~~~~~~~~~~~~~~~~
-------------------

A :class:`~mne_lsl.lsl.StreamInfo` has several convenience methods to retrieve and set
channel attributes: names, types, units.
Expand All @@ -84,7 +84,7 @@ be set or retrieved directly from a :class:`~mne.Info` object with
:meth:`~mne_lsl.lsl.StreamInfo.get_channel_info`.

Improve arguments
~~~~~~~~~~~~~~~~~
-----------------

The arguments of a :class:`~mne_lsl.lsl.StreamInfo`, :class:`~mne_lsl.lsl.StreamInlet`,
:class:`~mne_lsl.lsl.StreamOutlet` support a wider variety of types. For instance:
Expand All @@ -98,7 +98,7 @@ Overall, the arguments are checked in ``mne_lsl.lsl``. Any type or value mistake
raise an helpful error message.

Unique resolve function
~~~~~~~~~~~~~~~~~~~~~~~
-----------------------

`pylsl <lsl python_>`_ has several stream resolving functions:

Expand Down
20 changes: 20 additions & 0 deletions mne_lsl/stream/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ def add_reference_channels(
-------
stream : instance of ``Stream``
The stream instance modified in-place.

Notes
-----
Read about the :ref:`processing applied to the underlying
buffer <resources/implementations:StreamLSL>`.
"""
self._check_connected_and_regular_sampling("add_reference_channels()")
self._check_not_epoched("add_reference_channels()")
Expand Down Expand Up @@ -481,6 +486,11 @@ def filter(
-------
stream : instance of ``Stream``
The stream instance modified in-place.

Notes
-----
Read about the :ref:`processing applied to the underlying
buffer <resources/implementations:StreamLSL>`.
"""
self._check_connected_and_regular_sampling("filter()")
if len(self._epochs) != 0:
Expand Down Expand Up @@ -664,6 +674,11 @@ def notch_filter(
-------
stream : instance of ``Stream``
The stream instance modified in-place.

Notes
-----
Read about the :ref:`processing applied to the underlying
buffer <resources/implementations:StreamLSL>`.
"""
self._check_connected_and_regular_sampling("notch_filter()")
if len(self._epochs) != 0:
Expand Down Expand Up @@ -904,6 +919,11 @@ def set_eeg_reference(
-------
stream : instance of ``Stream``
The stream instance modified in-place.

Notes
-----
Read about the :ref:`processing applied to the underlying
buffer <resources/implementations:StreamLSL>`.
"""
self._check_connected_and_regular_sampling("set_eeg_reference()")
self._check_not_epoched("set_eeg_reference()")
Expand Down
3 changes: 3 additions & 0 deletions mne_lsl/stream/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ class EpochsStream:

In the 2 last cases where ``event_stream`` is provided, all ``'stim'`` channels
in the connected ``stream`` are ignored.

Read about the :ref:`processing applied to the underlying
buffer <resources/implementations:EpochsStream>`.
"""

def __init__(
Expand Down