Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Introduce mri_robust_template as option for longitudinal template generation #2165

Draft
wants to merge 27 commits into
base: prep_for/mri_robust_template
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
37fc070
:truck: Split longitudinal into anat and func \& :rotating_light: Lin…
shnizzedy Nov 13, 2024
9d09358
:wrench: Update config to allow choosing `mri_robust_template` for lo…
shnizzedy Nov 13, 2024
dc4c2df
:necktie: Use `max_iter` parameter for longitudinal template generation.
shnizzedy Nov 13, 2024
ae5be2c
:twisted_rightwards_arrows: Merge branch 'mri_robust_template' into f…
shnizzedy Nov 14, 2024
253ca80
:twisted_rightwards_arrows: Merge changes from #2160
shnizzedy Nov 15, 2024
c11c88f
:bug: Debug changes for longitudinal pipeline
shnizzedy Nov 15, 2024
af27165
:construction: WIP :sparkles: Drop in `mri_robust_template`
shnizzedy Nov 19, 2024
a5d2c60
:recycle: SSOT `check_creds_path`
shnizzedy Nov 20, 2024
1a90cc0
:necktie: Make cross-graph and cross-pool connections
shnizzedy Nov 21, 2024
322d660
:necktie: Convert xfms from `lta` to `mat`
shnizzedy Nov 21, 2024
342dae2
:necktie: Clarify longitudinal `xfm`s vs longitudinal `warp`s
shnizzedy Nov 23, 2024
7ce9361
:recycle: Differentiate longitudinal template images from session ima…
shnizzedy Nov 25, 2024
6ad7d55
:construction: :necktie: Enable subworkflow cross-graph connections
shnizzedy Dec 4, 2024
5b527a5
:necktie: Finalize longitudinal (FSL) connections
shnizzedy Dec 5, 2024
6e3da0d
:fire: Remove duplicate output rows
shnizzedy Dec 5, 2024
6339798
:necktie: Output longitudinal template images
shnizzedy Dec 5, 2024
1604b4f
:recycle: :art: SSOT `orientation_node`
shnizzedy Feb 3, 2025
1eacbe9
:necktie: Reorient `mri_robust_template` outputs
shnizzedy Feb 3, 2025
e7276e7
:art: Combine `get_cpac_provenance` + `check_prov_for_regtool` → `reg…
shnizzedy Feb 5, 2025
1a48bf3
:recycle: Refactor `ANTs_registration_connector` to make DRYer
shnizzedy Feb 5, 2025
78b2aa1
:recycle: Connect ANTs methods for longitudinal registration
shnizzedy Feb 7, 2025
087f1e3
:truck: Move `transform_derivative` into `registration.utils`
shnizzedy Feb 8, 2025
6162f2b
:necktie: Rewire ANTs registration + `mri_robust_template`
shnizzedy Feb 10, 2025
62451d2
:necktie: Update ANTs symmetric registration for longitudinal preproc…
shnizzedy Feb 13, 2025
870f43d
:truck: Longutudinal ↔ symtemplate to subject-level anat directory
shnizzedy Feb 14, 2025
0719a3e
:white_check_mark: Update test and signature re: docstring updates
shnizzedy Feb 17, 2025
5b1745a
:construction: WIP :recycle: :necktie: Refactor FSL registration for …
shnizzedy Feb 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Required positional parameter "wf" in input and output of `ingress_pipeconfig_paths` function, where a node to reorient templates is added to the `wf`.
- Required positional parameter "orientation" to `resolve_resolution`.
- Optional positional argument "cfg" to `create_lesion_preproc`.
- `mri_robust_template` for longitudinal template generation.
- `max_iter` parameter for longitudinal template generation.

### Changed

- Moved `pygraphviz` from requirements to `graphviz` optional dependencies group.
- Automatically tag untagged `subject_id` and `unique_id` as `!!str` when loading data config files.
- Made orientation configurable (was hard-coded as "RPI").
- Disabled variant image builds.
- Made `mri_robust_template` default implementation for longitudinal template generation.

### Fixed

Expand Down
10 changes: 3 additions & 7 deletions CPAC/alff/alff.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2024 C-PAC Developers
# Copyright (C) 2012-2025 C-PAC Developers

# This file is part of C-PAC.

Expand All @@ -23,9 +23,8 @@
from CPAC.alff.utils import get_opt_string
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.pipeline.nodeblock import nodeblock
from CPAC.registration.registration import apply_transform
from CPAC.registration.utils import apply_transform
from CPAC.utils.interfaces import Function
from CPAC.utils.utils import check_prov_for_regtool


def create_alff(wf_name="alff_workflow"):
Expand Down Expand Up @@ -320,10 +319,7 @@ def alff_falff(wf, cfg, strat_pool, pipe_num, opt=None):
def alff_falff_space_template(wf, cfg, strat_pool, pipe_num, opt=None):
outputs = {}
if strat_pool.check_rpool("desc-denoisedNofilt_bold"):
xfm_prov = strat_pool.get_cpac_provenance(
"from-bold_to-template_mode-image_xfm"
)
reg_tool = check_prov_for_regtool(xfm_prov)
reg_tool = strat_pool.reg_tool("from-bold_to-template_mode-image_xfm")

num_cpus = cfg.pipeline_setup["system_config"]["max_cores_per_participant"]

Expand Down
46 changes: 7 additions & 39 deletions CPAC/anat_preproc/anat_preproc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2023 C-PAC Developers
# Copyright (C) 2012-2025 C-PAC Developers

# This file is part of C-PAC.

Expand Down Expand Up @@ -34,7 +34,7 @@
wb_command,
)
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.pipeline.nodeblock import nodeblock
from CPAC.pipeline.nodeblock import nodeblock, NODEBLOCK_RETURN
from CPAC.utils.interfaces import Function
from CPAC.utils.interfaces.fsl import Merge as fslMerge

Expand Down Expand Up @@ -1227,15 +1227,7 @@ def freesurfer_fsl_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
wf.connect(node, out, convert_fs_T1_to_nifti, "in_file")

# 3dresample -orient RPI -inset brainmask.nii.gz -prefix brain_fs.nii.gz
reorient_fs_brainmask = pe.Node(
interface=afni.Resample(),
name=f"reorient_fs_brainmask_{node_id}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)
reorient_fs_brainmask.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
reorient_fs_brainmask.inputs.outputtype = "NIFTI_GZ"

reorient_fs_brainmask = cfg.orientation_node(f"reorient_fs_brainmask_{node_id}")
wf.connect(
convert_fs_brainmask_to_nifti, "out_file", reorient_fs_brainmask, "in_file"
)
Expand All @@ -1249,15 +1241,7 @@ def freesurfer_fsl_brain_connector(wf, cfg, strat_pool, pipe_num, opt):
wf.connect(reorient_fs_brainmask, "out_file", binarize_fs_brain, "in_file")

# 3dresample -orient RPI -inset T1.nii.gz -prefix head_fs.nii.gz
reorient_fs_T1 = pe.Node(
interface=afni.Resample(),
name=f"reorient_fs_T1_{node_id}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)
reorient_fs_T1.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
reorient_fs_T1.inputs.outputtype = "NIFTI_GZ"

reorient_fs_T1 = cfg.orientation_node(f"reorient_fs_T1_{node_id}")
wf.connect(convert_fs_T1_to_nifti, "out_file", reorient_fs_T1, "in_file")

# flirt -in head_fs.nii.gz -ref ${FSLDIR}/data/standard/MNI152_T1_1mm.nii.gz \
Expand Down Expand Up @@ -1447,22 +1431,14 @@ def mask_T2(wf_name="mask_T2"):
inputs=["T1w"],
outputs=["desc-preproc_T1w", "desc-reorient_T1w", "desc-head_T1w"],
)
def anatomical_init(wf, cfg, strat_pool, pipe_num, opt=None):
def anatomical_init(wf, cfg, strat_pool, pipe_num, opt=None) -> NODEBLOCK_RETURN:
anat_deoblique = pe.Node(interface=afni.Refit(), name=f"anat_deoblique_{pipe_num}")
anat_deoblique.inputs.deoblique = True

node, out = strat_pool.get_data("T1w")
wf.connect(node, out, anat_deoblique, "in_file")

anat_reorient = pe.Node(
interface=afni.Resample(),
name=f"anat_reorient_{pipe_num}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)
anat_reorient.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
anat_reorient.inputs.outputtype = "NIFTI_GZ"

anat_reorient = cfg.orientation_node(f"anat_reorient_{pipe_num}")
wf.connect(anat_deoblique, "out_file", anat_reorient, "in_file")

outputs = {
Expand Down Expand Up @@ -2262,15 +2238,7 @@ def anatomical_init_T2(wf, cfg, strat_pool, pipe_num, opt=None):
node, out = strat_pool.get_data("T2w")
wf.connect(node, out, T2_deoblique, "in_file")

T2_reorient = pe.Node(
interface=afni.Resample(),
name=f"T2_reorient_{pipe_num}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)
T2_reorient.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
T2_reorient.inputs.outputtype = "NIFTI_GZ"

T2_reorient = cfg.orientation_node(f"T2_reorient_{pipe_num}")
wf.connect(T2_deoblique, "out_file", T2_reorient, "in_file")

outputs = {
Expand Down
17 changes: 5 additions & 12 deletions CPAC/anat_preproc/lesion_preproc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019-2023 C-PAC Developers
# Copyright (C) 2019-2025 C-PAC Developers

# This file is part of C-PAC.

Expand All @@ -20,6 +20,7 @@

from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.utils.interfaces import Function
from CPAC.utils.nifti_utils import orientation_node


def inverse_lesion(lesion_path):
Expand Down Expand Up @@ -126,18 +127,10 @@ def create_lesion_preproc(cfg=None, wf_name="lesion_preproc"):
preproc.connect(lesion_deoblique, "out_file", outputnode, "refit")

# Anatomical reorientation
lesion_reorient = pe.Node(
interface=afni.Resample(),
name="lesion_reorient",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
node_name = "lesion_reorient"
lesion_reorient = (
cfg.orientation_node(node_name) if cfg else orientation_node(node_name, "RPI")
)

lesion_reorient.inputs.orientation = (
cfg.pipeline_setup["desired_orientation"] if cfg else "RPI"
)
lesion_reorient.inputs.outputtype = "NIFTI_GZ"

preproc.connect(lesion_deoblique, "out_file", lesion_reorient, "in_file")
preproc.connect(lesion_reorient, "out_file", outputnode, "reorient")

Expand Down
6 changes: 2 additions & 4 deletions CPAC/func_preproc/func_motion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2024 C-PAC Developers
# Copyright (C) 2012-2025 C-PAC Developers

# This file is part of C-PAC.

Expand Down Expand Up @@ -34,7 +34,6 @@
from CPAC.pipeline.nodeblock import nodeblock
from CPAC.pipeline.schema import valid_options
from CPAC.utils.interfaces.function import Function
from CPAC.utils.utils import check_prov_for_motion_tool


@nodeblock(
Expand Down Expand Up @@ -68,8 +67,7 @@
)
def calc_motion_stats(wf, cfg, strat_pool, pipe_num, opt=None):
"""Calculate motion statistics for motion parameters."""
motion_prov = strat_pool.get_cpac_provenance("desc-movementParameters_motion")
motion_correct_tool = check_prov_for_motion_tool(motion_prov)
motion_correct_tool = strat_pool.motion_tool("desc-movementParameters_motion")
coordinate_transformation = [
"filtered-coordinate-transformation",
"coordinate-transformation",
Expand Down
24 changes: 3 additions & 21 deletions CPAC/func_preproc/func_preproc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 C-PAC Developers
# Copyright (C) 2012-2025 C-PAC Developers

# This file is part of C-PAC.

Expand Down Expand Up @@ -521,16 +521,7 @@ def func_reorient(wf, cfg, strat_pool, pipe_num, opt=None):
node, out = strat_pool.get_data("bold")
wf.connect(node, out, func_deoblique, "in_file")

func_reorient = pe.Node(
interface=afni_utils.Resample(),
name=f"func_reorient_{pipe_num}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)

func_reorient.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
func_reorient.inputs.outputtype = "NIFTI_GZ"

func_reorient = cfg.orientation_node(f"func_reorient_{pipe_num}")
wf.connect(func_deoblique, "out_file", func_reorient, "in_file")

outputs = {
Expand Down Expand Up @@ -1283,16 +1274,7 @@ def bold_mask_anatomical_refined(wf, cfg, strat_pool, pipe_num, opt=None):
node, out = strat_pool.get_data("bold")
wf.connect(node, out, func_deoblique, "in_file")

func_reorient = pe.Node(
interface=afni_utils.Resample(),
name=f"raw_func_reorient_{pipe_num}",
mem_gb=0,
mem_x=(0.0115, "in_file", "t"),
)

func_reorient.inputs.orientation = cfg.pipeline_setup["desired_orientation"]
func_reorient.inputs.outputtype = "NIFTI_GZ"

func_reorient = cfg.orientation_node(f"raw_func_reorient_{pipe_num}")
wf.connect(func_deoblique, "out_file", func_reorient, "in_file")

wf.connect(func_reorient, "out_file", init_bold_mask, "inputspec.func")
Expand Down
1 change: 1 addition & 0 deletions CPAC/longitudinal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from CPAC.utils.docs import DOCS_URL_PREFIX

assert isinstance(__doc__, str)
__doc__ += f"""

See {DOCS_URL_PREFIX}/user/longitudinal
Expand Down
Loading