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

DNM: Add support for fNIRS data #365

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
39 changes: 35 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
automatically.
"""

ch_types: Iterable[Literal['meg', 'mag', 'grad', 'eeg']] = []
ch_types: Iterable[Literal['meg', 'mag', 'grad', 'eeg', 'nirs']] = []
"""
The channel types to consider.

Expand All @@ -178,7 +178,7 @@
```
"""

data_type: Optional[Literal['meg', 'eeg']] = None
data_type: Optional[Literal['meg', 'eeg', 'nirs']] = None
"""
The BIDS data type.

Expand Down Expand Up @@ -362,6 +362,32 @@
```
"""

###############################################################################
# NIRS PROCESSING
# ---------------

nirs_only_long_channels: Optional[bool] = True
"""
Specifies if only long NIRS channels should be retained for processing.

???+ example "Example"
```python
nirs_only_long_channels = True # processes only long channels
nirs_only_long_channels = False # process both long and short channels
```
"""

nirs_short_channel_correction: Optional[bool] = True
"""
Specifies if short channel correction should be applied to fNIRS data.

???+ example "Example"
```python
nirs_short_channel_correction = True # use short correction
nirs_short_channel_correction = False # do not use short correction
```
"""

###############################################################################
# MAXWELL FILTER PARAMETERS
# -------------------------
Expand Down Expand Up @@ -1341,7 +1367,7 @@ def get_t1_from_meeg(bids_path):
msg = ('EEG data can only be analyzed separately from other channel '
'types. Please adjust `ch_types` in your configuration.')
raise ValueError(msg)
elif any([ch_type not in ('meg', 'mag', 'grad') for ch_type in ch_types]):
elif any([ch_type not in ('meg', 'mag', 'grad', 'nirs') for ch_type in ch_types]):
msg = ('Invalid channel type passed. Please adjust `ch_types` in your '
'configuration.')
raise ValueError(msg)
Expand Down Expand Up @@ -1649,13 +1675,15 @@ def get_task() -> Optional[str]:
return task


def get_datatype() -> Literal['meg', 'eeg']:
def get_datatype() -> Literal['meg', 'eeg', 'nirs']:
# Content of ch_types should be sanitized already, so we don't need any
# extra sanity checks here.
if data_type is not None:
return data_type
elif data_type is None and ch_types == ['eeg']:
return 'eeg'
elif data_type is None and ch_types == ['nirs']:
return 'nirs'
elif data_type is None and any([t in ['meg', 'mag', 'grad']
for t in ch_types]):
return 'meg'
Expand Down Expand Up @@ -1824,6 +1852,9 @@ def get_channels_to_analyze(info) -> List[str]:
elif ch_types == ['eeg']:
pick_idx = mne.pick_types(info, meg=False, eeg=True, eog=True,
ecg=True, exclude=[])
elif ch_types == ['nirs']:
pick_idx = mne.pick_types(info, meg=False, eeg=False, eog=False,
ecg=False, fnirs=True, exclude=[])
else:
raise RuntimeError('Something unexpected happened. Please contact '
'the mne-bids-pipeline developers. Thank you.')
Expand Down
14 changes: 14 additions & 0 deletions scripts/preprocessing/01-import_and_maxfilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@

import mne
from mne.preprocessing import find_bad_channels_maxwell
from mne.preprocessing.nirs import optical_density, beer_lambert_law
from mne.parallel import parallel_func
from mne_bids import BIDSPath, read_raw_bids

from mne_nirs.signal_enhancement import short_channel_regression
from mne_nirs.channels import get_long_channels

import config
from config import gen_log_message, on_error, failsafe_run

Expand Down Expand Up @@ -238,6 +242,16 @@ def load_data(bids_path):
if hasattr(raw, 'fix_mag_coil_types'):
raw.fix_mag_coil_types()

if config.get_datatype() == 'nirs':
if 'fnirs_cw_amplitude' in raw:
raw = optical_density(raw)
if 'fnirs_od' in raw:
if config.nirs_short_channel_correction:
raw = short_channel_regression(raw)
raw = beer_lambert_law(raw)
if config.nirs_only_long_channels:
raw = get_long_channels(raw, min_dist=0.01)

montage_name = config.eeg_template_montage
if config.get_datatype() == 'eeg' and montage_name:
msg = (f'Setting EEG channel locations to template montage: '
Expand Down
2 changes: 2 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ coloredlogs
openneuro-py
https://api.github.com/repos/mne-tools/mne-python/zipball/main
https://api.github.com/repos/mne-tools/mne-bids/zipball/main
mne-nirs
nilearn