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

MRG: Add support for NIRS to plot_topomap #7063

Merged
merged 16 commits into from
Nov 18, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Changelog

- Allow returning vector source estimates from sparse inverse solvers through ``pick_ori='vector'`` by `Christian Brodbeck`_

- Add NIRS support to :func:`mne.viz.plot_topomap` by `Robert Luke`_

Bug
~~~

Expand Down
3 changes: 2 additions & 1 deletion mne/channels/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ def _get_ch_type(inst, ch_type, allow_ref_meg=False):
then grads, then ... to plot.
"""
if ch_type is None:
allowed_types = ['mag', 'grad', 'planar1', 'planar2', 'eeg', 'csd']
allowed_types = ['mag', 'grad', 'planar1', 'planar2', 'eeg', 'csd',
'hbo', 'hbr']
rob-luke marked this conversation as resolved.
Show resolved Hide resolved
allowed_types += ['ref_meg'] if allow_ref_meg else []
for type_ in allowed_types:
if isinstance(inst, Info):
Expand Down
11 changes: 8 additions & 3 deletions mne/viz/topomap.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ def _prepare_topo_plot(inst, ch_type, layout):
layout = find_layout(info) # XXX : why not passing ch_type???
elif layout == 'auto':
layout = None

clean_ch_names = _clean_names(info['ch_names'])
if ch_type in ['hbo', 'hbr']:
# The naming for nirs is very specific and should not be modified
clean_ch_names = info['ch_names']
else:
clean_ch_names = _clean_names(info['ch_names'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_clean_names is used in a number of places. I would rather avoid the branching here to make sure it's consistent with other places. How specific are NIRS channel names? What ch name breaks it?

Copy link
Member Author

@rob-luke rob-luke Nov 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name given by the device manufacturer is in the format of source number then detector number, so for example “S1 D2” is the channel between source one and detector two. We then appended the light frequency or chomaphore. So currently the MNE channel names are in the format S1 D2 760.

The problem was that the spaces were getting removed and the first characters were being kept, so we would have several channel names that were modified to be “S1” for example (because source one may have channels to several detectors). And that caused the error listed in the first comment of this PR.

But we can change the way channel names are stored. We could make it S1-D2-760 Or S1D2-760 etc. I think this would solve the problem, but not sure if it will break other things. What do you think?

Copy link
Member Author

@rob-luke rob-luke Nov 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that’s wrong, the format is currently S1-D2 760. Another example is S12-D3 850

https://github.com/mne-tools/mne-python/blob/master/mne/io/nirx/tests/test_nirx.py#L37

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I have push an attempt to change the naming to S1_D3 760. This way the source-detector pair is read as a single word, so stays unique without modifying clean_names. The raw.plot doesnt look as good, but I think thats ok. Thoughts?

for ii, this_ch in enumerate(info['chs']):
this_ch['ch_name'] = clean_ch_names[ii]
info['bads'] = _clean_names(info['bads'])
Expand All @@ -67,6 +70,9 @@ def _prepare_topo_plot(inst, ch_type, layout):
elif ch_type == 'csd':
picks = pick_types(info, meg=False, csd=True, ref_meg=False,
exclude='bads')
elif ch_type in ['hbo', 'hbr']:
picks = pick_types(info, meg=False, csd=True, ref_meg=False,
rob-luke marked this conversation as resolved.
Show resolved Hide resolved
fnirs=True, exclude='bads')
else:
picks = pick_types(info, meg=ch_type, ref_meg=False,
exclude='bads')
Expand Down Expand Up @@ -1637,7 +1643,6 @@ def plot_evoked_topomap(evoked, times="auto", ch_type=None, layout=None,
mask_params = _handle_default('mask_params', mask_params)
mask_params['markersize'] *= size / 2.
mask_params['markeredgewidth'] *= size / 2.

picks, pos, merge_grads, names, ch_type = _prepare_topo_plot(
evoked, ch_type, layout)

Expand Down
31 changes: 30 additions & 1 deletion tutorials/preprocessing/plot_70_fnirs_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# sphinx_gallery_thumbnail_number = 3

import os
import numpy as np
import matplotlib.pyplot as plt

import mne
Expand Down Expand Up @@ -162,7 +163,7 @@
# Plot standard fNIRS response image
# ----------------------------------
#
# Finally we generate the most common visualisation of fNIRS data, plotting
# Next we generate the most common visualisation of fNIRS data, plotting
# both the HbO and HbR on the same figure to illustrate the relation between
# the two signals.

Expand All @@ -180,3 +181,31 @@

mne.viz.plot_compare_evokeds(evoked_dict, combine="mean", ci=0.95,
colors=color_dict, styles=styles_dict)


###############################################################################
# View topographic representation of activity
# -------------------------------------------
#
# Next we view how the topographic activity changes throughout the response.

times = np.arange(-3.5, 13.2, 3.0)
epochs['Tapping'].average(picks='hbo').plot_joint(times=times)


###############################################################################
# Compare tapping of left and right hands
# ---------------------------------------
#
# Finally we generate topo maps for the left and right conditions to view
# the location of activity. First we visualise the HbO activity.

times = np.arange(4.0, 11.0, 1.0)
epochs['Tapping/Left'].average(picks='hbo').plot_topomap(times=times)
epochs['Tapping/Right'].average(picks='hbo').plot_topomap(times=times)

###############################################################################
# And we also view the HbR activity for the two conditions.

epochs['Tapping/Left'].average(picks='hbr').plot_topomap(times=times)
epochs['Tapping/Right'].average(picks='hbr').plot_topomap(times=times)