Precompute forward model results

We provide precomputed fluence and sensitivity files for the example datasets. These are created by this notebook and can be obtained through cedalion.datasets.get_precomputed_sensitivity.

The second part of the notebook visualizes the currently available sensitivities.

[1]:
# This cells setups the environment when executed in Google Colab.
try:
    import google.colab
    !curl -s https://raw.githubusercontent.com/ibs-lab/cedalion/dev/scripts/colab_setup.py -o colab_setup.py
    # Select branch with --branch "branch name" (default is "dev")
    %run colab_setup.py
except ImportError:
    pass
[2]:
# set to true, to actually compute fluence and sensitivity files
RUN_COMPUTATION = False
[3]:
import pyvista as pv
pv.set_jupyter_backend('static')

from functools import lru_cache
import os
from pathlib import Path

import xarray as xr

import cedalion.datasets
import cedalion.imagereco.forward_model as fw
from cedalion.io.forward_model import FluenceFile, load_Adot
import cedalion.vis.plot_sensitivity_matrix
from cedalion.geometry.landmarks import LandmarksBuilder1010

xr.set_options(display_expand_data=False);
[4]:
def compute_fluence_mcx(geo3d_snapped_ijk, meas_list, head, output_file):
    fwm = cedalion.imagereco.forward_model.ForwardModel(
        head, geo3d_snapped_ijk, meas_list
    )

    fwm.compute_fluence_mcx(output_file)


def compute_sensitivity(geo3d_snapped_ijk, meas_list, head, fluence_fname, sensitivity_fname):
    fwm = cedalion.imagereco.forward_model.ForwardModel(
        head, geo3d_snapped_ijk, meas_list
    )

    fwm.compute_sensitivity(fluence_fname, sensitivity_fname)

@lru_cache
def get_headmodel(model : str):
    if model == "colin27":
        SEG_DATADIR, mask_files, landmarks_file = (
            cedalion.datasets.get_colin27_segmentation()
        )
        PARCEL_FILE = cedalion.datasets.get_colin27_parcel_file()

        head_ijk = fw.TwoSurfaceHeadModel.from_surfaces(
            segmentation_dir=SEG_DATADIR,
            mask_files=mask_files,
            brain_surface_file=os.path.join(SEG_DATADIR, "mask_brain.obj"),
            scalp_surface_file=os.path.join(SEG_DATADIR, "mask_scalp.obj"),
            landmarks_ras_file=landmarks_file,
            parcel_file=PARCEL_FILE,
            brain_face_count=None,
            scalp_face_count=None,
        )

        head_ras = head_ijk.apply_transform(head_ijk.t_ijk2ras)
        builder = LandmarksBuilder1010(head_ras.scalp, head_ras.landmarks)
        head_ras.landmarks = builder.build()
        head_ijk = head_ras.apply_transform(head_ras.t_ras2ijk)

    elif model == "icbm152":
        SEG_DATADIR, mask_files, landmarks_file = (
        cedalion.datasets.get_icbm152_segmentation()
        )

        PARCEL_FILE = cedalion.datasets.get_icbm152_parcel_file()

        head_ijk = fw.TwoSurfaceHeadModel.from_surfaces(
            segmentation_dir=SEG_DATADIR,
            mask_files=mask_files,
            brain_surface_file=os.path.join(SEG_DATADIR, "mask_brain.obj"),
            scalp_surface_file=os.path.join(SEG_DATADIR, "mask_scalp.obj"),
            landmarks_ras_file=landmarks_file,
            parcel_file=PARCEL_FILE,
            brain_face_count=None,
            scalp_face_count=None,
        )

        head_ras = head_ijk.apply_transform(head_ijk.t_ijk2ras)
        builder = LandmarksBuilder1010(head_ras.scalp, head_ras.landmarks)
        head_ras.landmarks = builder.build()
        head_ijk = head_ras.apply_transform(head_ras.t_ras2ijk)
    else:
        raise ValueError("unknown head model")

    return head_ijk

def get_fnirs_dataset(dataset):
    if dataset == "fingertappingDOT":
        rec = cedalion.datasets.get_fingertappingDOT()
        return rec.geo3d, rec._measurement_lists["amp"]
    elif dataset == "fingertapping":
        rec = cedalion.datasets.get_fingertapping()
        return rec.geo3d, rec._measurement_lists["amp"]
    elif dataset == "nn22_resting":
        rec = cedalion.datasets.get_nn22_resting_state()
        return rec.geo3d, rec._measurement_lists["amp"]
    elif dataset == "ninja_cap_56x144":
        geo3d, landmarks, meas_list = cedalion.datasets.get_ninja_cap_probe()
        geo3d = xr.concat((geo3d, landmarks), dim="label")
        geo3d = geo3d.pint.quantify("mm")
        return geo3d, meas_list
    elif dataset == "ninja_uhd_cap_164x496":
        geo3d, landmarks, meas_list = cedalion.datasets.get_ninja_uhd_cap_probe()
        geo3d = xr.concat((geo3d, landmarks), dim="label")
        geo3d = geo3d.pint.quantify("mm")
        return geo3d, meas_list

def snap(head, geo3d, dataset):
    if dataset in ["fingertapping", "fingertappingDOT"]: # right handed
        geo3d_snapped_ijk = head.align_and_snap_to_scalp(geo3d)
    elif dataset in ["nn22_resting", "ninja_cap_56x144", "ninja_uhd_cap_164x496"]: # left handed
        geo3d_snapped_ijk = head.align_and_snap_to_scalp(geo3d, mode="general")

    return geo3d_snapped_ijk


def plot_sensitivity(dataset, headmodel, Adot=None):
    sensitivity_fname = f"sensitivity_{dataset}_{headmodel}.nc"

    if Adot is None:
        Adot = load_Adot(sensitivity_fname)

    head = get_headmodel(headmodel)

    geo3d, meas_list = get_fnirs_dataset(dataset)
    geo3d_snapped_ijk = snap(head, geo3d, dataset)

    plotter = cedalion.vis.plot_sensitivity_matrix.Main(
        sensitivity=Adot,
        brain_surface=head.brain,
        head_surface=head.scalp,
        labeled_points=geo3d_snapped_ijk,
    )
    plotter.plot(high_th=0, low_th=-3)
    plotter.plt.show()

def compute_fluence_and_sensitivity(dataset : str, headmodel : str):
    geo3d, meas_list = get_fnirs_dataset(dataset)
    head = get_headmodel(headmodel)

    geo3d_snapped_ijk = snap(head, geo3d, dataset)

    fluence_fname = f"fluence_{dataset}_{headmodel}.h5"
    sensitivity_fname = f"sensitivity_{dataset}_{headmodel}.nc"

    if not Path(fluence_fname).exists():
        print(f"computing fluence for {dataset} {headmodel}")
        compute_fluence_mcx(geo3d_snapped_ijk, meas_list, head, fluence_fname)
    else:
        print(f"fluence for {dataset} {headmodel} exists")
    if not Path(sensitivity_fname).exists():
        print(f"computing sensitivity for {dataset} {headmodel}")
        compute_sensitivity(geo3d_snapped_ijk, meas_list, head, fluence_fname, sensitivity_fname)
    else:
        print(f"sensitivity for {dataset} {headmodel} exists")
[5]:
if RUN_COMPUTATION:
    for dataset in ["fingertapping", "fingertappingDOT", "nn22_resting", "ninja_cap_56x144", "ninja_uhd_cap_164x496"]:
        for headmodel in ["colin27", "icbm152"]:
            compute_fluence_and_sensitivity(dataset, headmodel)
            plot_sensitivity(dataset, headmodel)

Visualize precomputed sensitivities

[6]:
for dataset in ["fingertapping", "fingertappingDOT", "nn22_resting", "ninja_cap_56x144", "ninja_uhd_cap_164x496"]:
        for headmodel in ["colin27", "icbm152"]:
            display(f"{dataset=} {headmodel=}")
            Adot = cedalion.datasets.get_precomputed_sensitivity(dataset, headmodel)
            plot_sensitivity(dataset, headmodel, Adot)
"dataset='fingertapping' headmodel='colin27'"
Downloading file 'sensitivity_fingertapping_colin27.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_fingertapping_colin27.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
/home/runner/work/cedalion/cedalion/src/cedalion/geometry/landmarks.py:242: UserWarning: WIP: distance calculation around ears
  warnings.warn("WIP: distance calculation around ears")
../../_images/examples_head_models_46_precompute_fluence_7_2.png
"dataset='fingertapping' headmodel='icbm152'"
Downloading file 'sensitivity_fingertapping_icbm152.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_fingertapping_icbm152.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
/home/runner/work/cedalion/cedalion/src/cedalion/geometry/landmarks.py:242: UserWarning: WIP: distance calculation around ears
  warnings.warn("WIP: distance calculation around ears")
../../_images/examples_head_models_46_precompute_fluence_7_5.png
"dataset='fingertappingDOT' headmodel='colin27'"
../../_images/examples_head_models_46_precompute_fluence_7_7.png
"dataset='fingertappingDOT' headmodel='icbm152'"
../../_images/examples_head_models_46_precompute_fluence_7_9.png
"dataset='nn22_resting' headmodel='colin27'"
../../_images/examples_head_models_46_precompute_fluence_7_11.png
"dataset='nn22_resting' headmodel='icbm152'"
Downloading file 'sensitivity_nn22_resting_icbm152.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_nn22_resting_icbm152.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
../../_images/examples_head_models_46_precompute_fluence_7_14.png
"dataset='ninja_cap_56x144' headmodel='colin27'"
Downloading file 'sensitivity_ninja_cap_56x144_colin27.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_ninja_cap_56x144_colin27.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
../../_images/examples_head_models_46_precompute_fluence_7_17.png
"dataset='ninja_cap_56x144' headmodel='icbm152'"
Downloading file 'sensitivity_ninja_cap_56x144_icbm152.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_ninja_cap_56x144_icbm152.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
../../_images/examples_head_models_46_precompute_fluence_7_20.png
"dataset='ninja_uhd_cap_164x496' headmodel='colin27'"
Downloading file 'sensitivity_ninja_uhd_cap_164x496_colin27.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_ninja_uhd_cap_164x496_colin27.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
../../_images/examples_head_models_46_precompute_fluence_7_23.png
"dataset='ninja_uhd_cap_164x496' headmodel='icbm152'"
Downloading file 'sensitivity_ninja_uhd_cap_164x496_icbm152.nc' from 'https://doc.ibs.tu-berlin.de/cedalion/datasets/v25.1.0/sensitivity_ninja_uhd_cap_164x496_icbm152.nc' to '/home/runner/.cache/cedalion/v25.1.0'.
../../_images/examples_head_models_46_precompute_fluence_7_26.png
[ ]: