Skip to content

Commit 3c2edb7

Browse files
authored
Merge pull request #388 from effigies/rf/template-resampling-workflows
RF: Split template and fsLR resampling and sinking into isolated workflows
2 parents 6ce2121 + 0bf46d1 commit 3c2edb7

File tree

5 files changed

+735
-363
lines changed

5 files changed

+735
-363
lines changed

smriprep/interfaces/gifti.py

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,76 @@
55
from nipype.interfaces.base import File, SimpleInterface, TraitedSpec, isdefined, traits
66

77

8-
class InvertShapeInputSpec(TraitedSpec):
8+
class MetricMathInputSpec(TraitedSpec):
99
subject_id = traits.Str(desc='subject ID')
1010
hemisphere = traits.Enum(
1111
"L",
1212
"R",
1313
mandatory=True,
1414
desc='hemisphere',
1515
)
16-
shape = traits.Str(desc='name of shape to invert')
17-
shape_file = File(exists=True, mandatory=True, desc='input GIFTI file')
16+
metric = traits.Str(desc='name of metric to invert')
17+
metric_file = File(exists=True, mandatory=True, desc='input GIFTI file')
18+
operation = traits.Enum(
19+
"invert",
20+
"abs",
21+
"bin",
22+
mandatory=True,
23+
desc='operation to perform',
24+
)
1825

1926

20-
class InvertShapeOutputSpec(TraitedSpec):
21-
shape_file = File(desc='output GIFTI file')
27+
class MetricMathOutputSpec(TraitedSpec):
28+
metric_file = File(desc='output GIFTI file')
2229

2330

24-
class InvertShape(SimpleInterface):
25-
"""Prepare GIFTI shape file for use in MSMSulc
31+
class MetricMath(SimpleInterface):
32+
"""Prepare GIFTI metric file for use in MSMSulc
2633
2734
This interface mirrors the action of the following portion
2835
of FreeSurfer2CaretConvertAndRegisterNonlinear.sh::
2936
30-
wb_command -set-structure ${shape_file} CORTEX_[LEFT|RIGHT]
31-
wb_command -metric-math "var * -1" ${shape_file} -var var ${shape_file}
32-
wb_command -set-map-names ${shape_file} -map 1 ${subject}_[L|R]_${shape}
37+
wb_command -set-structure ${metric_file} CORTEX_[LEFT|RIGHT]
38+
wb_command -metric-math "var * -1" ${metric_file} -var var ${metric_file}
39+
wb_command -set-map-names ${metric_file} -map 1 ${subject}_[L|R]_${metric}
40+
# If abs:
41+
wb_command -metric-math "abs(var)" ${metric_file} -var var ${metric_file}
3342
3443
We do not add palette information to the output file.
3544
"""
3645

37-
input_spec = InvertShapeInputSpec
38-
output_spec = InvertShapeOutputSpec
46+
input_spec = MetricMathInputSpec
47+
output_spec = MetricMathOutputSpec
3948

4049
def _run_interface(self, runtime):
41-
subject, hemi, shape = self.inputs.subject_id, self.inputs.hemisphere, self.inputs.shape
50+
subject, hemi, metric = self.inputs.subject_id, self.inputs.hemisphere, self.inputs.metric
4251
if not isdefined(subject):
4352
subject = 'sub-XYZ'
4453

45-
img = nb.GiftiImage.from_filename(self.inputs.shape_file)
54+
img = nb.GiftiImage.from_filename(self.inputs.metric_file)
4655
# wb_command -set-structure
4756
img.meta["AnatomicalStructurePrimary"] = {'L': 'CortexLeft', 'R': 'CortexRight'}[hemi]
4857
darray = img.darrays[0]
4958
# wb_command -set-map-names
5059
meta = darray.meta
51-
meta['Name'] = f"{subject}_{hemi}_{shape}"
52-
53-
# wb_command -metric-math "var * -1"
54-
inv = -darray.data
60+
meta['Name'] = f"{subject}_{hemi}_{metric}"
61+
62+
datatype = darray.datatype
63+
if self.inputs.operation == "abs":
64+
# wb_command -metric-math "abs(var)"
65+
data = abs(darray.data)
66+
elif self.inputs.operation == "invert":
67+
# wb_command -metric-math "var * -1"
68+
data = -darray.data
69+
elif self.inputs.operation == "bin":
70+
# wb_command -metric-math "var > 0"
71+
data = darray.data > 0
72+
datatype = 'uint8'
5573

5674
darray = nb.gifti.GiftiDataArray(
57-
inv,
75+
data,
5876
intent=darray.intent,
59-
datatype=darray.datatype,
77+
datatype=datatype,
6078
encoding=darray.encoding,
6179
endian=darray.endian,
6280
coordsys=darray.coordsys,
@@ -65,7 +83,7 @@ def _run_interface(self, runtime):
6583
)
6684
img.darrays[0] = darray
6785

68-
out_filename = os.path.join(runtime.cwd, f"{subject}.{hemi}.{shape}.native.shape.gii")
86+
out_filename = os.path.join(runtime.cwd, f"{subject}.{hemi}.{metric}.native.shape.gii")
6987
img.to_filename(out_filename)
70-
self._results["shape_file"] = out_filename
88+
self._results["metric_file"] = out_filename
7189
return runtime

smriprep/workflows/anatomical.py

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
from .fit.registration import init_register_template_wf
5656
from .outputs import (
5757
init_anat_reports_wf,
58+
init_ds_anat_volumes_wf,
59+
init_ds_grayord_metrics_wf,
5860
init_ds_surface_metrics_wf,
5961
init_ds_surfaces_wf,
6062
init_ds_template_wf,
@@ -64,16 +66,20 @@
6466
init_ds_template_registration_wf,
6567
init_ds_fs_registration_wf,
6668
init_anat_second_derivatives_wf,
69+
init_template_iterator_wf,
6770
)
6871
from .surfaces import (
6972
init_anat_ribbon_wf,
7073
init_fsLR_reg_wf,
7174
init_gifti_morphometrics_wf,
75+
init_hcp_morphometrics_wf,
7276
init_gifti_surfaces_wf,
77+
init_morph_grayords_wf,
7378
init_msm_sulc_wf,
7479
init_surface_derivatives_wf,
7580
init_surface_recon_wf,
7681
init_refinement_wf,
82+
init_resample_midthickness_wf,
7783
)
7884

7985
LOGGER = logging.getLogger("nipype.workflow")
@@ -113,6 +119,8 @@ def init_anat_preproc_wf(
113119
114120
from niworkflows.utils.spaces import SpatialReferences, Reference
115121
from smriprep.workflows.anatomical import init_anat_preproc_wf
122+
spaces = SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5'])
123+
spaces.checkpoint()
116124
wf = init_anat_preproc_wf(
117125
bids_root='.',
118126
output_dir='.',
@@ -124,7 +132,7 @@ def init_anat_preproc_wf(
124132
t2w=[],
125133
skull_strip_mode='force',
126134
skull_strip_template=Reference('OASIS30ANTs'),
127-
spaces=SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5']),
135+
spaces=spaces,
128136
precomputed={},
129137
omp_nthreads=1,
130138
)
@@ -261,14 +269,13 @@ def init_anat_preproc_wf(
261269
omp_nthreads=omp_nthreads,
262270
skull_strip_fixed_seed=skull_strip_fixed_seed,
263271
)
264-
anat_second_derivatives_wf = init_anat_second_derivatives_wf(
272+
template_iterator_wf = init_template_iterator_wf(spaces=spaces)
273+
ds_std_volumes_wf = init_ds_anat_volumes_wf(
265274
bids_root=bids_root,
266-
freesurfer=freesurfer,
267275
output_dir=output_dir,
268-
spaces=spaces,
269-
cifti_output=cifti_output,
276+
name="ds_std_volumes_wf",
270277
)
271-
# fmt:off
278+
272279
workflow.connect([
273280
(inputnode, anat_fit_wf, [
274281
("t1w", "inputnode.t1w"),
@@ -293,19 +300,32 @@ def init_anat_preproc_wf(
293300
(f"outputnode.sphere_reg_{'msm' if msm_sulc else 'fsLR'}", "sphere_reg_fsLR"),
294301
("outputnode.anat_ribbon", "anat_ribbon"),
295302
]),
296-
(anat_fit_wf, anat_second_derivatives_wf, [
297-
('outputnode.template', 'inputnode.template'),
303+
(anat_fit_wf, template_iterator_wf, [
304+
("outputnode.template", "inputnode.template"),
305+
("outputnode.anat2std_xfm", "inputnode.anat2std_xfm"),
306+
]),
307+
(anat_fit_wf, ds_std_volumes_wf, [
298308
('outputnode.t1w_valid_list', 'inputnode.source_files'),
299309
("outputnode.t1w_preproc", "inputnode.t1w_preproc"),
300310
("outputnode.t1w_mask", "inputnode.t1w_mask"),
301311
("outputnode.t1w_dseg", "inputnode.t1w_dseg"),
302312
("outputnode.t1w_tpms", "inputnode.t1w_tpms"),
303-
('outputnode.anat2std_xfm', 'inputnode.anat2std_xfm'),
304313
]),
305-
])
306-
# fmt:on
307-
if freesurfer:
314+
(template_iterator_wf, ds_std_volumes_wf, [
315+
("outputnode.std_t1w", "inputnode.ref_file"),
316+
("outputnode.anat2std_xfm", "inputnode.anat2std_xfm"),
317+
("outputnode.space", "inputnode.space"),
318+
("outputnode.cohort", "inputnode.cohort"),
319+
("outputnode.resolution", "inputnode.resolution"),
320+
]),
321+
]) # fmt:skip
308322

323+
if freesurfer:
324+
anat_second_derivatives_wf = init_anat_second_derivatives_wf(
325+
bids_root=bids_root,
326+
output_dir=output_dir,
327+
cifti_output=cifti_output,
328+
)
309329
surface_derivatives_wf = init_surface_derivatives_wf(
310330
cifti_output=cifti_output,
311331
)
@@ -316,7 +336,6 @@ def init_anat_preproc_wf(
316336
bids_root=bids_root, output_dir=output_dir, metrics=["curv"], name="ds_curv_wf"
317337
)
318338

319-
# fmt:off
320339
workflow.connect([
321340
(anat_fit_wf, surface_derivatives_wf, [
322341
('outputnode.t1w_preproc', 'inputnode.reference'),
@@ -336,18 +355,78 @@ def init_anat_preproc_wf(
336355
(surface_derivatives_wf, ds_curv_wf, [
337356
('outputnode.curv', 'inputnode.curv'),
338357
]),
358+
(anat_fit_wf, anat_second_derivatives_wf, [
359+
('outputnode.t1w_valid_list', 'inputnode.source_files'),
360+
]),
339361
(surface_derivatives_wf, anat_second_derivatives_wf, [
340362
('outputnode.out_aseg', 'inputnode.t1w_fs_aseg'),
341363
('outputnode.out_aparc', 'inputnode.t1w_fs_aparc'),
342-
('outputnode.cifti_morph', 'inputnode.cifti_morph'),
343-
('outputnode.cifti_metadata', 'inputnode.cifti_metadata'),
344364
]),
345365
(surface_derivatives_wf, outputnode, [
346366
('outputnode.out_aseg', 't1w_aseg'),
347367
('outputnode.out_aparc', 't1w_aparc'),
348368
]),
349-
])
350-
# fmt:on
369+
]) # fmt:skip
370+
371+
if cifti_output:
372+
hcp_morphometrics_wf = init_hcp_morphometrics_wf(omp_nthreads=omp_nthreads)
373+
resample_midthickness_wf = init_resample_midthickness_wf(grayord_density=cifti_output)
374+
morph_grayords_wf = init_morph_grayords_wf(
375+
grayord_density=cifti_output, omp_nthreads=omp_nthreads
376+
)
377+
378+
ds_grayord_metrics_wf = init_ds_grayord_metrics_wf(
379+
bids_root=bids_root,
380+
output_dir=output_dir,
381+
metrics=['curv', 'thickness', 'sulc'],
382+
cifti_output=cifti_output,
383+
)
384+
385+
workflow.connect([
386+
(anat_fit_wf, hcp_morphometrics_wf, [
387+
('outputnode.subject_id', 'inputnode.subject_id'),
388+
('outputnode.sulc', 'inputnode.sulc'),
389+
('outputnode.thickness', 'inputnode.thickness'),
390+
('outputnode.midthickness', 'inputnode.midthickness'),
391+
]),
392+
(surface_derivatives_wf, hcp_morphometrics_wf, [
393+
('outputnode.curv', 'inputnode.curv'),
394+
]),
395+
(anat_fit_wf, resample_midthickness_wf, [
396+
('outputnode.midthickness', 'inputnode.midthickness'),
397+
(
398+
f"outputnode.sphere_reg_{'msm' if msm_sulc else 'fsLR'}",
399+
"inputnode.sphere_reg_fsLR",
400+
),
401+
]),
402+
(anat_fit_wf, morph_grayords_wf, [
403+
('outputnode.midthickness', 'inputnode.midthickness'),
404+
(
405+
f"outputnode.sphere_reg_{'msm' if msm_sulc else 'fsLR'}",
406+
"inputnode.sphere_reg_fsLR",
407+
),
408+
]),
409+
(hcp_morphometrics_wf, morph_grayords_wf, [
410+
('outputnode.curv', 'inputnode.curv'),
411+
('outputnode.sulc', 'inputnode.sulc'),
412+
('outputnode.thickness', 'inputnode.thickness'),
413+
('outputnode.roi', 'inputnode.roi'),
414+
]),
415+
(resample_midthickness_wf, morph_grayords_wf, [
416+
('outputnode.midthickness_fsLR', 'inputnode.midthickness_fsLR'),
417+
]),
418+
(anat_fit_wf, ds_grayord_metrics_wf, [
419+
('outputnode.t1w_valid_list', 'inputnode.source_files'),
420+
]),
421+
(morph_grayords_wf, ds_grayord_metrics_wf, [
422+
('outputnode.curv_fsLR', 'inputnode.curv'),
423+
('outputnode.curv_metadata', 'inputnode.curv_metadata'),
424+
('outputnode.thickness_fsLR', 'inputnode.thickness'),
425+
('outputnode.thickness_metadata', 'inputnode.thickness_metadata'),
426+
('outputnode.sulc_fsLR', 'inputnode.sulc'),
427+
('outputnode.sulc_metadata', 'inputnode.sulc_metadata'),
428+
]),
429+
]) # fmt:skip
351430

352431
return workflow
353432

@@ -1264,7 +1343,7 @@ def init_anat_fit_wf(
12641343
# fmt:on
12651344
elif msm_sulc:
12661345
LOGGER.info("ANAT Stage 10: Found pre-computed MSM-Sulc registration sphere")
1267-
fsLR_buffer.inputs.sphere_reg_msm = sorted(precomputed["sphere_reg_msm"])
1346+
msm_buffer.inputs.sphere_reg_msm = sorted(precomputed["sphere_reg_msm"])
12681347
else:
12691348
LOGGER.info("ANAT Stage 10: MSM-Sulc disabled")
12701349

smriprep/workflows/base.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def init_smriprep_wf(
8181
os.environ['FREESURFER_HOME'] = os.getcwd()
8282
from smriprep.workflows.base import init_smriprep_wf
8383
from niworkflows.utils.spaces import SpatialReferences, Reference
84+
spaces = SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5'])
85+
spaces.checkpoint()
8486
wf = init_smriprep_wf(
8587
sloppy=False,
8688
debug=False,
@@ -98,7 +100,7 @@ def init_smriprep_wf(
98100
skull_strip_fixed_seed=False,
99101
skull_strip_mode='force',
100102
skull_strip_template=Reference('OASIS30ANTs'),
101-
spaces=SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5']),
103+
spaces=spaces,
102104
subject_list=['smripreptest'],
103105
work_dir='.',
104106
bids_filters=None,
@@ -250,6 +252,8 @@ def init_single_subject_wf(
250252
from niworkflows.utils.spaces import SpatialReferences, Reference
251253
from smriprep.workflows.base import init_single_subject_wf
252254
BIDSLayout = namedtuple('BIDSLayout', ['root'])
255+
spaces = SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5'])
256+
spaces.checkpoint()
253257
wf = init_single_subject_wf(
254258
sloppy=False,
255259
debug=False,
@@ -266,7 +270,7 @@ def init_single_subject_wf(
266270
skull_strip_fixed_seed=False,
267271
skull_strip_mode='force',
268272
skull_strip_template=Reference('OASIS30ANTs'),
269-
spaces=SpatialReferences(spaces=['MNI152NLin2009cAsym', 'fsaverage5']),
273+
spaces=spaces,
270274
subject_id='test',
271275
bids_filters=None,
272276
cifti_output=None,

0 commit comments

Comments
 (0)