fNIRS Custom Montage

  • MNE version: 1.8.0
  • operating system: Windows 11

Hello,

I have fNIRS data where the source and detector locations follow the brainproducts-RNP-BA-128 montage. I would like to use this montage configuration with the provided code.

raw_modified = raw_intensity.copy()
raw_modified.set_montage(brainproducts)

However, an error is occurring, as shown below:

ValueError: 'S1' is not in list

Here’s a refined version:

I reviewed similar posts, such as this one, and tried to follow the suggested steps. I also checked the MNE GitHub montages directory but couldn’t find the .elc file for BrainProducts. Therefore, I extracted the x, y, z coordinate information using the code provided below:

# Load the brainproducts standard montage
brainproducts = mne.channels.make_standard_montage('brainproducts-RNP-BA-128')

# Initialize the header of the .elc file content
elc_content = [
    "# ASA electrode file",
    "ReferenceLabel avg",
    "UnitPosition mm",
    f"NumberPositions= {len(brainproducts.dig)}",  # Total number of points, including fiducials
    "Positions"
]

# Extract the x, y, z coordinates for each channel in mm, including LPA, RPA, and Nasion
for pos in brainproducts.dig:
    x, y, z = pos['r'] * 1000  # Convert from meters to millimeters
    elc_content.append(f"{x:.4f} {y:.4f} {z:.4f}")

# Add Labels section
elc_content.append("Labels")

# Add fiducials and then channel names
fiducials = ['LPA', 'Nasion', 'RPA']
elc_content.extend(fiducials + brainproducts.ch_names)

# Save to .elc file
with open("brainproducts-RNP-BA-128-org.elc", "w") as f:
    f.write("\n".join(elc_content))

Since the fNIRS channel names differ from those in the BrainProducts montage, I attempted to map the fNIRS channels to the BrainProducts channels. However, I encountered some errors. My code is provided below:

mapping = {
    'S1_D1 760': 'Fp1_AFp1 760',
    'S1_D1 850': 'Fp1_AFp1 850',
    'S1_D3 760': 'Fp1_AFF3h 760',
    'S1_D3 850': 'Fp1_AFF3h 850',
    'S2_D2 760': 'AFp2_AFF2h 760',
    'S2_D2 850': 'AFp2_AFF2h 850',
    'S2_D1 760': 'AFp2_Fp1 760',
    'S2_D1 850': 'AFp2_Fp1 850',
    'S2_D5 760': 'AFp2_AFF6h 760',
    'S2_D5 850': 'AFp2_AFF6h 850',
    'S3_D1 760': 'AFF3h_Fp1 760',
    'S3_D1 850': 'AFF3h_Fp1 850',
    'S3_D3 760': 'AFF3h_AFF2h 760',
    'S3_D3 850': 'AFF3h_AFF2h 850',
    'S3_D4 760': 'AFF3h_AFF4h 760',
    'S3_D4 850': 'AFF3h_AFF4h 850',
    'S3_D6 760': 'AFF3h_AFF6h 760',
    'S3_D6 850': 'AFF3h_AFF6h 850',
    'S4_D4 760': 'AFF2h_AFF4h 760',
    'S4_D4 850': 'AFF2h_AFF4h 850',
    'S4_D5 760': 'AFF2h_AFF6h 760',
    'S4_D5 850': 'AFF2h_AFF6h 850',
    'S4_D7 760': 'AFF2h_AFF5h 760',
    'S4_D7 850': 'AFF2h_AFF5h 850',
    'S5_D2 760': 'FFC5h_Fp1 760',
    'S5_D2 850': 'FFC5h_Fp1 850',
    'S5_D5 760': 'FFC5h_AFF6h 760',
    'S5_D5 850': 'FFC5h_AFF6h 850',
    'S5_D8 760': 'FFC5h_FFC2h 760',
    'S5_D8 850': 'FFC5h_FFC2h 850',
    'S6_D3 760': 'FFC1h_AFF3h 760',
    'S6_D3 850': 'FFC1h_AFF3h 850',
    'S6_D6 760': 'FFC1h_AFF6h 760',
    'S6_D6 850': 'FFC1h_AFF6h 850',
    'S7_D4 760': 'FFC4h_AFF4h 760',
    'S7_D4 850': 'FFC4h_AFF4h 850',
    'S7_D6 760': 'FFC4h_AFF6h 760',
    'S7_D6 850': 'FFC4h_AFF6h 850',
    'S7_D7 760': 'FFC4h_FFC3h 760',
    'S7_D7 850': 'FFC4h_FFC3h 850',
    'S8_D5 760': 'FFC2h_AFF6h 760',
    'S8_D5 850': 'FFC2h_AFF6h 850',
    'S8_D7 760': 'FFC2h_FFC3h 760',
    'S8_D7 850': 'FFC2h_FFC3h 850',
    'S8_D8 760': 'FFC2h_FFC6h 760',
    'S8_D8 850': 'FFC2h_FFC6h 850'
}


brainproducts_montage = mne.channels.read_custom_montage("brainproducts-RNP-BA-128.elc", head_size=None)
print(brainproducts_montage)
print(brainproducts_montage.dig)
raw_intensity_modified = raw_intensity.copy()
raw_intensity_modified.rename_channels(mapping)

# Set the channels to fnirs_cw_amplitude type
fnirs_types = {ch_name: 'fnirs_cw_amplitude' for ch_name in raw_intensity_modified.ch_names}
raw_intensity_modified.set_channel_types(fnirs_types)

raw_intensity_modified.set_montage(brainproducts_montage)

But I get this error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[484], line 61
     58 fnirs_types = {ch_name: 'fnirs_cw_amplitude' for ch_name in raw_intensity_modified.ch_names}
     59 raw_intensity_modified.set_channel_types(fnirs_types)
---> 61 raw_intensity_modified.set_montage(brainproducts_montage)
     63 # raw_intensity_modified.plot_sensors(kind='topomap', show_names=False)
     64 # fig = raw_intensity_modified.get_montage().plot()

File <decorator-gen-22>:12, in set_montage(self, montage, match_case, match_alias, on_missing, verbose)

File c:\ProgramData\miniconda3\envs\fnirs\lib\site-packages\mne\_fiff\meas_info.py:422, in MontageMixin.set_montage(self, montage, match_case, match_alias, on_missing, verbose)
    419 from ..channels.montage import _set_montage
    421 info = self if isinstance(self, Info) else self.info
--> 422 _set_montage(info, montage, match_case, match_alias, on_missing)
    423 return self

File c:\ProgramData\miniconda3\envs\fnirs\lib\site-packages\mne\channels\montage.py:1329, in _set_montage(***failed resolving arguments***)
   1327 fnirs_picks = _picks_to_idx(info, "fnirs", allow_empty=True)
   1328 if len(fnirs_picks) > 0:
-> 1329     _set_montage_fnirs(info, mnt_head)

File c:\ProgramData\miniconda3\envs\fnirs\lib\site-packages\mne\channels\montage.py:1081, in _set_montage_fnirs(info, montage)
   1078 from ..preprocessing.nirs import _validate_nirs_info
   1080 # Validate that the fNIRS info is correctly formatted
-> 1081 picks = _validate_nirs_info(info)
   1083 # Modify info['chs'][#]['loc'] in place
   1084 num_ficiduals = len(montage.dig) - len(montage.ch_names)

File c:\ProgramData\miniconda3\envs\fnirs\lib\site-packages\mne\preprocessing\nirs\nirs.py:261, in _validate_nirs_info(info, throw_errors, fnirs, which, check_bads, allow_empty)
    257     if not len(pick_types(info, fnirs=fnirs)):
    258         raise RuntimeError(
    259             f"{which} must operate on {kind} data, but none was found."
    260         )
--> 261 freqs = np.unique(_channel_frequencies(info))
    262 if freqs.size > 0:
    263     pair_vals = freqs

File c:\ProgramData\miniconda3\envs\fnirs\lib\site-packages\mne\preprocessing\nirs\nirs.py:73, in _channel_frequencies(info)
     71 freqs = list()
     72 for pick in picks:
---> 73     freqs.append(round(float(_S_D_F_RE.match(info["ch_names"][pick]).groups()[2])))
     74 return np.array(freqs, int)

AttributeError: 'NoneType' object has no attribute 'groups'

Solution Found. I’ll share it here for others who may encounter a similar issue. I resolved it by creating the .elc file as follows:

# ASA electrode file
ReferenceLabel avg
UnitPosition mm
NumberPositions=19
Positions
-82.5 0.0 -0.0            
0.0 114.0 -0.0            
82.5 0.0 0.0             
-30.7 115.4 20.6         
12.0 118.6 45.1           
-37.1 104.4 71.9         
11.0 108.2 83.5           
49.0 98.1 52.8            
-68.0 70.8 68.3           
-19.0 81.7 112.6         
44.4 77.9 95.9            
-14.8 118.7 45.0          
27.7 115.2 21.0           
-52.6 98.0 52.3           
-14.3 108.2 83.4          
33.6 104.5 72.1          
-48.5 77.5 95.6           
15.0 81.8 112.7          
64.0 71.4 68.7            
Labels
LPA
Nasion
RPA
S1
S2
S3
S4
S5
S6
S7
S8
D1
D2
D3
D4
D5
D6
D7
D8
brainproducts_montage = mne.channels.read_custom_montage("brainproducts-RNP-BA-128.elc", head_size=None)
raw_intensity_modified = raw_intensity.copy()

raw_intensity_modified.set_montage(brainproducts_montage)

raw_intensity_modified.plot_sensors(kind='topomap', show_names=False)
fig = raw_intensity_modified.get_montage().plot()

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