Skip to content

Commit

Permalink
Merge pull request #359 from mgxd/enh/split-surfaces
Browse files Browse the repository at this point in the history
ENH: Separate surfaces and morphometrics into standalone outputs
  • Loading branch information
mgxd authored Aug 24, 2023
2 parents 23f9dc4 + cf49b5b commit 5e60fd8
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 3 deletions.
41 changes: 41 additions & 0 deletions smriprep/interfaces/surf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
SimpleInterface,
File,
isdefined,
InputMultiObject,
traits,
)


Expand Down Expand Up @@ -114,6 +116,45 @@ def _run_interface(self, runtime):
return runtime


class AggregateSurfacesInputSpec(TraitedSpec):
surfaces = InputMultiObject(File(exists=True), desc="Input surfaces")
morphometrics = InputMultiObject(File(exists=True), desc="Input morphometrics")


class AggregateSurfacesOutputSpec(TraitedSpec):
pial = traits.List(File(), maxlen=2, desc="Pial surfaces")
white = traits.List(File(), maxlen=2, desc="White surfaces")
inflated = traits.List(File(), maxlen=2, desc="Inflated surfaces")
midthickness = traits.List(File(), maxlen=2, desc="Midthickness (or graymid) surfaces")
thickness = traits.List(File(), maxlen=2, desc="Cortical thickness maps")
sulc = traits.List(File(), maxlen=2, desc="Sulcal depth maps")
curv = traits.List(File(), maxlen=2, desc="Curvature maps")


class AggregateSurfaces(SimpleInterface):
"""Aggregate and group surfaces & morphometrics into left/right pairs."""
input_spec = AggregateSurfacesInputSpec
output_spec = AggregateSurfacesOutputSpec

def _run_interface(self, runtime):
from collections import defaultdict
import os
import re

container = defaultdict(list)
inputs = (self.inputs.surfaces or []) + (self.inputs.morphometrics or [])
findre = re.compile(
r'(?:^|[^d])(?P<name>white|pial|inflated|midthickness|thickness|sulc|curv)'
)
for surface in sorted(inputs, key=os.path.basename):
match = findre.search(os.path.basename(surface))
if match:
container[match.group('name')].append(surface)
for name, files in container.items():
self._results[name] = files
return runtime


def normalize_surfs(in_file: str, transform_file: str, newpath: Optional[str] = None) -> str:
"""
Update GIFTI metadata and apply rigid coordinate correction.
Expand Down
60 changes: 57 additions & 3 deletions smriprep/workflows/surfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,13 @@ def init_surface_recon_wf(*, omp_nthreads, hires, name="surface_recon_wf"):
"out_aseg",
"out_aparc",
"morphometrics",
"midthickness",
"pial",
"white",
"inflated",
"thickness",
"sulc",
"curv",
]
),
name="outputnode",
Expand Down Expand Up @@ -294,7 +301,14 @@ def init_surface_recon_wf(*, omp_nthreads, hires, name="surface_recon_wf"):
(autorecon_resume_wf, outputnode, [('outputnode.subjects_dir', 'subjects_dir'),
('outputnode.subject_id', 'subject_id')]),
(gifti_surface_wf, outputnode, [('outputnode.surfaces', 'surfaces'),
('outputnode.morphometrics', 'morphometrics')]),
('outputnode.morphometrics', 'morphometrics'),
('outputnode.midthickness', 'midthickness'),
('outputnode.pial', 'pial'),
('outputnode.white', 'white'),
('outputnode.inflated', 'inflated'),
('outputnode.thickness', 'thickness'),
('outputnode.sulc', 'sulc'),
('outputnode.curv', 'curv')]),
(t1w2fsnative_xfm, outputnode, [('out_lta', 't1w2fsnative_xfm')]),
(fsnative2t1w_xfm, outputnode, [('out_reg_file', 'fsnative2t1w_xfm')]),
(refine, outputnode, [('out_file', 'out_brainmask')]),
Expand Down Expand Up @@ -574,23 +588,51 @@ def init_gifti_surface_wf(*, name="gifti_surface_wf"):
Outputs
-------
midthickness
Left and right midthickness (or graymid) surface GIFTIs
pial
Left and right pial surface GIFTIs
white
Left and right white surface GIFTIs
inflated
Left and right inflated surface GIFTIs
surfaces
GIFTI surfaces for gray/white matter boundary, pial surface,
midthickness (or graymid) surface, and inflated surfaces
thickness
Left and right cortical thickness GIFTIs
sulc
Left and right sulcal depth map GIFTIs
curv
Left and right curvature map GIFTIs
morphometrics
GIFTIs of cortical thickness, curvature, and sulcal depth
"""
from ..interfaces.freesurfer import MRIsConvertData
from ..interfaces.surf import NormalizeSurf
from ..interfaces.surf import NormalizeSurf, AggregateSurfaces

workflow = Workflow(name=name)

inputnode = pe.Node(
niu.IdentityInterface(["subjects_dir", "subject_id", "fsnative2t1w_xfm"]),
name="inputnode",
)
outputnode = pe.Node(niu.IdentityInterface(["surfaces", "morphometrics"]), name="outputnode")
outputnode = pe.Node(
niu.IdentityInterface([
"pial",
"white",
"inflated",
"midthickness",
"thickness",
"sulc",
"curv",
# Preserve grouping
"surfaces",
"morphometrics",
]),
name="outputnode"
)

get_surfaces = pe.Node(nio.FreeSurferSource(), name="get_surfaces")

Expand Down Expand Up @@ -624,6 +666,8 @@ def init_gifti_surface_wf(*, name="gifti_surface_wf"):
name="morphs2gii",
)

agg_surfaces = pe.Node(AggregateSurfaces(), name="agg_surfaces")

# fmt:off
workflow.connect([
(inputnode, get_surfaces, [('subjects_dir', 'subjects_dir'),
Expand All @@ -648,6 +692,16 @@ def init_gifti_surface_wf(*, name="gifti_surface_wf"):
('curv', 'in3')]),
(surfmorph_list, morphs2gii, [('out', 'scalarcurv_file')]),
(morphs2gii, outputnode, [('converted', 'morphometrics')]),
# Output individual surfaces as well
(fix_surfs, agg_surfaces, [('out_file', 'surfaces')]),
(morphs2gii, agg_surfaces, [('converted', 'morphometrics')]),
(agg_surfaces, outputnode, [('pial', 'pial'),
('white', 'white'),
('inflated', 'inflated'),
('midthickness', 'midthickness'),
('thickness', 'thickness'),
('sulc', 'sulc'),
('curv', 'curv')]),
])
# fmt:on
return workflow
Expand Down

0 comments on commit 5e60fd8

Please sign in to comment.