def _write_dig_bids(bids_path, raw, overwrite=False, verbose=True): """Write BIDS formatted DigMontage from Raw instance. Handles coordinatesystem.json and electrodes.tsv writing from DigMontage. Parameters ---------- bids_path : BIDSPath Path in the BIDS dataset to save the ``electrodes.tsv`` and ``coordsystem.json`` file for. ``datatype`` attribute must be ``eeg``, or ``ieeg``. For ``meg`` data, ``electrodes.tsv`` are not saved. raw : instance of Raw The data as MNE-Python Raw object. overwrite : bool Whether to overwrite the existing file. Defaults to False. verbose : bool Set verbose output to true or false. """ # write electrodes data for iEEG and EEG unit = "m" # defaults to meters # get coordinate frame from digMontage digpoint = raw.info['dig'][0] if any(digpoint['coord_frame'] != _digpoint['coord_frame'] for _digpoint in raw.info['dig']): warn("Not all digpoints have the same coordinate frame. " "Skipping electrodes.tsv writing...") return # get the accepted mne-python coordinate frames coord_frame_int = int(digpoint['coord_frame']) mne_coord_frame = MNE_FRAME_TO_STR.get(coord_frame_int, None) coord_frame = MNE_TO_BIDS_FRAMES.get(mne_coord_frame, None) # create electrodes/coordsystem files using a subset of entities # that are specified for these files in the specification coord_file_entities = { 'root': bids_path.root, 'datatype': bids_path.datatype, 'subject': bids_path.subject, 'session': bids_path.session, 'acquisition': bids_path.acquisition, 'space': bids_path.space } datatype = bids_path.datatype electrodes_path = BIDSPath(**coord_file_entities, suffix='electrodes', extension='.tsv') coordsystem_path = BIDSPath(**coord_file_entities, suffix='coordsystem', extension='.json') if verbose: print("Writing electrodes file to... ", electrodes_path) print("Writing coordsytem file to... ", coordsystem_path) if datatype == "ieeg": if coord_frame is not None: # XXX: To improve when mne-python allows coord_frame='unknown' if coord_frame not in BIDS_IEEG_COORDINATE_FRAMES: coordsystem_path.update(space=coord_frame) electrodes_path.update(space=coord_frame) coord_frame = 'Other' # Now write the data to the elec coords and the coordsystem _electrodes_tsv(raw, electrodes_path, datatype, overwrite, verbose) _coordsystem_json(raw=raw, unit=unit, hpi_coord_system='n/a', sensor_coord_system=coord_frame, fname=coordsystem_path, datatype=datatype, overwrite=overwrite, verbose=verbose) else: # default coordinate frame to mri if not available warn("Coordinate frame of iEEG coords missing/unknown " "for {}. Skipping reading " "in of montage...".format(electrodes_path)) elif datatype == 'eeg': # We only write EEG electrodes.tsv and coordsystem.json # if we have LPA, RPA, and NAS available to rescale to a known # coordinate system frame coords = _extract_landmarks(raw.info['dig']) landmarks = set(['RPA', 'NAS', 'LPA']) == set(list(coords.keys())) # XXX: to be improved to allow rescaling if landmarks are present # mne-python automatically converts unknown coord frame to head if coord_frame_int == FIFF.FIFFV_COORD_HEAD and landmarks: # Now write the data _electrodes_tsv(raw, electrodes_path, datatype, overwrite, verbose) _coordsystem_json(raw=raw, unit='m', hpi_coord_system='n/a', sensor_coord_system='CapTrak', fname=coordsystem_path, datatype=datatype, overwrite=overwrite, verbose=verbose) else: warn("Skipping EEG electrodes.tsv... " "Setting montage not possible if anatomical " "landmarks (NAS, LPA, RPA) are missing, " "and coord_frame is not 'head'.")
def _write_dig_bids(bids_path, raw, montage=None, acpc_aligned=False, overwrite=False): """Write BIDS formatted DigMontage from Raw instance. Handles coordinatesystem.json and electrodes.tsv writing from DigMontage. Parameters ---------- bids_path : mne_bids.BIDSPath Path in the BIDS dataset to save the ``electrodes.tsv`` and ``coordsystem.json`` file for. ``datatype`` attribute must be ``eeg``, or ``ieeg``. For ``meg`` data, ``electrodes.tsv`` are not saved. raw : mne.io.Raw The data as MNE-Python Raw object. montage : mne.channels.DigMontage | None The montage to use rather than the one in ``raw`` if it must be transformed from the "head" coordinate frame. acpc_aligned : bool Whether "mri" space is aligned to ACPC. overwrite : bool Whether to overwrite the existing file. Defaults to False. """ # write electrodes data for iEEG and EEG unit = "m" # defaults to meters if montage is None: montage = raw.get_montage() else: # prevent transformation back to "head", only should be used # in this specific circumstance if bids_path.datatype == 'ieeg': montage.remove_fiducials() raw.set_montage(montage) # get coordinate frame from digMontage digpoint = montage.dig[0] if any(digpoint['coord_frame'] != _digpoint['coord_frame'] for _digpoint in montage.dig): warn("Not all digpoints have the same coordinate frame. " "Skipping electrodes.tsv writing...") return # get the accepted mne-python coordinate frames coord_frame_int = int(digpoint['coord_frame']) mne_coord_frame = MNE_FRAME_TO_STR.get(coord_frame_int, None) coord_frame = MNE_TO_BIDS_FRAMES.get(mne_coord_frame, None) if bids_path.datatype == 'ieeg' and mne_coord_frame == 'mri': if acpc_aligned: coord_frame = 'ACPC' else: raise RuntimeError( '`acpc_aligned` is False, if your T1 is not aligned ' 'to ACPC and the coordinates are in fact in ACPC ' 'space there will be no way to relate the coordinates ' 'to the T1. If the T1 is ACPC-aligned, use ' '`acpc_aligned=True`') # create electrodes/coordsystem files using a subset of entities # that are specified for these files in the specification coord_file_entities = { 'root': bids_path.root, 'datatype': bids_path.datatype, 'subject': bids_path.subject, 'session': bids_path.session, 'acquisition': bids_path.acquisition, 'space': bids_path.space } datatype = bids_path.datatype electrodes_path = BIDSPath(**coord_file_entities, suffix='electrodes', extension='.tsv') coordsystem_path = BIDSPath(**coord_file_entities, suffix='coordsystem', extension='.json') logger.info(f'Writing electrodes file to... {electrodes_path}') logger.info(f'Writing coordsytem file to... {coordsystem_path}') if datatype == 'ieeg': if coord_frame is not None: # XXX: To improve when mne-python allows coord_frame='unknown' # coordinate frame is either coordsystem_path.update(space=coord_frame) electrodes_path.update(space=coord_frame) # Now write the data to the elec coords and the coordsystem _write_electrodes_tsv(raw, electrodes_path, datatype, overwrite) _write_coordsystem_json(raw=raw, unit=unit, hpi_coord_system='n/a', sensor_coord_system=(coord_frame, mne_coord_frame), fname=coordsystem_path, datatype=datatype, overwrite=overwrite) else: # default coordinate frame to mri if not available warn("Coordinate frame of iEEG coords missing/unknown " "for {}. Skipping reading " "in of montage...".format(electrodes_path)) elif datatype == 'eeg': # We only write EEG electrodes.tsv and coordsystem.json # if we have LPA, RPA, and NAS available to rescale to a known # coordinate system frame coords = _extract_landmarks(raw.info['dig']) landmarks = set(['RPA', 'NAS', 'LPA']) == set(list(coords.keys())) # XXX: to be improved to allow rescaling if landmarks are present # mne-python automatically converts unknown coord frame to head if coord_frame_int == FIFF.FIFFV_COORD_HEAD and landmarks: # Now write the data _write_electrodes_tsv(raw, electrodes_path, datatype, overwrite) _write_coordsystem_json(raw=raw, unit='m', hpi_coord_system='n/a', sensor_coord_system='CapTrak', fname=coordsystem_path, datatype=datatype, overwrite=overwrite) else: warn("Skipping EEG electrodes.tsv... " "Setting montage not possible if anatomical " "landmarks (NAS, LPA, RPA) are missing, " "and coord_frame is not 'head'.")
def _write_dig_bids(bids_path, raw, montage=None, acpc_aligned=False, overwrite=False): """Write BIDS formatted DigMontage from Raw instance. Handles coordsystem.json and electrodes.tsv writing from DigMontage. Parameters ---------- bids_path : BIDSPath Path in the BIDS dataset to save the ``electrodes.tsv`` and ``coordsystem.json`` file for. ``datatype`` attribute must be ``eeg``, or ``ieeg``. For ``meg`` data, ``electrodes.tsv`` are not saved. raw : mne.io.Raw The data as MNE-Python Raw object. montage : mne.channels.DigMontage | None The montage to use rather than the one in ``raw`` if it must be transformed from the "head" coordinate frame. acpc_aligned : bool Whether "mri" space is aligned to ACPC. overwrite : bool Whether to overwrite the existing file. Defaults to False. """ # write electrodes data for iEEG and EEG unit = "m" # defaults to meters if montage is None: montage = raw.get_montage() else: # assign montage to raw but supress any coordinate transforms montage = montage.copy() # don't modify original montage_coord_frame = montage.get_positions()['coord_frame'] fids = [ d for d in montage.dig # save to add back if d['kind'] == FIFF.FIFFV_POINT_CARDINAL ] montage.remove_fiducials() # prevent coordinate transform with warnings.catch_warnings(): warnings.filterwarnings(action='ignore', category=RuntimeWarning, message='.*nasion not found', module='mne') raw.set_montage(montage) for ch in raw.info['chs']: ch['coord_frame'] = MNE_STR_TO_FRAME[montage_coord_frame] for d in raw.info['dig']: d['coord_frame'] = MNE_STR_TO_FRAME[montage_coord_frame] with raw.info._unlock(): # add back fiducials raw.info['dig'] = fids + raw.info['dig'] # get the accepted mne-python coordinate frames coord_frame_int = int(montage.dig[0]['coord_frame']) mne_coord_frame = MNE_FRAME_TO_STR.get(coord_frame_int, None) coord_frame = MNE_TO_BIDS_FRAMES.get(mne_coord_frame, None) if coord_frame == 'CapTrak' and bids_path.datatype in ('eeg', 'nirs'): pos = raw.get_montage().get_positions() if any([pos[fid_key] is None for fid_key in ('nasion', 'lpa', 'rpa')]): raise RuntimeError("'head' coordinate frame must contain nasion " "and left and right pre-auricular point " "landmarks") if bids_path.datatype == 'ieeg' and bids_path.space in (None, 'ACPC') and \ mne_coord_frame == 'ras': if not acpc_aligned: raise RuntimeError( '`acpc_aligned` is False, if your T1 is not aligned ' 'to ACPC and the coordinates are in fact in ACPC ' 'space there will be no way to relate the coordinates ' 'to the T1. If the T1 is ACPC-aligned, use ' '`acpc_aligned=True`') coord_frame = 'ACPC' if bids_path.space is None: # no space, use MNE coord frame if coord_frame is None: # if no MNE coord frame, skip warn("Coordinate frame could not be inferred from the raw object " "and the BIDSPath.space was none, skipping the writing of " "channel positions") return else: # either a space and an MNE coord frame or just a space if coord_frame is None: # just a space, use that coord_frame = bids_path.space else: # space and raw have coordinate frame, check match if bids_path.space != coord_frame and not ( coord_frame == 'fsaverage' and bids_path.space == 'MNI305'): # fsaverage == MNI305 raise ValueError('Coordinates in the raw object or montage ' f'are in the {coord_frame} coordinate ' 'frame but BIDSPath.space is ' f'{bids_path.space}') # create electrodes/coordsystem files using a subset of entities # that are specified for these files in the specification coord_file_entities = { 'root': bids_path.root, 'datatype': bids_path.datatype, 'subject': bids_path.subject, 'session': bids_path.session, 'acquisition': bids_path.acquisition, 'space': None if bids_path.datatype == 'nirs' else coord_frame } channels_suffix = \ 'optodes' if bids_path.datatype == 'nirs' else 'electrodes' _channels_fun = _write_optodes_tsv if bids_path.datatype == 'nirs' else \ _write_electrodes_tsv channels_path = BIDSPath(**coord_file_entities, suffix=channels_suffix, extension='.tsv') coordsystem_path = BIDSPath(**coord_file_entities, suffix='coordsystem', extension='.json') # Now write the data to the elec coords and the coordsystem _channels_fun(raw, channels_path, bids_path.datatype, overwrite) _write_coordsystem_json(raw=raw, unit=unit, hpi_coord_system='n/a', sensor_coord_system=coord_frame, fname=coordsystem_path, datatype=bids_path.datatype, overwrite=overwrite)
def _write_dig_bids(electrodes_fname, coordsystem_fname, data_path, raw, kind, overwrite=False, verbose=True): """Write BIDS formatted DigMontage from Raw instance. Handles coordinatesystem.json and electrodes.tsv writing from DigMontage. Parameters ---------- electrodes_fname : str Filename to save the electrodes.tsv to. coordsystem_fname : str Filename to save the coordsystem.json to. data_path : str | pathlib.Path Path to the data directory raw : instance of Raw The data as MNE-Python Raw object. kind : str Type of the data as in ALLOWED_KINDS. overwrite : bool Whether to overwrite the existing file. Defaults to False. verbose : bool Set verbose output to true or false. """ # write electrodes data for iEEG and EEG unit = "m" # defaults to meters params = _parse_bids_filename(electrodes_fname, verbose) subject_id = params['sub'] session_id = params['ses'] acquisition = params['acq'] # get coordinate frame from digMontage digpoint = raw.info['dig'][0] if any(digpoint['coord_frame'] != _digpoint['coord_frame'] for _digpoint in raw.info['dig']): warn("Not all digpoints have the same coordinate frame. " "Skipping electrodes.tsv writing...") return # get the accepted mne-python coordinate frames coord_frame_int = int(digpoint['coord_frame']) mne_coord_frame = MNE_FRAME_TO_STR.get(coord_frame_int, None) coord_frame = MNE_TO_BIDS_FRAMES.get(mne_coord_frame, None) if verbose: print("Writing electrodes file to... ", electrodes_fname) print("Writing coordsytem file to... ", coordsystem_fname) if kind == "ieeg": if coord_frame is not None: # XXX: To improve when mne-python allows coord_frame='unknown' if coord_frame not in BIDS_IEEG_COORDINATE_FRAMES: coordsystem_fname = make_bids_basename( subject=subject_id, session=session_id, acquisition=acquisition, space=coord_frame, suffix='coordsystem.json', prefix=data_path) electrodes_fname = make_bids_basename(subject=subject_id, session=session_id, acquisition=acquisition, space=coord_frame, suffix='electrodes.tsv', prefix=data_path) coord_frame = 'Other' # Now write the data to the elec coords and the coordsystem _electrodes_tsv(raw, electrodes_fname, kind, overwrite, verbose) _coordsystem_json(raw, unit, 'n/a', coord_frame, coordsystem_fname, kind, overwrite, verbose) else: # default coordinate frame to mri if not available warn("Coordinate frame of iEEG coords missing/unknown " "for {}. Skipping reading " "in of montage...".format(electrodes_fname)) elif kind == 'eeg': # We only write EEG electrodes.tsv and coordsystem.json # if we have LPA, RPA, and NAS available to rescale to a known # coordinate system frame coords = _extract_landmarks(raw.info['dig']) landmarks = set(['RPA', 'NAS', 'LPA']) == set(list(coords.keys())) # XXX: to be improved to allow rescaling if landmarks are present # mne-python automatically converts unknown coord frame to head if coord_frame_int == FIFF.FIFFV_COORD_HEAD and landmarks: # Now write the data _electrodes_tsv(raw, electrodes_fname, kind, overwrite, verbose) _coordsystem_json(raw, 'm', 'RAS', 'CapTrak', coordsystem_fname, kind, overwrite, verbose) else: warn("Skipping EEG electrodes.tsv... " "Setting montage not possible if anatomical " "landmarks (NAS, LPA, RPA) are missing, " "and coord_frame is not 'head'.")