How to add montage manually for multi-modal data

:question: If you have a question or issue with MNE-Python, please include the following info:

  • MNE version: e.g. 1.6.0
  • operating system: e.g. Windows 10.0.22621
  • Python 3.11.5
  • IDE: VSCode

Hello,
I am having trouble with adding manual Montage using .set_montage() and visualisation.plot_3d_montage()

set montage returns “IndexError: list index out of range”
and plot_3d_montage() raises: “ValueError: not enough values to unpack (expected 3, got 0)” in brain.add_head(dense=False, alpha=0.1) → surf = _get_head_surface(

My data structure is as follows:
Good channels 15 EEG, 1 Stimulus, 14 Oxyhemoglobin, 14 Deoxyhemoglobin

I have tried different ways: just picking “fnirs” channels or “eeg” channels. Either way, I got an error.

I have added eeg data separately and then added them to a combined MNE raw file.
Any help would be highly appreciated.

ch_names = [
            'Fpz',
            'AFz',
            'F5',
            'F6',
            'FCz',
            'FC3',
            'FC4',
            'Cz',
            'C5',
            'C6',
            'TTP7',
            'TTP8',
            'TPP7',
            'TPP8',
            'Pz',
            'StimulusCode',
            'S1_D1','S2_D1', 'S2_D2', 'S3_D2', 'S4_D3', 'S4_D4', 'S4_D5', 'S5_D4', 'S5_D5', 'S6_D6', 'S6_D7','S6_D8', 'S7_D7','S7_D8',
            'S1_D1 hbr','S2_D1 hbr', 'S2_D2 hbr', 'S3_D2 hbr', 'S4_D3 hbr', 'S4_D4 hbr', 'S4_D5 hbr', 'S5_D4 hbr', 'S5_D5 hbr', 'S6_D6 hbr', 'S6_D7 hbr','S6_D8 hbr', 'S7_D7 hbr','S7_D8 hbr'                   
]
info_combined = mne.create_info(ch_names=ch_names,
                                sfreq=reconst_raw.info['sfreq'],
                                ch_types= ['eeg']*15+['stim']*1 + ['hbo'] *14 +['hbr'] *14
                                )

# Combine the data arrays
data_combined = np.concatenate([reconst_raw.get_data(), raw_hbo_resampled.get_data(),raw_hbr_resampled.get_data()], axis=0)

# Create a new Raw object with the combined information and data
raw_combined = mne.io.RawArray(data_combined, info_combined)
# The Output of the coordination Dict
combined_coords = {'S1_D1 760': array([-0.0502438,  0.0531112,  0.042192 ]), 'S2_D1 760': array([0.0003122, 0.058512 , 0.066462 ]), 'S2_D2 760': array([0.0003122, 0.058512 , 0.066462 ]), 'S3_D2 760': array([0.0518362, 0.0543048, 0.040814 ]), 'S4_D3 760': array([-0.0848302, -0.0460217, -0.007056 ]), 'S4_D4 760': array([-0.0848302, -0.0460217, -0.007056 ]), 'S4_D5 760': array([-0.0848302, -0.0460217, -0.007056 ]), 'S5_D4 760': array([-0.0672723, -0.0762907,  0.028382 ]), 'S5_D5 760': array([-0.0672723, -0.0762907,  0.028382 ]), 'S6_D6 760': array([ 0.0855488, -0.0455453, -0.00713  ]), 'S6_D7 760': array([ 0.0855488, -0.0455453, -0.00713  ]), 'S6_D8 760': array([ 0.0855488, -0.0455453, -0.00713  ]), 'S7_D7 760': array([ 0.0678877, -0.0759043,  0.028091 ]), 'S7_D8 760': array([ 0.0678877, -0.0759043,  0.028091 ]), 'S1_D1 850': array([-0.0502438,  0.0531112,  0.042192 ]), 'S2_D1 850': array([0.0003122, 0.058512 , 0.066462 ]), 'S2_D2 850': array([0.0003122, 0.058512 , 0.066462 ]), 'S3_D2 850': array([0.0518362, 0.0543048, 0.040814 ]), 'S4_D3 850': array([-0.0848302, -0.0460217, -0.007056 ]), 'S4_D4 850': array([-0.0848302, -0.0460217, -0.007056 ]), 'S4_D5 850': array([-0.0848302, -0.0460217, -0.007056 ]), 'S5_D4 850': array([-0.0672723, -0.0762907,  0.028382 ]), 'S5_D5 850': array([-0.0672723, -0.0762907,  0.028382 ]), 'S6_D6 850': array([ 0.0855488, -0.0455453, -0.00713  ]), 'S6_D7 850': array([ 0.0855488, -0.0455453, -0.00713  ]), 'S6_D8 850': array([ 0.0855488, -0.0455453, -0.00713  ]), 'S7_D7 850': array([ 0.0678877, -0.0759043,  0.028091 ]), 'S7_D8 850': array([ 0.0678877, -0.0759043,  0.028091 ]), 'Fpz': array([ 0.0001123,  0.088247 , -0.001713 ]), 'AFz': array([0.0002313, 0.080771 , 0.035417 ]), 'F5': array([-0.0644658,  0.0480353,  0.016921 ]), 'F6': array([0.0679142, 0.0498297, 0.016367 ]), 'FCz': array([0.0003761, 0.02739  , 0.088668 ]), 'FC3': array([-0.0601819,  0.0227162,  0.055544 ]), 'FC4': array([0.0622931, 0.0237228, 0.05563  ]), 'Cz': array([ 0.0004009, -0.009167 ,  0.100244 ]), 'C5': array([-0.0802801, -0.0137597,  0.02916  ]), 'C6': array([ 0.0834559, -0.0127763,  0.029208 ]), 'TTP7': array([-0.0859331, -0.0310927, -0.008474 ]), 'TTP8': array([ 0.0859999, -0.0302803, -0.008435 ]), 'TPP7': array([-0.0785992, -0.0597237, -0.004758 ]), 'TPP8': array([ 0.0793188, -0.0593033, -0.00484  ]), 'Pz': array([ 0.0003247, -0.081115 ,  0.082615 ])}

combined_montage = mne.channels.make_dig_montage(ch_pos=conbined_coords, coord_frame='mri') # nasion=(0, 0.1, 0), lpa=(-0.1, 0, 0), rpa=(0.1, 0, 0)

raw_combined.set_montage(combined_montage)

This results in the error mentioned before.

I tried this method also:

locs_eeg = [ch['loc'] for ch in reconst_raw.info['chs']]
locs_eeg = np.stack(locs_eeg)
for indx,row in enumerate(locs_eeg):
    raw_combined.info['chs'][indx]['loc']= row
 for indx,ch_name in enumerate(raw_hbo_resampled.ch_names):
    # Split channel name to get source and detector
    src, det = ch_name.split('_')
    # Get positions of the source and detector from the montage
    src_pos = montage_positions[src]
    det_pos = montage_positions[det]
    # Find the index of the channel in raw_hbo_resampled
    ch_idx = raw_hbo_resampled.ch_names.index(ch_name)
    raw_combined.info['chs'][16+indx]['loc'][0:12] = np.concatenate([src_pos,det_pos, [np.sqrt(np.sum((src_pos - det_pos)**2))*100 ,0,0,0,0,0]], axis=0)

for indx,ch_name in enumerate(raw_hbr_resampled.ch_names):
    src, det = ch_name.split('_')
    src_pos = montage_positions[src]
    det_pos = montage_positions[det]
    # Find the index of the channel in raw_hbr_resampled
    ch_idx = raw_hbr_resampled.ch_names.index(ch_name) 
    raw_combined.info['chs'][30+indx]['loc'][0:12] = np.concatenate([src_pos,det_pos,[np.sqrt(np.sum((src_pos - det_pos)**2)) ,0,0,0,0,0]], axis=0)
 

are you sure you have supplied the exact right amount of coordinates for the sensors that you have? This error suggests that you have supplied too few.

Furthermore, I would simplify my situation and remove all channels that are not expected to have a position (e.g., stim channels) … just for debugging.

Thank you for your help. I am not sure why I got the error by I solved the montage part by writing it like this:

standard_montage = mne.channels.make_standard_montage(montage)
    # Extract positions from the standard montage
    source_positions = {s: standard_montage.get_positions()['ch_pos'][loc] for s, loc in sources.items()}
    detector_positions = {d: standard_montage.get_positions()['ch_pos'][loc] for d, loc in detectors.items()}

    eeg_coords={}        
    for channel in eeg_ch_names:
        if channel in standard_montage.ch_names:
            # Get the index of the EEG label in the standard montage
            idx = standard_montage.ch_names.index(channel)
            # Use this index to get the 3D coordinates from the standard montage
            coord = standard_montage.dig[idx+3]['r']  # Offset by 3 to skip the fiducials
            # Assign these coordinates to your NIRS channel
            eeg_coords[channel] = coord
        else:
            print("Label not found in standard montage.")

    # Combine source and detector positions
    montage_positions = {**source_positions, **detector_positions,**eeg_coords}
    mne.channels.make_dig_montage(ch_pos=montage_positions,nasion=(0, 0.1, 0), lpa=(-0.1, 0, 0), rpa=(0.1, 0, 0), coord_frame='head')
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.