Skip to content

Commit

Permalink
ENH: Restore CIFTI-2 generation
Browse files Browse the repository at this point in the history
  • Loading branch information
effigies committed Nov 8, 2023
1 parent 7ceba97 commit 0d1be7a
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 136 deletions.
42 changes: 40 additions & 2 deletions fmriprep/workflows/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def init_single_subject_wf(subject_id: str):
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
from niworkflows.interfaces.bids import BIDSDataGrabber, BIDSInfo
from niworkflows.interfaces.nilearn import NILEARN_VERSION
from niworkflows.interfaces.utility import KeySelect
from niworkflows.utils.bids import collect_data
from niworkflows.utils.misc import fix_multi_T1w_source_name
from niworkflows.utils.spaces import Reference
Expand Down Expand Up @@ -333,7 +334,7 @@ def init_single_subject_wf(subject_id: str):

# Set up the template iterator once, if used
if config.workflow.level == "full":
if spaces.get_spaces(nonstandard=False, dim=(3,)):
if spaces.cached.get_spaces(nonstandard=False, dim=(3,)):
template_iterator_wf = init_template_iterator_wf(spaces=spaces)
workflow.connect([
(anat_fit_wf, template_iterator_wf, [
Expand All @@ -342,6 +343,34 @@ def init_single_subject_wf(subject_id: str):
]),
]) # fmt:skip

# Thread MNI152NLin6Asym standard outputs to CIFTI subworkflow, skipping
# the iterator, which targets only output spaces.
# This can lead to duplication in the working directory if people actually
# want MNI152NLin6Asym outputs, but we'll live with it.
if config.workflow.cifti_output:
from smriprep.interfaces.templateflow import TemplateFlowSelect

ref = Reference(
"MNI152NLin6Asym",
{"res": 2 if config.workflow.cifti_output == "91k" else 1},
)

select_MNI6_xfm = pe.Node(
KeySelect(fields=["anat2std_xfm"], key=ref.fullname),
name="select_MNI6",
run_without_submitting=True,
)
select_MNI6_tpl = pe.Node(
TemplateFlowSelect(template=ref.fullname, resolution=ref.spec['res']),
name="select_MNI6_tpl",
)
workflow.connect([
(anat_fit_wf, select_MNI6_xfm, [
("outputnode.anat2std_xfm", "anat2std_xfm"),
("outputnode.template", "keys"),
]),
]) # fmt:skip

if config.workflow.anat_only:
return clean_datasinks(workflow)

Expand All @@ -362,7 +391,6 @@ def init_single_subject_wf(subject_id: str):
f"{[e.method for e in fmap_estimators]}."
)

from niworkflows.interfaces.utility import KeySelect
from sdcflows import fieldmaps as fm
from sdcflows.workflows.base import init_fmap_preproc_wf

Expand Down Expand Up @@ -537,6 +565,16 @@ def init_single_subject_wf(subject_id: str):
]),
]) # fmt:skip

# Thread MNI152NLin6Asym standard outputs to CIFTI subworkflow, skipping
# the iterator, which targets only output spaces.
# This can lead to duplication in the working directory if people actually
# want MNI152NLin6Asym outputs, but we'll live with it.
if config.workflow.cifti_output:
workflow.connect([
(select_MNI6_xfm, bold_wf, [("anat2std_xfm", "inputnode.anat2mni6_xfm")]),
(select_MNI6_tpl, bold_wf, [("brain_mask", "inputnode.mni6_mask")]),
]) # fmt:skip

return clean_datasinks(workflow)


Expand Down
114 changes: 67 additions & 47 deletions fmriprep/workflows/bold/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ def init_bold_wf(
"white",
"midthickness",
"pial",
"sphere_reg_fsLR",
"thickness",
"anat_ribbon",
# Fieldmap registration
"fmap",
Expand All @@ -211,6 +213,9 @@ def init_bold_wf(
"std_cohort",
"std_t1w",
"std_mask",
# MNI152NLin6Asym warp, for CIFTI use
"anat2mni6_xfm",
"mni6_mask",
],
),
name="inputnode",
Expand Down Expand Up @@ -467,6 +472,68 @@ def init_bold_wf(
(bold_anat_wf, bold_surf_wf, [("outputnode.bold_file", "inputnode.bold_t1w")]),
]) # fmt:skip

if config.workflow.cifti_output:
from .resampling import init_bold_fsLR_resampling_wf, init_bold_grayords_wf

bold_MNI6_wf = init_bold_volumetric_resample_wf(
metadata=all_metadata[0],
fieldmap_id=fieldmap_id if not multiecho else None,
omp_nthreads=omp_nthreads,
name='bold_MNI6_wf',
)

bold_fsLR_resampling_wf = init_bold_fsLR_resampling_wf(
estimate_goodvoxels=config.workflow.project_goodvoxels,
grayord_density=config.workflow.cifti_output,
omp_nthreads=omp_nthreads,
mem_gb=mem_gb["resampled"],
)

bold_grayords_wf = init_bold_grayords_wf(
grayord_density=config.workflow.cifti_output,
mem_gb=mem_gb["resampled"],
repetition_time=all_metadata[0]["RepetitionTime"],
)

workflow.connect([
# Resample BOLD to MNI152NLin6Asym, may duplicate bold_std_wf above
(inputnode, bold_MNI6_wf, [
("mni6_mask", "inputnode.target_ref_file"),
("mni6_mask", "inputnode.target_mask"),
("anat2mni6_xfm", "inputnode.anat2std_xfm"),
("fmap_ref", "inputnode.fmap_ref"),
("fmap_coeff", "inputnode.fmap_coeff"),
("fmap_id", "inputnode.fmap_id"),
]),
(bold_fit_wf, bold_MNI6_wf, [
("outputnode.coreg_boldref", "inputnode.bold_ref_file"),
("outputnode.boldref2fmap_xfm", "inputnode.boldref2fmap_xfm"),
("outputnode.boldref2anat_xfm", "inputnode.boldref2anat_xfm"),
]),
(bold_native_wf, bold_MNI6_wf, [
("outputnode.bold_minimal", "inputnode.bold_file"),
("outputnode.motion_xfm", "inputnode.motion_xfm"),
]),
# Resample T1w-space BOLD to fsLR surfaces
(inputnode, bold_fsLR_resampling_wf, [
("white", "inputnode.white"),
("pial", "inputnode.pial"),
("midthickness", "inputnode.midthickness"),
("thickness", "inputnode.thickness"),
("sphere_reg_fsLR", "inputnode.sphere_reg_fsLR"),
("anat_ribbon", "inputnode.anat_ribbon"),
]),
(bold_anat_wf, bold_fsLR_resampling_wf, [
("outputnode.bold_file", "inputnode.bold_file"),
]),
(bold_MNI6_wf, bold_grayords_wf, [
("outputnode.bold_file", "inputnode.bold_std"),
]),
(bold_fsLR_resampling_wf, bold_grayords_wf, [
("outputnode.bold_fsLR", "inputnode.bold_fsLR"),
]),
]) # fmt:skip

bold_confounds_wf = init_bold_confs_wf(
mem_gb=mem_gb["largemem"],
metadata=all_metadata[0],
Expand Down Expand Up @@ -662,7 +729,6 @@ def init_func_preproc_wf(bold_file, has_fieldmap=False):
spaces = config.workflow.spaces
fmriprep_dir = str(config.execution.fmriprep_dir)
freesurfer_spaces = spaces.get_fs_spaces()
project_goodvoxels = config.workflow.project_goodvoxels and config.workflow.cifti_output

ref_file = bold_file
wf_name = _get_wf_name(ref_file, "func_preproc")
Expand Down Expand Up @@ -750,52 +816,6 @@ def init_func_preproc_wf(bold_file, has_fieldmap=False):
# SURFACES ##################################################################################

# CIFTI output
if config.workflow.cifti_output:
from .resampling import init_bold_fsLR_resampling_wf, init_bold_grayords_wf

bold_fsLR_resampling_wf = init_bold_fsLR_resampling_wf(
estimate_goodvoxels=project_goodvoxels,
grayord_density=config.workflow.cifti_output,
omp_nthreads=omp_nthreads,
mem_gb=mem_gb["resampled"],
)

bold_grayords_wf = init_bold_grayords_wf(
grayord_density=config.workflow.cifti_output,
mem_gb=mem_gb["resampled"],
repetition_time=metadata["RepetitionTime"],
)

# fmt:off
workflow.connect([
(inputnode, bold_fsLR_resampling_wf, [
("surfaces", "inputnode.surfaces"),
("morphometrics", "inputnode.morphometrics"),
("sphere_reg_fsLR", "inputnode.sphere_reg_fsLR"),
("anat_ribbon", "inputnode.anat_ribbon"),
]),
(bold_t1_trans_wf, bold_fsLR_resampling_wf, [
("outputnode.bold_t1", "inputnode.bold_file"),
]),
(bold_std_trans_wf, bold_grayords_wf, [
("outputnode.bold_std", "inputnode.bold_std"),
("outputnode.spatial_reference", "inputnode.spatial_reference"),
]),
(bold_fsLR_resampling_wf, bold_grayords_wf, [
("outputnode.bold_fsLR", "inputnode.bold_fsLR"),
]),
(bold_fsLR_resampling_wf, func_derivatives_wf, [
("outputnode.goodvoxels_mask", "inputnode.goodvoxels_mask"),
]),
(bold_fsLR_resampling_wf, outputnode, [
("outputnode.weights_text", "weights_text"),
]),
(bold_grayords_wf, outputnode, [
("outputnode.cifti_bold", "bold_cifti"),
("outputnode.cifti_metadata", "cifti_metadata"),
]),
])
# fmt:on

if spaces.get_spaces(nonstandard=False, dim=(3,)):
carpetplot_wf = init_carpetplot_wf(
Expand Down
Loading

0 comments on commit 0d1be7a

Please sign in to comment.