Skip to content

Commit

Permalink
MRG: Add nirx file reader (#6674)
Browse files Browse the repository at this point in the history
* Initial add of nirx reader files

* Initial add of test files

* Add test call to nirx reader. Ensure required files are present

* Add reader for header file

* Add _read_segment_file for nirx

* Ensure nirx files adhere to style guidlines

* Check nirx file format version is supported

* Extract wavelength information from file. Tidy info variable names

* Add `subject_info` field based on .inf file

* Point tests to newly added nirx file

* Fix assert bug raised by larsoner

* Add new lines where indicated by larsoner

#6674 (review)

* Add read_raw_nirx to python_reference.rst

* larsoner suffestion accepted. Update mne/io/nirx/nirx.py

Co-Authored-By: Eric Larson <larson.eric.d@gmail.com>

* larsoner suffestion accepted. Update mne/io/nirx/nirx.py

Co-Authored-By: Eric Larson <larson.eric.d@gmail.com>

* larsoner suffestion accepted. Update mne/io/nirx/nirx.py

Co-Authored-By: Eric Larson <larson.eric.d@gmail.com>

* larsoner suffestion accepted. Update mne/io/nirx/nirx.py

Co-Authored-By: Eric Larson <larson.eric.d@gmail.com>

* Nest pandas import, use meaningful channel names

* Import FIFF. Change HDR reader.

Couldnt get one liner from larsoner to work. Split to two lines.
Will come back to this once CI is green

* Add montage reader. Pydocstyle fix

* Fix typo in comments

* Tell nirx tests that pandas is required

* Fix nirx reader typo

* Add RawNIRX to numpydoc_xref_ignore

* Fix nirx reading bug that dropped first sample. Check names

* Ensure channel interleaving matches channel names.

Tested results match MATLAB by checking std of channels is same
in both programs

* Store nirx channel, source, and detector locations

* Fixed position bug for nirx channels

* Remove code repetition in nirx reader

* Dont import configparser as cp in nirx reader

* Proper check and assert for nirx file format

* Scale locations to mm. Add test for S-D distances against NIRSite values

* Add function to determine nirx short channels

* Extract nirx triggers. Commented out until I read where events live

* Add annotations to nirx reader

* Tidy comments and remove unused code

* Add two new fNIRS types `fnirs_raw` and `fnirs_od`

fNIRS raw is for measurements directly from machine (V)
fNIRS od is for measurements converted to optical density (unitless)
Then you convert to hbo/hbr

* Specify types for arrays in nirx reader

* Changes required to raw.plot nirx files

* Use fNIRS channel type, create three fnirs coil types

#6674 (comment)

* Add fnirs chroma channel

* Ensure fnirs_raw type works in picks

* Fix picks for other added types

* Add new FIFF to tag ignore, flake fixes

* Add fnirs to cov.py

* Add od and chroma to plotting

* Allow raw.plot_psd() for nirx files

* Remove chroma based on discussion with larsoner

#6674 (comment)

* Use new fiff release

* Remove old comments

* Add documentation for NIRS

* Add tests for picking fnirs_raw and fnirs_od

* Fix type on fNIRS module

* Add nirx io tutorial

* Add nirx reader to changes

* Update tutorials/io/plot_30_reading_fnirs_data.py

Co-Authored-By: Alexandre Gramfort <alexandre.gramfort@m4x.org>

* ENH: Use NumPy instead of Pandas
  • Loading branch information
rob-luke authored and larsoner committed Sep 25, 2019
1 parent db30fc4 commit 8861432
Show file tree
Hide file tree
Showing 22 changed files with 518 additions and 38 deletions.
2 changes: 2 additions & 0 deletions doc/_includes/data_formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ EEG :ref:`eXimia <import-nxe>` .nxe :func:`mn
EEG :ref:`General data format <import-gdf>` .gdf :func:`mne.io.read_raw_gdf`

EEG :ref:`Nicolet <import-nicolet>` .data :func:`mne.io.read_raw_nicolet`

NIRS :ref:`NIRx <import-nirx>` directory :func:`mne.io.read_raw_nirx`
============ ============================================ ========= ===================================

More details are provided in the tutorials in the :ref:`tut-data-formats`
Expand Down
3 changes: 3 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
Current (0.20.dev0)
-------------------

- Add reader for NIRx data in :func:`mne.io.read_raw_nirx` by `Robert Luke`_


Changelog
~~~~~~~~~

Expand Down
3 changes: 3 additions & 0 deletions doc/changes/names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,6 @@

.. _Abram Hindle: http://softwareprocess.es

.. _Robert Luke: https://github.com/rob-luke


2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ def reset_warnings(gallery_conf, fname):
'n_segments',
# Undocumented (on purpose)
'RawKIT', 'RawEximia', 'RawEGI', 'RawEEGLAB', 'RawEDF', 'RawCTF', 'RawBTi',
'RawBrainVision', 'RawCurry',
'RawBrainVision', 'RawCurry', 'RawNIRX',
# sklearn subclasses
'mapping', 'to', 'any',
# unlinkable
Expand Down
1 change: 1 addition & 0 deletions doc/python_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Reading raw data
read_raw_gdf
read_raw_kit
read_raw_nicolet
read_raw_nirx
read_raw_eeglab
read_raw_brainvision
read_raw_egi
Expand Down
8 changes: 8 additions & 0 deletions mne/channels/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ def compensation_grade(self):
'syst': FIFF.FIFFV_SYST_CH,
'bio': FIFF.FIFFV_BIO_CH,
'ecog': FIFF.FIFFV_ECOG_CH,
'fnirs_raw': FIFF.FIFFV_FNIRS_CH,
'fnirs_od': FIFF.FIFFV_FNIRS_CH,
'hbo': FIFF.FIFFV_FNIRS_CH,
'hbr': FIFF.FIFFV_FNIRS_CH}
_human2unit = {'ecg': FIFF.FIFF_UNIT_V,
Expand All @@ -197,6 +199,8 @@ def compensation_grade(self):
'syst': FIFF.FIFF_UNIT_NONE,
'bio': FIFF.FIFF_UNIT_V,
'ecog': FIFF.FIFF_UNIT_V,
'fnirs_raw': FIFF.FIFF_UNIT_V,
'fnirs_od': FIFF.FIFF_UNIT_NONE,
'hbo': FIFF.FIFF_UNIT_MOL,
'hbr': FIFF.FIFF_UNIT_MOL}
_unit2human = {FIFF.FIFF_UNIT_V: 'V',
Expand Down Expand Up @@ -423,6 +427,10 @@ def set_channel_types(self, mapping):
coil_type = FIFF.FIFFV_COIL_FNIRS_HBO
elif ch_type == 'hbr':
coil_type = FIFF.FIFFV_COIL_FNIRS_HBR
elif ch_type == 'fnirs_raw':
coil_type = FIFF.FIFFV_COIL_FNIRS_RAW
elif ch_type == 'fnirs_od':
coil_type = FIFF.FIFFV_COIL_FNIRS_OD
else:
coil_type = FIFF.FIFFV_COIL_NONE
self.info['chs'][c_ind]['coil_type'] = coil_type
Expand Down
11 changes: 9 additions & 2 deletions mne/cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -1136,8 +1136,8 @@ class _RegCovariance(BaseEstimator):
"""Aux class."""

def __init__(self, info, grad=0.1, mag=0.1, eeg=0.1, seeg=0.1, ecog=0.1,
hbo=0.1, hbr=0.1, store_precision=False,
assume_centered=False):
hbo=0.1, hbr=0.1, fnirs_raw=0.1, fnirs_od=0.1,
store_precision=False, assume_centered=False):
self.info = info
# For sklearn compat, these cannot (easily?) be combined into
# a single dictionary
Expand All @@ -1148,6 +1148,8 @@ def __init__(self, info, grad=0.1, mag=0.1, eeg=0.1, seeg=0.1, ecog=0.1,
self.ecog = ecog
self.hbo = hbo
self.hbr = hbr
self.fnirs_raw = fnirs_raw
self.fnirs_od = fnirs_od
self.store_precision = store_precision
self.assume_centered = assume_centered

Expand Down Expand Up @@ -1430,6 +1432,7 @@ def _smart_eigh(C, info, rank, scalings=None, projs=None,
@verbose
def regularize(cov, info, mag=0.1, grad=0.1, eeg=0.1, exclude='bads',
proj=True, seeg=0.1, ecog=0.1, hbo=0.1, hbr=0.1,
fnirs_raw=0.1, fnirs_od=0.1,
rank=None, scalings=None, verbose=None):
"""Regularize noise covariance matrix.
Expand Down Expand Up @@ -1470,6 +1473,10 @@ def regularize(cov, info, mag=0.1, grad=0.1, eeg=0.1, exclude='bads',
Regularization factor for HBO signals.
hbr : float (default 0.1)
Regularization factor for HBR signals.
fnirs_raw : float (default 0.1)
Regularization factor for fNIRS raw signals.
fnirs_od : float (default 0.1)
Regularization factor for fNIRS optical density signals.
%(rank_None)s
.. versionadded:: 0.17
Expand Down
11 changes: 6 additions & 5 deletions mne/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@
color=dict(mag='darkblue', grad='b', eeg='k', eog='k', ecg='m', emg='k',
ref_meg='steelblue', misc='k', stim='k', resp='k', chpi='k',
exci='k', ias='k', syst='k', seeg='saddlebrown', dipole='k',
gof='k', bio='k', ecog='k', hbo='darkblue', hbr='b'),
gof='k', bio='k', ecog='k', hbo='darkblue', hbr='b',
fnirs_raw='k'),
units=dict(mag='fT', grad='fT/cm', eeg='uV', eog='uV', ecg='uV', emg='uV',
misc='AU', seeg='mV', dipole='nAm', gof='GOF', bio='uV',
ecog='uV', hbo='uM', hbr='uM', ref_meg='fT'),
ecog='uV', hbo='uM', hbr='uM', ref_meg='fT', fnirs_raw='V'),
# scalings for the units
scalings=dict(mag=1e15, grad=1e13, eeg=1e6, eog=1e6, emg=1e6, ecg=1e6,
misc=1.0, seeg=1e3, dipole=1e9, gof=1.0, bio=1e6, ecog=1e6,
hbo=1e6, hbr=1e6, ref_meg=1e15),
hbo=1e6, hbr=1e6, ref_meg=1e15, fnirs_raw=1),
# rough guess for a good plot
scalings_plot_raw=dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6,
ecg=5e-4, emg=1e-3, ref_meg=1e-12, misc='auto',
stim=1, resp=1, chpi=1e-4, exci=1, ias=1, syst=1,
seeg=1e-4, bio=1e-6, ecog=1e-4, hbo=10e-6,
hbr=10e-6, whitened=10.),
hbr=10e-6, whitened=10., fnirs_raw=2e-2),
scalings_cov_rank=dict(mag=1e12, grad=1e11, eeg=1e5, # ~100x scalings
seeg=1e1, ecog=1e4, hbo=1e4, hbr=1e4),
ylim=dict(mag=(-600., 600.), grad=(-200., 200.), eeg=(-200., 200.),
Expand All @@ -33,7 +34,7 @@
titles=dict(mag='Magnetometers', grad='Gradiometers', eeg='EEG', eog='EOG',
ecg='ECG', emg='EMG', misc='misc', seeg='sEEG', bio='BIO',
dipole='Dipole', ecog='ECoG', hbo='Oxyhemoglobin',
ref_meg='Reference Magnetometers',
ref_meg='Reference Magnetometers', fnirs_raw='fNIRS',
hbr='Deoxyhemoglobin', gof='Goodness of fit'),
mask_params=dict(marker='o',
markerfacecolor='w',
Expand Down
4 changes: 2 additions & 2 deletions mne/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ def get_peak(self, ch_type=None, tmin=None, tmax=None,
.. versionadded:: 0.16
""" # noqa: E501
supported = ('mag', 'grad', 'eeg', 'seeg', 'ecog', 'misc', 'hbo',
'hbr', 'None')
'hbr', 'None', 'fnirs_raw', 'fnirs_od')
data_picks = _pick_data_channels(self.info, with_ref_meg=False)
types_used = {channel_type(self.info, idx) for idx in data_picks}

Expand Down Expand Up @@ -602,7 +602,7 @@ def get_peak(self, ch_type=None, tmin=None, tmax=None,
seeg = True
elif ch_type == 'ecog':
ecog = True
elif ch_type in ('hbo', 'hbr'):
elif ch_type in ('hbo', 'hbr', 'fnirs_raw', 'fnirs_od'):
fnirs = ch_type

if ch_type is not None:
Expand Down
4 changes: 3 additions & 1 deletion mne/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from . import fiff
from . import kit
from . import nicolet
from . import nirx
from . import eeglab
from . import pick

Expand All @@ -43,6 +44,7 @@
from .artemis123 import read_raw_artemis123
from .eeglab import read_raw_eeglab, read_epochs_eeglab
from .eximia import read_raw_eximia
from .nirx import read_raw_nirx
from .fieldtrip import (read_raw_fieldtrip, read_epochs_fieldtrip,
read_evoked_fieldtrip)

Expand All @@ -66,5 +68,5 @@
read_raw_curry, read_raw_edf, read_raw_eeglab, read_raw_egi,
read_raw_eximia, read_raw_fieldtrip, read_raw_fif, read_raw_gdf,
read_raw_kit, read_raw_nicolet, set_bipolar_reference, set_eeg_reference,
show_fiff, write_fiducials, write_info,
show_fiff, write_fiducials, write_info, read_raw_nirx,
]
4 changes: 4 additions & 0 deletions mne/io/meas_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
seeg=(FIFF.FIFFV_SEEG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
bio=(FIFF.FIFFV_BIO_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
ecog=(FIFF.FIFFV_ECOG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
fnirs_raw=(FIFF.FIFFV_FNIRS_CH, FIFF.FIFFV_COIL_FNIRS_RAW,
FIFF.FIFF_UNIT_V),
fnirs_od=(FIFF.FIFFV_FNIRS_CH, FIFF.FIFFV_COIL_FNIRS_OD,
FIFF.FIFF_UNIT_NONE),
hbo=(FIFF.FIFFV_FNIRS_CH, FIFF.FIFFV_COIL_FNIRS_HBO, FIFF.FIFF_UNIT_MOL),
hbr=(FIFF.FIFFV_FNIRS_CH, FIFF.FIFFV_COIL_FNIRS_HBR, FIFF.FIFF_UNIT_MOL)
)
Expand Down
7 changes: 7 additions & 0 deletions mne/io/nirx/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""fNIRS module for conversion to FIF."""

# Author: Robert Luke <mail@robertluke.net>
#
# License: BSD (3-clause)

from .nirx import read_raw_nirx
Loading

0 comments on commit 8861432

Please sign in to comment.