Template space¶
from laion_fmri.subject import load_subject
sub = load_subject("sub-03")
mni305 = sub.to_template(per_voxel_array, "MNI305")
fsavg_L = sub.to_template(per_voxel_array, "fsaverage", hemi="L")
Subject.to_template projects subject-T1w-space values into
a volume or surface template space using the FreeSurfer recon
that ships with each subject. Two chains:
Volume chain – T1w volume to MNI305 via
mri/transforms/talairach.lta(FreeSurfer’s linear T1w to MNI305 affine; thetalairachfilename is historical and refers to MNI305, not true Talairach space).Surface chain – T1w volume to fsnative surface via
nilearn.surface.vol_to_surfon the recon’s white / pial meshes, then fsnative to fsaverage vianitransforms.surface.SurfaceResampleron the recon’ssphere.reg.
Setup¶
The template-space module needs an opt-in extra:
uv sync --extra template # or: pip install laion-fmri[template]
This pulls nilearn, nitransforms, and templateflow.
TemplateFlow caches reference images at
~/.cache/templateflow/ on first use.
The per-subject FreeSurfer recon is not fetched by default
because it adds several hundred megabytes per subject. Pull it
explicitly with include_freesurfer=True:
from laion_fmri.download import download
download(subject="sub-03", include_freesurfer=True, n_jobs=4)
You can check whether the recon is on disk with
sub.has_freesurfer().
Supported targets¶
Pass a target name as the second argument to to_template:
Target |
Type |
Mechanism |
|---|---|---|
|
surface |
T1w to fsnative ( |
|
volume |
Direct affine from the recon’s |
MNI152 variants and MNIColin27 are not supported here. Run
fmriprep externally if you need them.
Volumetric accuracy¶
The MNI305 hop is a 12-parameter linear affine from the
subject’s FreeSurfer recon (mri/transforms/talairach.lta).
For sub-millimetre cortical alignment, prefer the
"fsaverage" surface target – surface registration via the
recon’s sphere.reg aligns by cortical topology and is much
sharper than any volumetric affine at the cortical sheet.
Return shapes¶
Surface target, single hemi: 1-D
ndarrayover the vertices of the requested density.Surface target, no hemi:
{"L": ndarray, "R": ndarray}.Volume target:
nibabel.Nifti1Imageon the templateflow reference grid for that target.
Worked example¶
from laion_fmri.download import download
from laion_fmri.subject import load_subject
download(subject="sub-03", ses="ses-01",
include_freesurfer=True, n_jobs=4)
sub = load_subject("sub-03")
betas = sub.get_betas(session="ses-01", roi="visual")
mean_b = betas.mean(axis=0) # (n_voxels,)
# Surface: per-hemi fsaverage5 (10k vertices) array.
fsavg_L = sub.to_template(mean_b, "fsaverage", hemi="L")
fsavg_R = sub.to_template(mean_b, "fsaverage", hemi="R")
fsavg_both = sub.to_template(mean_b, "fsaverage") # {"L": ..., "R": ...}
# Volume: MNI305 (includes subcortex).
mni305 = sub.to_template(mean_b, "MNI305")
Explicit per-direction entry points¶
Subject.to_template accepts any volume input and dispatches
on the target name. Three more specific methods make the
input → output direction explicit and refuse mismatched targets
up front:
# Volume input -> volume target.
sub.volume_to_template(mean_b, "MNI305")
# Volume input -> surface target (fsaverage).
sub.volume_to_surface(mean_b, hemi="L")
sub.volume_to_surface(mean_b) # dict of L+R
# fsnative-surface input -> fsaverage.
roi_L = sub.get_roi_data("FFA1",
format="func.gii", hemi="L")
sub.surface_to_template(roi_L["FFA1"]["gii"]["hemi-L"]["func.gii"],
hemi="L")
sub.surface_to_template({"L": lh_array, "R": rh_array})
surface_to_template accepts either a single hemisphere
array (with hemi="L" or "R") or a {"L": ..., "R": ...}
dict; the return shape matches the input shape. The input
array’s length must equal the recon’s fsnative vertex count
for that hemisphere – a ValueError is raised otherwise.
Writing BIDS-conformant files¶
Pass output_dir= to also persist the result to disk under a
BIDS filename. Optional desc and session kwargs fill
the corresponding tokens:
sub.to_template(
mean_b, "MNI305",
output_dir="./out",
desc="MeanBeta",
session="ses-01",
)
The filename pattern is:
sub-{ID}[_ses-{S}]_space-{SPACE}[_den-{D}][_hemi-{L|R}][_desc-{DESC}]_statmap.{ext}
.nii.gz for volume targets, .func.gii for surface
targets (one file per hemisphere). The density token (den-)
uses BIDS-recommended labels (10k for fsaverage5,
41k for fsaverage6, 164k for fsaverage7).
Concrete examples for the call above:
sub-03_ses-01_space-MNI305_desc-MeanBeta_statmap.nii.gzsub-03_ses-01_space-fsaverage_den-10k_hemi-L_desc-MeanBeta_statmap.func.gii
When hemi=None on a surface target, two files are written
(one per hemisphere) and to_template returns the matching
{"L": ..., "R": ...} dict.