02. Convert Coordinate Frames of Electrodes

When working with intracranial electrophysiological data in the iEEG-BIDS format, we usually have iEEG coordinate data either in voxel, or real world coordinates space (xyz coordinates). Then within xyz coordinates, it can either be RAS, or tkRAS if one uses FreeSurfer.

In this tutorial, we show how to quickly use the Sensors data class and quickly go back and forth between coordinate frames using convert_elec_coords.

We assume that you have already localized the electrodes and coregistered them over to the T1w image FreeSurfer space.

# Authors: Adam Li <adam2392@gmail.com>
#
# License: BSD (3-clause)

Imports

We are importing everything we need for this example:

from pathlib import Path

from mne_bids import BIDSPath

from seek_localize import read_dig_bids, convert_coord_units, convert_coord_space

We will be using the testing dataset, which is already stored in BIDS format and stored with the seek-localize repository.

bids_root = (Path.cwd() / Path("../data/")).absolute()
subjects_dir = bids_root / "derivatives" / "freesurfer"

Now it’s time to get ready for labeling some of the data! First, we need to create a mne_bids.BIDSPath(), which will point to the corresponding *electrodes.tsv file.

subject = "la02"
session = "presurgery"
acquisition = "seeg"
space = "fs"
suffix = "electrodes"
extension = ".tsv"
datatype = "ieeg"
electrodes_fpath = BIDSPath(
    root=bids_root,
    datatype=datatype,
    subject=subject,
    session=session,
    acquisition=acquisition,
    space=space,
    suffix=suffix,
    extension=extension,
)

# the full file path to the electrodes.tsv file
print(electrodes_fpath.fpath)

Out:

/home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv

The necessary iEEG files are the sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv, sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json files. Note these are co-occurring files in iEEG-BIDS (one present requires the other to be present).

coordsystem_fpath = electrodes_fpath.copy().update(
    suffix="coordsystem", extension=".json"
)
print(coordsystem_fpath.fpath)

Out:

/home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json

Let’s load in the electrode coordinates as an instance of the seek_localize.Sensors class. Rather then instantiating the class directly, we use seek_localize.read_dig_bids to read in the correct data. This will perform extra work, such as figuring out the full path to the IntendedFor volumetric image. The image corresponds to the coordinate space to interpret the electrode coordinates in (e.g. a T1w image in FreeSurfer space).

sensors = read_dig_bids(electrodes_fpath, root=bids_root)
print(sensors)

Out:

SETTING COORDINATE SYSTEM AS MRI by default if coordinatesystem is "other".
<Sensors | 9 non-empty values
 ch_names: L'1, L'2, L'3, L'4, L'5, L'6, L'7, L'8, L'11, L'12, L'13, L'14, ...
 coord_system: mri
 coord_unit: mm
 coordsystem_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json
 elecs_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv
 intended_for: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii
 x: 88 items (list)
 y: 88 items (list)
 z: 88 items (list)
>

The data already saved was originally written in 'mm', so we can convert to voxel space denoted by the mri coordinate frame. This is in-line with how MNE does things

Out:

Converting coordinates from mm to voxel using /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii.
<Sensors | 9 non-empty values
 ch_names: L'1, L'2, L'3, L'4, L'5, L'6, L'7, L'8, L'11, L'12, L'13, L'14, ...
 coord_system: mri
 coord_unit: voxel
 coordsystem_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json
 elecs_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv
 intended_for: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii
 x: 88 items (ndarray)
 y: 88 items (ndarray)
 z: 88 items (ndarray)
>

We could convert it to mm.

Out:

../seek_localize/coordsystem.py:220: RuntimeWarning: Rounding when to_unit is mm and not voxel is not recommended.
  f"Rounding when to_unit is {to_unit} " f"and not voxel is not recommended."
Converting coordinates from voxel to mm using /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii.
<Sensors | 9 non-empty values
 ch_names: L'1, L'2, L'3, L'4, L'5, L'6, L'7, L'8, L'11, L'12, L'13, L'14, ...
 coord_system: mri
 coord_unit: mm
 coordsystem_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json
 elecs_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv
 intended_for: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii
 x: 88 items (ndarray)
 y: 88 items (ndarray)
 z: 88 items (ndarray)
>

The data was originally saved according to the mri space, intended for the T1.mgz image in FreeSurfer. One can also use seek_localize to transform to standard coordinate spaces, such as tkras and mni.

# We could convert it to ``tkras``.
sensors_tkras = convert_coord_space(sensors_vox, to_frame="tkras")
print(sensors_tkras)

# We could convert it to ``mni``.
sensors_mni = convert_coord_space(
    sensors_vox, to_frame="mni", subjects_dir=subjects_dir
)
print(sensors_mni)

# We could convert it to back to ``mri``.
sensors_mri = convert_coord_space(sensors_vox, to_frame="mri")
print(sensors_mri)

Out:

../seek_localize/coordsystem.py:285: RuntimeWarning: Unable to programmatically get vox2ras TKR from /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii, so setting manually.
  f"Unable to programmatically get vox2ras TKR "
Using Vox2TKRAS affine: [[-1.0, 0.0, 0.0, 128.0], [0.0, 0.0, 1.0, -128.0], [0.0, -1.0, 0.0, 128.0], [0.0, 0.0, 0.0, 1.0]].
<Sensors | 9 non-empty values
 ch_names: L'1, L'2, L'3, L'4, L'5, L'6, L'7, L'8, L'11, L'12, L'13, L'14, ...
 coord_system: tkras
 coord_unit: mm
 coordsystem_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json
 elecs_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv
 intended_for: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii
 x: 88 items (ndarray)
 y: 88 items (ndarray)
 z: 88 items (ndarray)
>
<Sensors | 9 non-empty values
 ch_names: L'1, L'2, L'3, L'4, L'5, L'6, L'7, L'8, L'11, L'12, L'13, L'14, ...
 coord_system: mni
 coord_unit: voxel
 coordsystem_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json
 elecs_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv
 intended_for: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii
 x: 88 items (ndarray)
 y: 88 items (ndarray)
 z: 88 items (ndarray)
>
<Sensors | 9 non-empty values
 ch_names: L'1, L'2, L'3, L'4, L'5, L'6, L'7, L'8, L'11, L'12, L'13, L'14, ...
 coord_system: mri
 coord_unit: voxel
 coordsystem_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_coordsystem.json
 elecs_fname: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/ieeg/sub-la02_ses-presurgery_acq-seeg_space-fs_electrodes.tsv
 intended_for: /home/docs/checkouts/readthedocs.org/user_builds/seek-localize/checkouts/stable/examples/../data/sub-la02/ses-presurgery/anat/sub-la02_ses-presurgery_space-fs_T1w.nii
 x: 88 items (ndarray)
 y: 88 items (ndarray)
 z: 88 items (ndarray)
>

Total running time of the script: ( 0 minutes 0.255 seconds)

Gallery generated by Sphinx-Gallery