Skip to content

Commit

Permalink
Correct documentation regarding Freesurfer (PennLINC#1277)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsalo authored Oct 3, 2024
1 parent b766391 commit 68caadf
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ orbs:
.dockersetup:
&dockersetup
docker:
- image: pennlinc/xcp_d_build:0.0.12
- image: pennlinc/xcp_d_build:0.0.19
working_directory: /src/xcp_d

runinstall:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM pennlinc/xcp_d_build:0.0.12
FROM pennlinc/xcp_d_build:0.0.19

# Install xcp_d
COPY . /src/xcp_d
Expand Down
1 change: 0 additions & 1 deletion docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,6 @@ A Docker container can be created using the following command:
-v /dset/derivatives/fmriprep:/fmriprep:ro \
-v /tmp/wkdir:/work:rw \
-v /dset/derivatives/xcp_d:/out:rw \
-v /dset/derivatives/freesurfer:/freesurfer:ro \ # Necessary for fMRIPrep versions <22.0.2
pennlinc/xcp_d:latest \
/fmriprep /out participant \
--file-format cifti --despike --head_radius 40 -w /work --smoothing 6
Expand Down
4 changes: 0 additions & 4 deletions docs/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,6 @@ Surface normalization
If the ``--warp-surfaces-native2std`` flag is used,
then fsnative surface files from the preprocessing derivatives will be warped to fsLR-32k space.

.. important::

This step will only succeed if FreeSurfer derivatives are also available.


Identification of high-motion outlier volumes
=============================================
Expand Down
2 changes: 1 addition & 1 deletion xcp_d/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ class workflow(_Config):
dcan_correlation_lengths = None
"""Produce correlation matrices limited to each requested amount of time."""
process_surfaces = None
"""Warp FreeSurfer's surfaces to the MNI space."""
"""Warp fsnative-space surfaces to the MNI space."""
abcc_qc = None
"""Run DCAN QC."""
linc_qc = None
Expand Down
61 changes: 39 additions & 22 deletions xcp_d/interfaces/ants.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class ConvertTransformFile(CommandLine):
output_spec = _ConvertTransformFileOutputSpec


class _CompositeInvTransformUtilInputSpec(ANTSCommandInputSpec):
"""Input specification for CompositeInvTransformUtil."""
class _CompositeTransformUtilInputSpec(ANTSCommandInputSpec):
"""Input specification for CompositeTransformUtil."""

process = traits.Enum(
"assemble",
Expand All @@ -67,6 +67,11 @@ class _CompositeInvTransformUtilInputSpec(ANTSCommandInputSpec):
usedefault=True,
desc="What to do with the transform inputs (assemble or disassemble)",
)
inverse = traits.Bool(
False,
usedefault=True,
desc="Whether to invert the order of the transform components. Not used by the command.",
)
out_file = File(
exists=False,
argstr="%s",
Expand All @@ -89,32 +94,32 @@ class _CompositeInvTransformUtilInputSpec(ANTSCommandInputSpec):
)


class _CompositeInvTransformUtilOutputSpec(TraitedSpec):
"""Output specification for CompositeInvTransformUtil."""
class _CompositeTransformUtilOutputSpec(TraitedSpec):
"""Output specification for CompositeTransformUtil."""

affine_transform = File(desc="Affine transform component")
displacement_field = File(desc="Displacement field component")
out_file = File(desc="Compound transformation file")


class CompositeInvTransformUtil(ANTSCommand):
class CompositeTransformUtil(ANTSCommand):
"""Wrapper for the ANTS CompositeTransformUtil command.
ANTs utility which can combine or break apart transform files into their individual
constituent components.
Examples
--------
>>> from nipype.interfaces.ants import CompositeInvTransformUtil
>>> tran = CompositeInvTransformUtil()
>>> from nipype.interfaces.ants import CompositeTransformUtil
>>> tran = CompositeTransformUtil()
>>> tran.inputs.process = 'disassemble'
>>> tran.inputs.in_file = 'output_Composite.h5'
>>> tran.cmdline
'CompositeTransformUtil --disassemble output_Composite.h5 transform'
>>> tran.run() # doctest: +SKIP
example for assembling transformation files
>>> from nipype.interfaces.ants import CompositeInvTransformUtil
>>> tran = CompositeInvTransformUtil()
>>> from nipype.interfaces.ants import CompositeTransformUtil
>>> tran = CompositeTransformUtil(inverse=False)
>>> tran.inputs.process = 'assemble'
>>> tran.inputs.out_file = 'my.h5'
>>> tran.inputs.in_file = ['AffineTransform.mat', 'DisplacementFieldTransform.nii.gz']
Expand All @@ -125,13 +130,13 @@ class CompositeInvTransformUtil(ANTSCommand):
"""

_cmd = "CompositeTransformUtil"
input_spec = _CompositeInvTransformUtilInputSpec
output_spec = _CompositeInvTransformUtilOutputSpec
input_spec = _CompositeTransformUtilInputSpec
output_spec = _CompositeTransformUtilOutputSpec

def _num_threads_update(self):
"""Do not update the number of threads environment variable.
CompositeInvTransformUtil ignores environment variables,
CompositeTransformUtil ignores environment variables,
so override environment update from ANTSCommand class.
"""
pass
Expand All @@ -141,19 +146,31 @@ def _format_arg(self, name, spec, value):
return ""
if name == "out_file" and self.inputs.process == "disassemble":
return ""
return super(CompositeInvTransformUtil, self)._format_arg(name, spec, value)
return super(CompositeTransformUtil, self)._format_arg(name, spec, value)

def _list_outputs(self):
outputs = self.output_spec().get()
if self.inputs.process == "disassemble":
outputs["affine_transform"] = os.path.abspath(
f"01_{self.inputs.output_prefix}_AffineTransform.mat"
)
outputs["displacement_field"] = os.path.abspath(
f"00_{self.inputs.output_prefix}_DisplacementFieldTransform.nii.gz"
)
if self.inputs.process == "assemble":
outputs["out_file"] = os.path.abspath(self.inputs.out_file)
if self.inputs.inverse:
if self.inputs.process == "disassemble":
outputs["affine_transform"] = os.path.abspath(
f"{self.inputs.output_prefix}_01_AffineTransform.mat"
)
outputs["displacement_field"] = os.path.abspath(
f"{self.inputs.output_prefix}_00_DisplacementFieldTransform.nii.gz"
)
elif self.inputs.process == "assemble":
outputs["out_file"] = os.path.abspath(self.inputs.out_file)
else:
if self.inputs.process == "disassemble":
outputs["affine_transform"] = os.path.abspath(
f"{self.inputs.output_prefix}_00_AffineTransform.mat"
)
outputs["displacement_field"] = os.path.abspath(
f"{self.inputs.output_prefix}_01_DisplacementFieldTransform.nii.gz"
)
elif self.inputs.process == "assemble":
outputs["out_file"] = os.path.abspath(self.inputs.out_file)

return outputs


Expand Down
22 changes: 15 additions & 7 deletions xcp_d/utils/atlas.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ def get_atlas_nifti(atlas):

from xcp_d.data import load as load_data

if "4S" in atlas or atlas in ("Glasser", "Gordon"):
if "4S" in atlas:
# 1 mm3 atlases
atlas_fname = f"atlas-{atlas}_space-MNI152NLin6Asym_res-01_dseg.nii.gz"
tsv_fname = f"atlas-{atlas}_dseg.tsv"
elif atlas in ("Glasser", "Gordon"):
# 1 mm3 atlases
atlas_fname = f"tpl-MNI152NLin6Asym_atlas-{atlas}_res-01_dseg.nii.gz"
tsv_fname = f"atlas-{atlas}_dseg.tsv"
Expand All @@ -92,9 +96,11 @@ def get_atlas_nifti(atlas):
tsv_fname = f"atlas-{atlas}_dseg.tsv"

if "4S" in atlas:
atlas_file = join("/AtlasPack", atlas_fname)
atlas_labels_file = join("/AtlasPack", tsv_fname)
atlas_metadata_file = f"/AtlasPack/tpl-MNI152NLin6Asym_atlas-{atlas}_dseg.json"
atlas_file = join(f"/AtlasPack/atlas-{atlas}", atlas_fname)
atlas_labels_file = join(f"/AtlasPack/atlas-{atlas}", tsv_fname)
atlas_metadata_file = (
f"/AtlasPack/atlas-{atlas}/atlas-{atlas}_space-MNI152NLin6Asym_res-01_dseg.json"
)
else:
atlas_file = str(load_data(f"atlases/{atlas_fname}"))
atlas_labels_file = str(load_data(f"atlases/{tsv_fname}"))
Expand Down Expand Up @@ -139,9 +145,11 @@ def get_atlas_cifti(atlas):
from xcp_d.data import load as load_data

if "4S" in atlas:
atlas_file = f"/AtlasPack/tpl-fsLR_atlas-{atlas}_den-91k_dseg.dlabel.nii"
atlas_labels_file = f"/AtlasPack/atlas-{atlas}_dseg.tsv"
atlas_metadata_file = f"/AtlasPack/tpl-fsLR_atlas-{atlas}_dseg.json"
atlas_file = f"/AtlasPack/atlas-{atlas}/atlas-{atlas}_space-fsLR_den-91k_dseg.dlabel.nii"
atlas_labels_file = f"/AtlasPack/atlas-{atlas}/atlas-{atlas}_dseg.tsv"
atlas_metadata_file = (
f"/AtlasPack/atlas-{atlas}/atlas-{atlas}_space-fsLR_den-91k_dseg.json"
)
elif "MIDB" in atlas:
atlas_file = str(
load_data("atlases/tpl-fsLR_atlas-MIDB_den-32k_desc-abcdThresh75_dseg.dlabel.nii")
Expand Down
69 changes: 0 additions & 69 deletions xcp_d/utils/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,75 +948,6 @@ def _get_tr(img):
return img.header.get_zooms()[-1]


def get_segmentation_software(fmri_dir):
"""Find FreeSurfer or MCRIBS derivatives associated with preprocessing pipeline.
NOTE: This is a Node function.
Parameters
----------
fmri_dir : :obj:`str`
Path to preprocessed derivatives.
Returns
-------
software
Raises
------
ValueError
If more than one potential FreeSurfer derivative folder is found.
NotADirectoryError
If no FreeSurfer derivatives are found.
"""
import os

from nipype import logging

LOGGER = logging.getLogger("nipype.utils")

patterns = {
"Nibabies >= 24.0.0a1": (
os.path.join(fmri_dir, "sourcedata/mcribs"),
"MCRIBS",
),
"fMRIPrep >= 20.2.1": (
os.path.join(fmri_dir, "sourcedata/freesurfer"),
"FreeSurfer",
),
"Nibabies >= 21.0.0": (
os.path.join(fmri_dir, "sourcedata/infant-freesurfer"),
"FreeSurfer",
),
"fMRIPrep < 20.2.1": (
os.path.join(os.path.dirname(fmri_dir), "freesurfer"),
"FreeSurfer",
),
"Nibabies < 21.0.0": (
os.path.join(os.path.dirname(fmri_dir), "infant-freesurfer"),
"FreeSurfer",
),
}

for desc, key in patterns.items():
pattern, software = key
if os.path.isdir(pattern):
LOGGER.info(
f"{software} derivatives associated with {desc} preprocessing derivatives found "
f"at {pattern}"
)
return software

# Otherwise, continue to the next pattern

seg_patterns = [pattern[0] for pattern in patterns.values()]
patterns_str = "\n\t".join(seg_patterns)
raise NotADirectoryError(
"No FreeSurfer/MCRIBS derivatives found in any of the following locations:"
f"\n\t{patterns_str}"
)


def get_entity(filename, entity):
"""Extract a given entity from a BIDS filename via string manipulation.
Expand Down
9 changes: 5 additions & 4 deletions xcp_d/workflows/anatomical/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

from nipype import logging
from nipype.interfaces import utility as niu
from nipype.interfaces.ants import CompositeTransformUtil # MB
from nipype.pipeline import engine as pe
from niworkflows.engine.workflows import LiterateWorkflow as Workflow

from xcp_d import config
from xcp_d.interfaces.ants import CompositeInvTransformUtil, ConvertTransformFile
from xcp_d.interfaces.ants import CompositeTransformUtil, ConvertTransformFile
from xcp_d.interfaces.bids import CollectRegistrationFiles, DerivativesDataSink
from xcp_d.interfaces.c3 import C3d # TM
from xcp_d.interfaces.nilearn import BinaryMath, Merge
Expand Down Expand Up @@ -661,6 +660,7 @@ def init_ants_xfm_to_fsl_wf(mem_gb, name="ants_xfm_to_fsl_wf"):
# Use ANTs CompositeTransformUtil to separate the .h5 into affine and warpfield xfms.
disassemble_h5 = pe.Node(
CompositeTransformUtil(
inverse=False,
process="disassemble",
output_prefix="T1w_to_MNI152NLin6Asym",
),
Expand All @@ -671,10 +671,11 @@ def init_ants_xfm_to_fsl_wf(mem_gb, name="ants_xfm_to_fsl_wf"):

# Nipype's CompositeTransformUtil assumes a certain file naming and
# concatenation order of xfms which does not work for the inverse .h5,
# so we use our modified class, "CompositeInvTransformUtil"
# so we use our modified class with an additional inverse flag.
disassemble_h5_inv = pe.Node(
CompositeInvTransformUtil(
CompositeTransformUtil(
process="disassemble",
inverse=True,
output_prefix="MNI152NLin6Asym_to_T1w",
),
name="disassemble_h5_inv",
Expand Down

0 comments on commit 68caadf

Please sign in to comment.