Volume-wise T2*/S0 estimation with t2smap
#
Use tedana.workflows.t2smap_workflow()
[DuPre et al., 2021] to calculate volume-wise T2*/S0,
as in Power et al. [2018] and Heunis et al. [2021].
import os
from glob import glob
import matplotlib.pyplot as plt
import numpy as np
from myst_nb import glue
from nilearn import image, plotting
from repo2data.repo2data import Repo2Data
from tedana import workflows
# Install the data if running locally, or point to cached data if running on neurolibre
DATA_REQ_FILE = os.path.join("../binder/data_requirement.json")
# Download data
repo2data = Repo2Data(DATA_REQ_FILE)
data_path = repo2data.install()
data_path = os.path.abspath(data_path[0])
---- repo2data starting ----
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/repo2data
Config from file :
../binder/data_requirement.json
Destination:
./../data/multi-echo-data-analysis
Info : ./../data/multi-echo-data-analysis already downloaded
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
func_dir = os.path.join(data_path, "func/")
data_files = [
os.path.join(
func_dir,
"sub-04570_task-rest_echo-1_space-scanner_desc-partialPreproc_bold.nii.gz",
),
os.path.join(
func_dir,
"sub-04570_task-rest_echo-2_space-scanner_desc-partialPreproc_bold.nii.gz",
),
os.path.join(
func_dir,
"sub-04570_task-rest_echo-3_space-scanner_desc-partialPreproc_bold.nii.gz",
),
os.path.join(
func_dir,
"sub-04570_task-rest_echo-4_space-scanner_desc-partialPreproc_bold.nii.gz",
),
]
echo_times = [12.0, 28.0, 44.0, 60.0]
mask_file = os.path.join(
func_dir, "sub-04570_task-rest_space-scanner_desc-brain_mask.nii.gz"
)
confounds_file = os.path.join(
func_dir, "sub-04570_task-rest_desc-confounds_timeseries.tsv"
)
out_dir = os.path.join(data_path, "fit")
workflows.t2smap_workflow(
data_files,
echo_times,
out_dir=out_dir,
mask=mask_file,
prefix="sub-04570_task-rest_space-scanner",
fittype="loglin",
fitmode="ts",
)
INFO t2smap:t2smap_workflow:252 Using output directory: /home/runner/work/multi-echo-data-analysis/multi-echo-data-analysis/data/multi-echo-data-analysis/fit
INFO t2smap:t2smap_workflow:278 Loading input data: ['/home/runner/work/multi-echo-data-analysis/multi-echo-data-analysis/data/multi-echo-data-analysis/func/sub-04570_task-rest_echo-1_space-scanner_desc-partialPreproc_bold.nii.gz', '/home/runner/work/multi-echo-data-analysis/multi-echo-data-analysis/data/multi-echo-data-analysis/func/sub-04570_task-rest_echo-2_space-scanner_desc-partialPreproc_bold.nii.gz', '/home/runner/work/multi-echo-data-analysis/multi-echo-data-analysis/data/multi-echo-data-analysis/func/sub-04570_task-rest_echo-3_space-scanner_desc-partialPreproc_bold.nii.gz', '/home/runner/work/multi-echo-data-analysis/multi-echo-data-analysis/data/multi-echo-data-analysis/func/sub-04570_task-rest_echo-4_space-scanner_desc-partialPreproc_bold.nii.gz']
INFO t2smap:t2smap_workflow:298 Using user-defined mask
INFO utils:make_adaptive_mask:198 Echo-wise intensity thresholds for adaptive mask: [258.33994278 180.98638476 134.6796175 91.51006253]
WARNING utils:make_adaptive_mask:227 734 voxels in user-defined mask do not have good signal. Removing voxels from mask.
INFO t2smap:t2smap_workflow:306 Computing adaptive T2* map
INFO t2smap:t2smap_workflow:319 Calculating model fit quality metrics
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/tedana/decay.py:541: RuntimeWarning: Mean of empty slice
rmse_map = np.nanmean(rmse, axis=1)
INFO t2smap:t2smap_workflow:331 Computing optimal combination
INFO combine:make_optcom:192 Optimally combining data with voxel- and volume-wise T2* estimates
INFO t2smap:t2smap_workflow:389 Workflow completed
out_files = sorted(glob(os.path.join(out_dir, "*")))
out_files = [os.path.basename(f) for f in out_files]
print("\n".join(out_files))
sub-04570_task-rest_space-scanner_S0map.nii.gz
sub-04570_task-rest_space-scanner_T2starmap.nii.gz
sub-04570_task-rest_space-scanner_dataset_description.json
sub-04570_task-rest_space-scanner_desc-confounds_timeseries.tsv
sub-04570_task-rest_space-scanner_desc-limited_S0map.nii.gz
sub-04570_task-rest_space-scanner_desc-limited_T2starmap.nii.gz
sub-04570_task-rest_space-scanner_desc-optcom_bold.nii.gz
sub-04570_task-rest_space-scanner_desc-rmse_statmap.nii.gz
sub-04570_task-rest_space-scanner_desc-tedana_registry.json
fig, axes = plt.subplots(figsize=(16, 16), nrows=3)
plotting.plot_carpet(
os.path.join(out_dir, "sub-04570_task-rest_space-scanner_desc-optcom_bold.nii.gz"),
axes=axes[0],
figure=fig,
)
axes[0].set_title("Optimally Combined Data", fontsize=20)
plotting.plot_carpet(
os.path.join(out_dir, "sub-04570_task-rest_space-scanner_T2starmap.nii.gz"),
axes=axes[1],
figure=fig,
)
axes[1].set_title("T2* Estimates", fontsize=20)
plotting.plot_carpet(
os.path.join(out_dir, "sub-04570_task-rest_space-scanner_S0map.nii.gz"),
axes=axes[2],
figure=fig,
)
axes[2].set_title("S0 Estimates", fontsize=20)
axes[0].xaxis.set_visible(False)
axes[1].xaxis.set_visible(False)
axes[0].spines["bottom"].set_visible(False)
axes[1].spines["bottom"].set_visible(False)
fig.tight_layout()
glue("figure_volumewise_t2ss0_carpets", fig, display=False)
fig, ax = plt.subplots(figsize=(16, 8))
plotting.plot_stat_map(
image.mean_img(
os.path.join(out_dir, "sub-04570_task-rest_space-scanner_T2starmap.nii.gz")
),
vmax=0.6,
draw_cross=False,
bg_img=None,
figure=fig,
axes=ax,
)
glue("figure_mean_volumewise_t2s", fig, display=False)
fig, ax = plt.subplots(figsize=(16, 8))
plotting.plot_stat_map(
image.mean_img(
os.path.join(out_dir, "sub-04570_task-rest_space-scanner_S0map.nii.gz")
),
vmax=8000,
draw_cross=False,
bg_img=None,
figure=fig,
axes=ax,
)
glue("figure_mean_volumewise_s0", fig, display=False)
fig, axes = plt.subplots(figsize=(16, 15), nrows=5)
plotting.plot_epi(
image.mean_img(data_files[0]),
draw_cross=False,
bg_img=None,
cut_coords=[-10, 0, 10, 20, 30, 40, 50, 60, 70],
display_mode="z",
axes=axes[0],
)
plotting.plot_epi(
image.mean_img(data_files[1]),
draw_cross=False,
bg_img=None,
cut_coords=[-10, 0, 10, 20, 30, 40, 50, 60, 70],
display_mode="z",
axes=axes[1],
)
plotting.plot_epi(
image.mean_img(data_files[2]),
draw_cross=False,
bg_img=None,
cut_coords=[-10, 0, 10, 20, 30, 40, 50, 60, 70],
display_mode="z",
axes=axes[2],
)
plotting.plot_epi(
image.mean_img(data_files[3]),
draw_cross=False,
bg_img=None,
cut_coords=[-10, 0, 10, 20, 30, 40, 50, 60, 70],
display_mode="z",
axes=axes[3],
)
plotting.plot_epi(
image.mean_img(
os.path.join(
out_dir, "sub-04570_task-rest_space-scanner_desc-optcom_bold.nii.gz"
)
),
draw_cross=False,
bg_img=None,
cut_coords=[-10, 0, 10, 20, 30, 40, 50, 60, 70],
display_mode="z",
axes=axes[4],
)
glue("figure_mean_echos_and_optcom", fig, display=False)
te30_tsnr = image.math_img(
"(np.nanmean(img, axis=3) / np.nanstd(img, axis=3)) * mask",
img=data_files[1],
mask=mask_file,
)
oc_tsnr = image.math_img(
"(np.nanmean(img, axis=3) / np.nanstd(img, axis=3)) * mask",
img=os.path.join(
out_dir, "sub-04570_task-rest_space-scanner_desc-optcom_bold.nii.gz"
),
mask=mask_file,
)
vmax = np.nanmax(np.abs(oc_tsnr.get_fdata()))
fig, axes = plt.subplots(figsize=(10, 8), nrows=2)
plotting.plot_stat_map(
te30_tsnr,
draw_cross=False,
bg_img=None,
threshold=0.1,
cut_coords=[0, 10, 10],
vmax=vmax,
symmetric_cbar=False,
axes=axes[0],
)
axes[0].set_title("TE30 TSNR", fontsize=16)
plotting.plot_stat_map(
oc_tsnr,
draw_cross=False,
bg_img=None,
threshold=0.1,
cut_coords=[0, 10, 10],
vmax=vmax,
symmetric_cbar=False,
axes=axes[1],
)
axes[1].set_title("Optimal Combination TSNR", fontsize=16)
glue("figure_t2snr_te30_and_optcom", fig, display=False)
<string>:1: RuntimeWarning: invalid value encountered in divide
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/nilearn/plotting/img_plotting.py:1317: UserWarning: Non-finite values detected. These values will be replaced with zeros.
safe_get_data(stat_map_img, ensure_finite=True),
fig, ax = plt.subplots(figsize=(16, 8))
plotting.plot_carpet(
data_files[1],
axes=ax,
)
glue("figure_echo3_carpet", fig, display=False)
fig, ax = plt.subplots(figsize=(16, 8))
plotting.plot_carpet(
os.path.join(out_dir, "sub-04570_task-rest_space-scanner_desc-optcom_bold.nii.gz"),
axes=ax,
)
glue("figure_carpet_optcom", fig, display=False)