diff --git a/doc/changes/latest.inc b/doc/changes/latest.inc index f85500dd1a1..33999191029 100644 --- a/doc/changes/latest.inc +++ b/doc/changes/latest.inc @@ -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 ~~~ diff --git a/mne/channels/channels.py b/mne/channels/channels.py index fb2eda06457..de65a6624b5 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -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', + 'fnirs_raw', 'fnirs_od', 'hbo', 'hbr'] allowed_types += ['ref_meg'] if allow_ref_meg else [] for type_ in allowed_types: if isinstance(inst, Info): diff --git a/mne/io/nirx/nirx.py b/mne/io/nirx/nirx.py index 00aeb72b52b..f442e152e55 100644 --- a/mne/io/nirx/nirx.py +++ b/mne/io/nirx/nirx.py @@ -198,7 +198,7 @@ def prepend(list, str): list = [str.format(i) for i in list] return(list) snames = prepend(sources[req_ind], 'S') - dnames = prepend(detectors[req_ind], '-D') + dnames = prepend(detectors[req_ind], '_D') sdnames = [m + str(n) for m, n in zip(snames, dnames)] sd1 = [s + ' ' + str(fnirs_wavelengths[0]) for s in sdnames] sd2 = [s + ' ' + str(fnirs_wavelengths[1]) for s in sdnames] diff --git a/mne/io/nirx/tests/test_nirx.py b/mne/io/nirx/tests/test_nirx.py index 757202b0daa..b54274afe87 100644 --- a/mne/io/nirx/tests/test_nirx.py +++ b/mne/io/nirx/tests/test_nirx.py @@ -34,9 +34,9 @@ def test_nirx_15_2_short(): assert raw.info['sfreq'] == 12.5 # Test channel naming - assert raw.info['ch_names'][:4] == ["S1-D1 760", "S1-D1 850", - "S1-D9 760", "S1-D9 850"] - assert raw.info['ch_names'][24:26] == ["S5-D13 760", "S5-D13 850"] + assert raw.info['ch_names'][:4] == ["S1_D1 760", "S1_D1 850", + "S1_D9 760", "S1_D9 850"] + assert raw.info['ch_names'][24:26] == ["S5_D13 760", "S5_D13 850"] # Test frequency encoding assert raw.info['chs'][0]['loc'][9] == 760 @@ -123,8 +123,8 @@ def test_nirx_15_2(): assert raw.info['sfreq'] == 3.90625 # Test channel naming - assert raw.info['ch_names'][:4] == ["S1-D1 760", "S1-D1 850", - "S1-D10 760", "S1-D10 850"] + assert raw.info['ch_names'][:4] == ["S1_D1 760", "S1_D1 850", + "S1_D10 760", "S1_D10 850"] # Test info import assert raw.info['subject_info'] == dict(sex=1, first_name="TestRecording") @@ -157,12 +157,12 @@ def test_nirx_15_0(): assert raw.info['sfreq'] == 6.25 # Test channel naming - assert raw.info['ch_names'][:12] == ["S1-D1 760", "S1-D1 850", - "S2-D2 760", "S2-D2 850", - "S3-D3 760", "S3-D3 850", - "S4-D4 760", "S4-D4 850", - "S5-D5 760", "S5-D5 850", - "S6-D6 760", "S6-D6 850"] + assert raw.info['ch_names'][:12] == ["S1_D1 760", "S1_D1 850", + "S2_D2 760", "S2_D2 850", + "S3_D3 760", "S3_D3 850", + "S4_D4 760", "S4_D4 850", + "S5_D5 760", "S5_D5 850", + "S6_D6 760", "S6_D6 850"] # Test info import assert raw.info['subject_info'] == {'first_name': 'NIRX', diff --git a/mne/preprocessing/nirs/_beer_lambert_law.py b/mne/preprocessing/nirs/_beer_lambert_law.py index c242d2df79f..be5ed06ff07 100644 --- a/mne/preprocessing/nirs/_beer_lambert_law.py +++ b/mne/preprocessing/nirs/_beer_lambert_law.py @@ -74,9 +74,9 @@ def _check_channels_ordered(raw, freqs): # and have the specified light frequencies. picks = _picks_to_idx(raw.info, 'fnirs_od') for ii in picks[::2]: - ch1_name_info = re.match(r'S(\d+)-D(\d+) (\d+)', + ch1_name_info = re.match(r'S(\d+)_D(\d+) (\d+)', raw.info['chs'][ii]['ch_name']) - ch2_name_info = re.match(r'S(\d+)-D(\d+) (\d+)', + ch2_name_info = re.match(r'S(\d+)_D(\d+) (\d+)', raw.info['chs'][ii + 1]['ch_name']) if (ch1_name_info.groups()[0] != ch2_name_info.groups()[0]) or \ diff --git a/mne/preprocessing/tests/test_beer_lambert_law.py b/mne/preprocessing/tests/test_beer_lambert_law.py index c0c2ca9a401..1b7643862e0 100644 --- a/mne/preprocessing/tests/test_beer_lambert_law.py +++ b/mne/preprocessing/tests/test_beer_lambert_law.py @@ -68,6 +68,6 @@ def test_beer_lambert_v_matlab(): raw._data[idx]) assert mean_error < 0.1 matlab_name = ("S" + str(int(matlab_data['sources'][idx])) + - "-D" + str(int(matlab_data['detectors'][idx])) + + "_D" + str(int(matlab_data['detectors'][idx])) + " " + matlab_data['type'][idx]) assert raw.info['ch_names'][idx] == matlab_name diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index f1d95a288f4..2158ff015bb 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -67,6 +67,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', 'fnirs_raw', 'fnirs_od']: + picks = pick_types(info, meg=False, ref_meg=False, + fnirs=ch_type, exclude='bads') else: picks = pick_types(info, meg=ch_type, ref_meg=False, exclude='bads') diff --git a/tutorials/preprocessing/plot_70_fnirs_processing.py b/tutorials/preprocessing/plot_70_fnirs_processing.py index 23a83b75fa8..296b303358c 100644 --- a/tutorials/preprocessing/plot_70_fnirs_processing.py +++ b/tutorials/preprocessing/plot_70_fnirs_processing.py @@ -17,6 +17,7 @@ # sphinx_gallery_thumbnail_number = 3 import os +import numpy as np import matplotlib.pyplot as plt import mne @@ -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. @@ -180,3 +181,61 @@ 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) + +############################################################################### +# And we can plot the comparison at a single time point for two conditions. + +fig, axes = plt.subplots(nrows=2, ncols=4, figsize=(9, 5)) +vmin, vmax, ts = -8, 8, 9.0 + +evoked_left = epochs['Tapping/Left'].average() +evoked_right = epochs['Tapping/Right'].average() + +evoked_left.plot_topomap(ch_type='hbo', times=ts, axes=axes[0, 0], + vmin=vmin, vmax=vmax, colorbar=False) +evoked_left.plot_topomap(ch_type='hbr', times=ts, axes=axes[1, 0], + vmin=vmin, vmax=vmax, colorbar=False) +evoked_right.plot_topomap(ch_type='hbo', times=ts, axes=axes[0, 1], + vmin=vmin, vmax=vmax, colorbar=False) +evoked_right.plot_topomap(ch_type='hbr', times=ts, axes=axes[1, 1], + vmin=vmin, vmax=vmax, colorbar=False) + +evoked_diff = mne.combine_evoked([evoked_left, -evoked_right], weights='equal') + +evoked_diff.plot_topomap(ch_type='hbo', times=ts, axes=axes[0, 2], + vmin=vmin, vmax=vmax) +evoked_diff.plot_topomap(ch_type='hbr', times=ts, axes=axes[1, 2], + vmin=vmin, vmax=vmax, colorbar=True) + +for column, condition in enumerate( + ['Tapping Left', 'Tapping Right', 'Left-Right']): + for row, chroma in enumerate(['HbO', 'HbR']): + axes[row, column].set_title('{}: {}'.format(chroma, condition))