Skip to content

Commit c1f454e

Browse files
committed
ENH: Integrate morphometric fsLR resampling
1 parent 16c6383 commit c1f454e

File tree

2 files changed

+59
-73
lines changed

2 files changed

+59
-73
lines changed

nibabies/workflows/anatomical/base.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def init_infant_anat_wf(
402402
elif config.workflow.surface_recon_method == 'mcribs':
403403
from nipype.interfaces.ants import DenoiseImage
404404

405-
from .surfaces import init_mcribs_surface_recon_wf
405+
from .surfaces import init_mcribs_sphere_reg_wf, init_mcribs_surface_recon_wf
406406

407407
# Denoise raw T2w, since using the template / preproc resulted in intersection errors
408408
denoise_raw_t2w = pe.Node(
@@ -511,20 +511,24 @@ def init_infant_anat_wf(
511511
# fmt: on
512512

513513
if cifti_output:
514-
from smriprep.workflows.surfaces import init_morph_grayords_wf
514+
from nibabies.workflows.anatomical.resampling import (
515+
init_anat_fsLR_resampling_wf,
516+
)
515517

516-
morph_grayords_wf = init_morph_grayords_wf(grayord_density=cifti_output)
518+
is_mcribs = config.workflow.surface_recon_method == "mcribs"
519+
# handles morph_grayords_wf
520+
anat_fsLR_resampling_wf = init_anat_fsLR_resampling_wf(cifti_output, mcribs=is_mcribs)
517521
anat_derivatives_wf.get_node('inputnode').inputs.cifti_density = cifti_output
518522
# fmt:off
519523
wf.connect([
520-
(surface_recon_wf, morph_grayords_wf, [
524+
(surface_recon_wf, anat_fsLR_resampling_wf, [
521525
('outputnode.subject_id', 'inputnode.subject_id'),
522-
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
523-
]),
524-
(morph_grayords_wf, anat_derivatives_wf, [
526+
('outputnode.subjects_dir', 'inputnode.subjects_dir')]),
527+
(anat_fsLR_resampling_wf, anat_derivatives_wf, [
525528
("outputnode.cifti_morph", "inputnode.cifti_morph"),
526-
("outputnode.cifti_metadata", "inputnode.cifti_metadata"),
527-
]),
529+
("outputnode.cifti_metadata", "inputnode.cifti_metadata")]),
530+
(anat_fsLR_resampling_wf, outputnode, [
531+
("outputnode.fsLR_midthickness", "fsLR_midthickness")])
528532
])
529533
# fmt:on
530534

nibabies/workflows/anatomical/resampling.py

Lines changed: 46 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,16 @@ def init_anat_fsLR_resampling_wf(
4747
iterables=[('hemi', ['L', 'R'])],
4848
)
4949

50-
outputnode = pe.Node(niu.IdentityInterface(fields=['fsLR_midthickness']), name='outputnode')
50+
outputnode = pe.Node(
51+
niu.IdentityInterface(
52+
fields=[
53+
'fsLR_midthickness',
54+
'cifti_morph',
55+
'cifti_metadata',
56+
]
57+
),
58+
name='outputnode',
59+
)
5160

5261
# select white, midthickness and pial surfaces based on hemi
5362
select_surfaces = pe.Node(CiftiSelect(), name='select_surfaces')
@@ -96,50 +105,33 @@ def init_anat_fsLR_resampling_wf(
96105
else:
97106
morph_grayords_wf = init_morph_grayords_wf(grayord_density)
98107

99-
workflow.connect(
100-
[
101-
(
102-
inputnode,
103-
select_surfaces,
104-
[("surfaces", "surfaces"), ("sphere_reg_fsLR", "spherical_registrations")],
105-
),
106-
(itersource, select_surfaces, [("hemi", "hemi")]),
107-
# Downsample midthickness to fsLR density
108-
(
109-
select_surfaces,
110-
downsampled_midthickness,
111-
[
112-
("midthickness", "surface_in"),
113-
("sphere_reg", "current_sphere"),
114-
("template_sphere", "new_sphere"),
115-
],
116-
),
117-
(downsampled_midthickness, joinnode, [("surface_out", "fsLR_midthickness")]),
118-
(joinnode, outputnode, [("surface_out", "fsLR_midthickness")]),
119-
# resample surfaces
120-
(
121-
inputnode,
122-
morph_grayords_wf,
123-
[
124-
("subject_id", "inputnode.subject_id"),
125-
("subjects_dir", "inputnode.subjects_dir"),
126-
],
127-
),
128-
(
129-
morph_grayords_wf,
130-
outputnode,
131-
[
132-
("outputnode.cifti_morph", "cifti_morph"),
133-
("outputnode.cifti_metadata", "cifti_metadata"),
134-
],
135-
),
136-
]
137-
)
108+
# fmt:off
109+
workflow.connect([
110+
(inputnode, select_surfaces, [
111+
("surfaces", "surfaces"),
112+
("sphere_reg_fsLR", "spherical_registrations")]),
113+
(itersource, select_surfaces, [("hemi", "hemi")]),
114+
# Downsample midthickness to fsLR density
115+
(select_surfaces, downsampled_midthickness, [
116+
("midthickness", "surface_in"),
117+
("sphere_reg", "current_sphere"),
118+
("template_sphere", "new_sphere")]),
119+
(downsampled_midthickness, joinnode, [("surface_out", "fsLR_midthickness")]),
120+
(joinnode, outputnode, [("surface_out", "fsLR_midthickness")]),
121+
# resample morphometrics to fsLR 32k
122+
(inputnode, morph_grayords_wf, [
123+
("subject_id", "inputnode.subject_id"),
124+
("subjects_dir", "inputnode.subjects_dir")]),
125+
(morph_grayords_wf, outputnode, [
126+
("outputnode.cifti_morph", "cifti_morph"),
127+
("outputnode.cifti_metadata", "cifti_metadata")]),
128+
])
129+
# fmt:on
138130
return workflow
139131

140132

141133
def init_mcribs_morph_grayords_wf(
142-
grayord_density: ty.Literal['91k', '170k'],
134+
grayord_density: ty.Literal['91k'], # Only 91k supported ATM
143135
name: str = "morph_grayords_wf",
144136
):
145137
"""
@@ -180,21 +172,17 @@ def init_mcribs_morph_grayords_wf(
180172
Paths to JSON files containing metadata corresponding to ``cifti_morph``
181173
182174
"""
183-
import templateflow.api as tf
184-
from nipype.interfaces.io import FreeSurferSource
185175
from nipype.interfaces.workbench import MetricResample
186176
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
187177
from smriprep.interfaces.cifti import GenerateDScalar
188178

189179
workflow = Workflow(name=name)
190180
workflow.__desc__ = f"""\
191181
*Grayordinate* "dscalar" files [@hcppipelines] containing {grayord_density} samples were
192-
also generated using the highest-resolution ``fsaverage`` as an intermediate standardized
182+
also generated using `M-CRIB-S` as an intermediate standardized
193183
surface space.
194184
"""
195185

196-
fslr_density = "32k" if grayord_density == "91k" else "59k"
197-
198186
inputnode = pe.Node(
199187
niu.IdentityInterface(
200188
fields=[
@@ -213,17 +201,12 @@ def init_mcribs_morph_grayords_wf(
213201
name="outputnode",
214202
)
215203

216-
get_surfaces = pe.Node(FreeSurferSource(), name="get_surfaces")
217-
218204
surfmorph_list = pe.Node(
219205
niu.Merge(3, ravel_inputs=True),
220206
name="surfmorph_list",
221207
run_without_submitting=True,
222208
)
223209

224-
# TODO: Extract L/R midthickness from surfaces
225-
# TODO: Coerce morphometrics into curv-L, curv-R, sulc-L, sulc-R, thickness-L, thickness-R
226-
227210
# Setup Workbench command. LR ordering for hemi can be assumed, as it is imposed
228211
# by the iterfield of the MapNode in the surface sampling workflow above.
229212
resample = pe.MapNode(
@@ -244,7 +227,6 @@ def init_mcribs_morph_grayords_wf(
244227
str(atlases / 'mcribs' / 'lh.sphere.reg.dHCP42.surf.gii'),
245228
str(atlases / 'mcribs' / 'rh.sphere.reg.dHCP42.surf.gii'),
246229
] * 3
247-
# current area: FreeSurfer (M-CRIB-S) midthickness
248230
resample.inputs.new_sphere = [
249231
str(atlases / 'dHCP' / 'dHCP.week42.L.sphere.surf.gii'),
250232
str(atlases / 'dHCP' / 'dHCP.week42.R.sphere.surf.gii'),
@@ -267,23 +249,23 @@ def init_mcribs_morph_grayords_wf(
267249

268250
# fmt: off
269251
workflow.connect([
270-
(inputnode, get_surfaces, [
271-
('subject_id', 'subject_id'),
272-
('subjects_dir', 'subjects_dir'),
252+
(inputnode, resample, [
253+
("fsLR_midthickness", "new_area"),
254+
(('surfaces', _get_surf, "midthickness", 3), "current_area")]),
255+
(inputnode, surfmorph_list, [
256+
(('morphometrics', _get_surf, "curv"), "in1"),
257+
(('morphometrics', _get_surf, "sulc"), "in2"),
258+
(('morphometrics', _get_surf, "thickness"), "in3"),
273259
]),
274-
(get_surfaces, surfmorph_list, [
275-
(('curv', _sorted_by_basename), 'in1'),
276-
(('sulc', _sorted_by_basename), 'in2'),
277-
(('thickness', _sorted_by_basename), 'in3'),
278-
]),
279-
# (surfmorph_list, surf2surf, [('out', 'source_file')]),
280-
# (surf2surf, resample, [('out_file', 'in_file')]),
281-
(inputnode, resample, [("fsLR_midthickness", "new_area")]),
282260
(resample, gen_cifti, [
283261
(("out_file", _collate), "scalar_surfs")]),
284262
(gen_cifti, outputnode, [("out_file", "cifti_morph"),
285263
("out_metadata", "cifti_metadata")]),
286264
])
287265
# fmt: on
288-
289266
return workflow
267+
268+
269+
def _get_surf(surfaces, name, mult=1):
270+
"Select a specific surface by name, and optionally multiple it."
271+
return [surf for surf in _sorted_by_basename(surfaces) if name in surf] * mult

0 commit comments

Comments
 (0)