-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
WIP: Adds support for fNIRS data to set_montage and new standard artinis montage #9141
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
Changes from 47 commits
e0f8cc6
2159fb3
44cbe02
5dae7fa
e55859a
cebdf10
9064883
4091332
9bbf7a8
d65d8f8
7428812
599bbfd
b77459c
801c27f
28ff382
69bc74c
eef41f3
0a35722
03e6944
8033643
c04ad5c
059ae2f
7fc5db9
7e00eb5
a47743e
4675c64
4fb7463
27b8123
d60f88e
e465a1e
b7cfe66
9a4db53
e4ed607
acc71eb
e3a6c7b
28e2a50
11c30ee
4d4a80c
c990c8b
9228a01
e971fe8
5fac262
2640d12
2b17fb0
1ffa727
227fc24
717f2e6
4690830
726e282
20aa229
de264a0
bade068
c695f04
0ea038a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # ASA optode file | ||
| ReferenceLabel avg | ||
| UnitPosition mm | ||
| NumberPositions= 21 | ||
| Positions | ||
| -4.62 82.33 -45.74 | ||
| 79.66 -18.72 -45.89 | ||
| -81.41 -17.18 -45.56 | ||
| 65.18 27.28 35.31 | ||
| 48.62 59.71 22.68 | ||
| 18.95 72.41 38.32 | ||
| -3.97 79.74 30.28 | ||
| -25.96 72.19 35.16 | ||
| -52.51 60.53 14.54 | ||
| -66.37 32.04 31.08 | ||
| 76.10 -0.29 31.24 | ||
| 65.61 -0.26 56.15 | ||
| 64.93 42.43 8.29 | ||
| 43.32 46.36 50.77 | ||
| 21.58 82.45 1.06 | ||
| -2.91 59.57 61.59 | ||
| -29.62 79.35 2.38 | ||
| -48.13 44.76 49.15 | ||
| -67.68 43.26 -3.18 | ||
| -65.37 4.89 56.36 | ||
| -77.24 5.88 27.58 | ||
| Labels | ||
| Nz | ||
| RPA | ||
| LPA | ||
| D1 | ||
| D2 | ||
| D3 | ||
| D4 | ||
| D5 | ||
| D6 | ||
| D7 | ||
| S1 | ||
| S2 | ||
| S3 | ||
| S4 | ||
| S5 | ||
| S6 | ||
| S7 | ||
| S8 | ||
| S9 | ||
| S10 | ||
| S11 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # ASA optode file | ||
| ReferenceLabel avg | ||
| UnitPosition mm | ||
| NumberPositions= 13 | ||
| Positions | ||
| 0.96 83.56 -48.63 | ||
| 80.25 -19.67 -43.88 | ||
| -82.58 -20.09 -43.10 | ||
| 47.77 65.28 7.28 | ||
| -46.45 67.76 8.81 | ||
| 63.88 34.84 28.34 | ||
| 64.96 45.02 -10.31 | ||
| 22.07 74.86 31.03 | ||
| 17.84 84.96 -10.84 | ||
| -10.81 77.96 32.10 | ||
| -15.96 85.24 -7.41 | ||
| -61.78 40.78 29.92 | ||
| -65.28 48.14 -10.73 | ||
| Labels | ||
| Nz | ||
| RPA | ||
| LPA | ||
| D1 | ||
| D2 | ||
| S1 | ||
| S2 | ||
| S3 | ||
| S4 | ||
| S5 | ||
| S6 | ||
| S7 | ||
| S8 |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,7 +33,7 @@ | |||||||
| _get_data_as_dict_from_dig) | ||||||||
| from ..io.meas_info import create_info | ||||||||
| from ..io.open import fiff_open | ||||||||
| from ..io.pick import pick_types | ||||||||
| from ..io.pick import pick_types, _picks_to_idx | ||||||||
| from ..io.constants import FIFF, CHANNEL_LOC_ALIASES | ||||||||
| from ..utils import (warn, copy_function_doc_to_method_doc, _pl, verbose, | ||||||||
| _check_option, _validate_type, _check_fname, _on_missing, | ||||||||
|
|
@@ -52,7 +52,8 @@ | |||||||
| 'easycap-M1', 'easycap-M10', | ||||||||
| 'mgh60', 'mgh70', | ||||||||
| 'standard_1005', 'standard_1020', 'standard_alphabetic', | ||||||||
| 'standard_postfixed', 'standard_prefixed', 'standard_primed' | ||||||||
| 'standard_postfixed', 'standard_prefixed', 'standard_primed', | ||||||||
| 'artinis-octamon', 'artinis-brite23' | ||||||||
| ] | ||||||||
|
|
||||||||
|
|
||||||||
|
|
@@ -725,6 +726,45 @@ def _get_montage_in_head(montage): | |||||||
| return transform_to_head(montage.copy()) | ||||||||
|
|
||||||||
|
|
||||||||
| def _set_montage_fnirs(info, montage): | ||||||||
| """Set the montage for fNIRS data. | ||||||||
|
|
||||||||
| This needs to be different to electrodes as each channel has three | ||||||||
| coordinates that need to be set. For each channel there is a source optode | ||||||||
| location, a detector optode location, and a channel midpoint that must be | ||||||||
| stored. This function modifies info['chs'][#]['loc'] and info['dig'] in | ||||||||
| place. | ||||||||
| """ | ||||||||
| from ..preprocessing.nirs import (_channel_frequencies, | ||||||||
| _channel_chromophore, | ||||||||
| _check_channels_ordered) | ||||||||
| # Modify info['chs'][#]['loc'] in place | ||||||||
| num_ficiduals = len(montage.dig) - len(montage.ch_names) | ||||||||
| freqs = np.unique(_channel_frequencies(info)) | ||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| if freqs.size > 0: | ||||||||
| picks = _check_channels_ordered(info, freqs) | ||||||||
| else: | ||||||||
| picks = _check_channels_ordered(info, | ||||||||
| np.unique(_channel_chromophore(info))) | ||||||||
| for ch_idx in picks: | ||||||||
| ch = info['chs'][ch_idx]['ch_name'] | ||||||||
| source, detector = ch.split(' ')[0].split('_') | ||||||||
| source_pos = montage.dig[montage.ch_names.index(source) | ||||||||
| + num_ficiduals]['r'] | ||||||||
| detector_pos = montage.dig[montage.ch_names.index(detector) | ||||||||
| + num_ficiduals]['r'] | ||||||||
|
|
||||||||
| info['chs'][ch_idx]['loc'][3:6] = source_pos | ||||||||
| info['chs'][ch_idx]['loc'][6:9] = detector_pos | ||||||||
| midpoint = (source_pos + detector_pos) / 2 | ||||||||
| info['chs'][ch_idx]['loc'][:3] = midpoint | ||||||||
|
|
||||||||
| # Modify info['dig'] in place | ||||||||
| info['dig'] = montage.dig | ||||||||
|
|
||||||||
| return info | ||||||||
|
|
||||||||
|
|
||||||||
| @fill_doc | ||||||||
| def _set_montage(info, montage, match_case=True, match_alias=False, | ||||||||
| on_missing='raise'): | ||||||||
|
|
@@ -758,6 +798,7 @@ def _set_montage(info, montage, match_case=True, match_alias=False, | |||||||
|
|
||||||||
| if isinstance(montage, DigMontage): | ||||||||
| mnt_head = _get_montage_in_head(montage) | ||||||||
| del montage | ||||||||
|
larsoner marked this conversation as resolved.
|
||||||||
|
|
||||||||
| def _backcompat_value(pos, ref_pos): | ||||||||
| if any(np.isnan(pos)): | ||||||||
|
|
@@ -862,6 +903,7 @@ def _backcompat_value(pos, ref_pos): | |||||||
|
|
||||||||
| for name, use in zip(info_names, info_names_use): | ||||||||
| _loc_view = info['chs'][info['ch_names'].index(name)]['loc'] | ||||||||
| # XXX info['chs'][#]['loc'] modified in place | ||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this related to a change we made? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not directly related but I wrote those comments to pinpoint all the lines where the in-place modifications were happening as it wasn't always obvious at first glance for example for this line 907. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I remove them or is it okay?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. such comments are useful, but it would be clearer if it didn't start with |
||||||||
| _loc_view[:6] = _backcompat_value(ch_pos_use[use], eeg_ref_pos) | ||||||||
|
|
||||||||
| del ch_pos_use | ||||||||
|
|
@@ -890,14 +932,23 @@ def _backcompat_value(pos, ref_pos): | |||||||
| # in the old dig | ||||||||
| if ref_dig_point in old_dig: | ||||||||
| digpoints.append(ref_dig_point) | ||||||||
| # XXX info['dig'] modified in place | ||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above |
||||||||
| info['dig'] = _format_dig_points(digpoints, enforce_order=True) | ||||||||
|
|
||||||||
| if mnt_head.dev_head_t is not None: | ||||||||
| # XXX info['dev_head_t'] modified in place | ||||||||
| info['dev_head_t'] = Transform('meg', 'head', mnt_head.dev_head_t) | ||||||||
|
|
||||||||
| # Handle fNIRS with source, detector and channel | ||||||||
| fnirs_picks = _picks_to_idx(info, 'fnirs', allow_empty=True) | ||||||||
| if len(fnirs_picks) > 0: | ||||||||
| info = _set_montage_fnirs(info, mnt_head) | ||||||||
|
|
||||||||
| else: # None case | ||||||||
| # XXX info['dig'] modified in place | ||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above |
||||||||
| info['dig'] = None | ||||||||
| for ch in info['chs']: | ||||||||
| # XXX info['chs'][#]['loc'] modified in place | ||||||||
| ch['loc'] = np.full(12, np.nan) | ||||||||
|
|
||||||||
|
|
||||||||
|
|
@@ -1361,6 +1412,10 @@ def make_standard_montage(kind, head_size=HEAD_SIZE_DEFAULT): | |||||||
| MGH (60+3 locations) | ||||||||
| mgh70 The (newer) 70-channel BrainVision cap used at | ||||||||
| MGH (70+3 locations) | ||||||||
|
|
||||||||
| artinis-octamon Artinis OctaMon fNIRS (8 sources, 2 detectors) | ||||||||
|
|
||||||||
| artinis-brite23 Artinis Brite23 fNIRS (11 sources, 7 detectors) | ||||||||
| =================== ===================================================== | ||||||||
|
|
||||||||
| .. versionadded:: 0.19.0 | ||||||||
|
|
||||||||
Uh oh!
There was an error while loading. Please reload this page.