1
1
"""Base anatomical preprocessing."""
2
+ import warnings
3
+
2
4
from nipype .interfaces import utility as niu
3
5
from nipype .pipeline import engine as pe
4
6
@@ -93,6 +95,7 @@ def init_infant_anat_wf(
93
95
init_anat_reports_wf ,
94
96
init_coreg_report_wf ,
95
97
)
98
+ from .preproc import init_anat_preproc_wf
96
99
from .registration import init_coregistration_wf
97
100
from .segmentation import init_anat_segmentations_wf
98
101
from .surfaces import init_anat_ribbon_wf
@@ -107,13 +110,23 @@ def init_infant_anat_wf(
107
110
108
111
# verify derivatives are relatively similar to T1w
109
112
if precomp_mask or precomp_aseg :
110
- from ...utils .validation import validate_t1w_derivatives
111
-
112
- validated_derivatives = validate_t1w_derivatives ( # compare derivatives to the first T1w
113
- t1w [0 ], anat_mask = precomp_mask , anat_aseg = precomp_aseg
114
- )
115
- precomp_mask = validated_derivatives .get ("anat_mask" )
116
- precomp_aseg = validated_derivatives .get ("anat_aseg" )
113
+ if num_t1w > 1 :
114
+ precomp_mask = None
115
+ precomp_aseg = None
116
+ warnings .warn (
117
+ "Multiple T1w files were found; precomputed derivatives will not be used."
118
+ )
119
+
120
+ else :
121
+ from ...utils .validation import validate_t1w_derivatives
122
+
123
+ validated_derivatives = (
124
+ validate_t1w_derivatives ( # compare derivatives to the first T1w
125
+ t1w [0 ], anat_mask = precomp_mask , anat_aseg = precomp_aseg
126
+ )
127
+ )
128
+ precomp_mask = validated_derivatives .get ("anat_mask" )
129
+ precomp_aseg = validated_derivatives .get ("anat_aseg" )
117
130
118
131
wf = Workflow (name = name )
119
132
desc = f"""\n
@@ -159,10 +172,10 @@ def init_infant_anat_wf(
159
172
160
173
desc += (
161
174
"""\
162
- All of the T1-weighted images were corrected for intensity non-uniformity (INU)"""
175
+ All of the T1-weighted images were denoised and corrected for intensity non-uniformity (INU)"""
163
176
if num_t1w > 1
164
177
else """\
165
- The T1-weighted (T1w) image was corrected for intensity non-uniformity (INU)"""
178
+ The T1-weighted (T1w) image was denoised and corrected for intensity non-uniformity (INU)"""
166
179
)
167
180
168
181
desc += """\
@@ -200,13 +213,15 @@ def init_infant_anat_wf(
200
213
cifti_output = cifti_output ,
201
214
)
202
215
203
- # Multiple T1w files -> generate average reference
216
+ # Multiple anatomical files -> generate average reference
204
217
t1w_template_wf = init_anat_template_wf (
205
218
contrast = "T1w" ,
206
219
num_files = num_t1w ,
207
220
longitudinal = longitudinal ,
208
221
omp_nthreads = omp_nthreads ,
209
222
sloppy = sloppy ,
223
+ precomputed_mask = bool (precomp_mask ),
224
+ precomputed_aseg = bool (precomp_aseg ),
210
225
name = "t1w_template_wf" ,
211
226
)
212
227
@@ -219,38 +234,27 @@ def init_infant_anat_wf(
219
234
name = "t2w_template_wf" ,
220
235
)
221
236
222
- # INU + Brain Extraction
237
+ # Clean up each anatomical template
238
+ # Denoise, INU, + Clipping
239
+ t1w_preproc_wf = init_anat_preproc_wf (name = "t1w_preproc_wf" )
240
+ t2w_preproc_wf = init_anat_preproc_wf (name = "t2w_preproc_wf" )
241
+
223
242
if skull_strip_mode != "force" :
224
243
raise NotImplementedError ("Skull stripping is currently required." )
225
244
226
- if precomp_mask :
227
- precomp_mask_wf = init_precomputed_mask_wf (omp_nthreads = omp_nthreads )
228
- precomp_mask_wf .inputs .inputnode .t1w_mask = precomp_mask
229
- sdc_brain_extraction_wf = init_sdc_brain_extraction_wf (
230
- name = "sdc_brain_extraction_wf" ,
231
- )
232
- brain_extraction_wf = init_infant_brain_extraction_wf (
233
- age_months = age_months ,
234
- ants_affine_init = ants_affine_init ,
235
- skull_strip_template = skull_strip_template .space ,
236
- template_specs = skull_strip_template .spec ,
237
- omp_nthreads = omp_nthreads ,
238
- sloppy = sloppy ,
239
- debug = "registration" in config .execution .debug ,
240
- )
241
245
coregistration_wf = init_coregistration_wf (
242
246
omp_nthreads = omp_nthreads ,
243
247
sloppy = sloppy ,
244
248
debug = "registration" in config .execution .debug ,
249
+ precomputed_mask = bool (precomp_mask ),
245
250
)
246
251
coreg_report_wf = init_coreg_report_wf (
247
252
output_dir = output_dir ,
248
253
)
249
- t1w_preproc_wf = precomp_mask_wf if precomp_mask else coregistration_wf
250
254
251
255
# Segmentation - initial implementation should be simple: JLF
252
256
anat_seg_wf = init_anat_segmentations_wf (
253
- anat_modality = anat_modality .capitalize (),
257
+ anat_modality = anat_modality .capitalize (), # TODO: Revisit this option
254
258
template_dir = segmentation_atlases ,
255
259
sloppy = sloppy ,
256
260
omp_nthreads = omp_nthreads ,
@@ -264,143 +268,117 @@ def init_infant_anat_wf(
264
268
templates = spaces .get_spaces (nonstandard = False , dim = (3 ,)),
265
269
)
266
270
267
- # Anatomical ribbon file using HCP signed-distance volume method
268
- # if config.workflow.project_goodvoxels:
269
- anat_ribbon_wf = init_anat_ribbon_wf ()
270
-
271
271
# fmt:off
272
272
wf .connect ([
273
273
(inputnode , t1w_template_wf , [("t1w" , "inputnode.in_files" )]),
274
+ (inputnode , t2w_template_wf , [("t2w" , "inputnode.in_files" )]),
275
+ (inputnode , anat_reports_wf , [("t1w" , "inputnode.source_file" )]),
276
+ (inputnode , coreg_report_wf , [("t1w" , "inputnode.source_file" )]),
277
+ (inputnode , anat_norm_wf , [(("t1w" , fix_multi_source_name ), "inputnode.orig_t1w" )]),
278
+
274
279
(t1w_template_wf , outputnode , [
275
- ("outputnode.realign_xfms" , "anat_ref_xfms" ),
276
- ]),
280
+ ("outputnode.realign_xfms" , "anat_ref_xfms" )]),
281
+ (t1w_template_wf , t1w_preproc_wf , [("outputnode.out_file" , "inputnode.in_anat" )]),
282
+ (t2w_template_wf , t2w_preproc_wf , [("outputnode.out_file" , "inputnode.in_anat" )]),
283
+ (t1w_template_wf , anat_derivatives_wf , [
284
+ ("outputnode.valid_list" , "inputnode.source_files" ),
285
+ ("outputnode.realign_xfms" , "inputnode.t1w_ref_xfms" )]),
286
+ (t2w_template_wf , anat_derivatives_wf , [
287
+ ("outputnode.valid_list" , "inputnode.t2w_source_files" )]),
288
+
289
+ (t1w_preproc_wf , coregistration_wf , [("outputnode.anat_preproc" , "inputnode.in_t1w" )]),
290
+ (t1w_preproc_wf , coreg_report_wf , [("outputnode.anat_preproc" , "inputnode.t1w_preproc" )]),
291
+ (t1w_preproc_wf , anat_norm_wf , [
292
+ ("outputnode.t1w_preproc" , "inputnode.moving_image" ),
293
+ ("outputnode.t1w_mask" , "inputnode.moving_mask" )]),
294
+
295
+ (coregistration_wf , coreg_report_wf , [
296
+ ("outputnode.t1w_mask" , "inputnode.in_mask" ),
297
+ ("outputnode.t2w_preproc" , "inputnode.t2w_preproc" )]),
298
+
277
299
(anat_seg_wf , outputnode , [
278
300
("outputnode.anat_dseg" , "anat_dseg" ),
279
- ("outputnode.anat_tpms" , "anat_tpms" ),
301
+ ("outputnode.anat_tpms" , "anat_tpms" )]),
302
+ (anat_seg_wf , anat_derivatives_wf , [
303
+ ("outputnode.anat_dseg" , "inputnode.t1w_dseg" ),
304
+ ("outputnode.anat_tpms" , "inputnode.t1w_tpms" ),
280
305
]),
281
306
(anat_seg_wf , anat_norm_wf , [
282
307
("outputnode.anat_dseg" , "inputnode.moving_segmentation" ),
283
- ("outputnode.anat_tpms" , "inputnode.moving_tpms" ),
284
- ]),
308
+ ("outputnode.anat_tpms" , "inputnode.moving_tpms" )]),
309
+
310
+ (anat_norm_wf , anat_reports_wf , [("poutputnode.template" , "inputnode.template" )]),
285
311
(anat_norm_wf , outputnode , [
286
312
("poutputnode.standardized" , "std_preproc" ),
287
313
("poutputnode.std_mask" , "std_mask" ),
288
314
("poutputnode.std_dseg" , "std_dseg" ),
289
315
("poutputnode.std_tpms" , "std_tpms" ),
290
316
("outputnode.template" , "template" ),
291
317
("outputnode.anat2std_xfm" , "anat2std_xfm" ),
292
- ("outputnode.std2anat_xfm" , "std2anat_xfm" ),
293
- ]),
294
- (inputnode , anat_norm_wf , [
295
- (("t1w" , fix_multi_source_name ), "inputnode.orig_t1w" ), # anat_validate? not used...
296
- ]),
297
- (t1w_preproc_wf , anat_norm_wf , [
298
- ("outputnode.t1w_preproc" , "inputnode.moving_image" ),
299
- ("outputnode.t1w_mask" , "inputnode.moving_mask" ),
300
- ]),
301
- (t1w_preproc_wf , anat_derivatives_wf , [
318
+ ("outputnode.std2anat_xfm" , "std2anat_xfm" )]),
319
+ (anat_norm_wf , anat_derivatives_wf , [
320
+ ("outputnode.template" , "inputnode.template" ),
321
+ ("outputnode.anat2std_xfm" , "inputnode.anat2std_xfm" ),
322
+ ("outputnode.std2anat_xfm" , "inputnode.std2anat_xfm" )]),
323
+
324
+ (coregistration_wf , anat_seg_wf , [("outputnode.t1w_brain" , "inputnode.anat_brain" )]),
325
+ (coregistration_wf , anat_derivatives_wf , [
302
326
("outputnode.t1w_mask" , "inputnode.t1w_mask" ),
303
327
("outputnode.t1w_preproc" , "inputnode.t1w_preproc" ),
304
- ]),
305
- (coregistration_wf , anat_derivatives_wf , [
306
- ("outputnode.t2w_preproc" , "inputnode.t2w_preproc" )
328
+ ("outputnode.t2w_preproc" , "inputnode.t2w_preproc" ),
307
329
]),
308
- (t1w_preproc_wf , outputnode , [
330
+ (coregistration_wf , outputnode , [
309
331
("outputnode.t1w_preproc" , "anat_preproc" ),
310
332
("outputnode.t1w_brain" , "anat_brain" ),
311
333
("outputnode.t1w_mask" , "anat_mask" ),
312
334
]),
313
- ])
314
335
315
- if not precomp_aseg :
316
- wf .connect ([
317
- (t1w_preproc_wf , anat_seg_wf , [("outputnode.t1w_brain" , "inputnode.anat_brain" )]),
318
- ])
319
- wf .connect ([
320
- (inputnode , t2w_template_wf , [("t2w" , "inputnode.in_files" )]),
336
+ (t1w_template_wf , anat_reports_wf , [
337
+ ("outputnode.out_report" , "inputnode.t1w_conform_report" )]),
338
+ (outputnode , anat_reports_wf , [
339
+ ("anat_preproc" , "inputnode.t1w_preproc" ),
340
+ ("anat_mask" , "inputnode.t1w_mask" ),
341
+ ("anat_dseg" , "inputnode.t1w_dseg" ),
342
+ ("std_preproc" , "inputnode.std_t1w" ),
343
+ ("std_mask" , "inputnode.std_mask" ),
344
+ ]),
321
345
])
346
+
322
347
if precomp_mask :
348
+ # Ensure the mask is conformed along with the T1w
349
+ t1w_template_wf .inputs .inputnode .anat_mask = precomp_mask
323
350
wf .connect ([
324
- (t1w_template_wf , precomp_mask_wf , [
325
- ("outputnode.out_file" , "inputnode.t1w" ),
326
- ]),
327
- (t2w_template_wf , sdc_brain_extraction_wf , [
328
- ("outputnode.out_file" , "inputnode.in_file" ),
329
- ]),
330
- (sdc_brain_extraction_wf , coregistration_wf , [
331
- ("outputnode.out_file" , "inputnode.in_t2w_preproc" ),
332
- ("outputnode.out_mask" , "inputnode.in_mask" ),
333
- ("outputnode.out_probseg" , "inputnode.in_probmap" ),
334
- ]),
351
+ (t1w_template_wf , coregistration_wf , [("outputnode.anat_mask" , "inputnode.in_mask" )]),
335
352
])
336
353
else :
354
+ # Run brain extraction on the T2w
355
+ brain_extraction_wf = init_infant_brain_extraction_wf (
356
+ age_months = age_months ,
357
+ ants_affine_init = ants_affine_init ,
358
+ skull_strip_template = skull_strip_template .space ,
359
+ template_specs = skull_strip_template .spec ,
360
+ omp_nthreads = omp_nthreads ,
361
+ sloppy = sloppy ,
362
+ debug = "registration" in config .execution .debug ,
363
+ )
364
+
337
365
wf .connect ([
338
- (t2w_template_wf , brain_extraction_wf , [
339
- ("outputnode.out_file" , "inputnode.in_t2w" ),
340
- ]),
366
+ (t1w_preproc_wf , brain_extraction_wf , [
367
+ ("outputnode.anat_preproc" , "inputnode.in_t1w" )]),
368
+ (t2w_preproc_wf , brain_extraction_wf , [
369
+ ("outputnode.anat_preproc" , "inputnode.in_t2w" )]),
341
370
(brain_extraction_wf , coregistration_wf , [
342
- ("outputnode.t2w_preproc" , "inputnode.in_t2w_preproc " ),
371
+ ("outputnode.t2w_preproc" , "inputnode.in_t2w " ),
343
372
("outputnode.out_mask" , "inputnode.in_mask" ),
344
- ("outputnode.out_probmap" , "inputnode.in_probmap" ),
345
- ]),
373
+ ("outputnode.out_probmap" , "inputnode.in_probmap" )]),
346
374
])
347
- wf .connect ([
348
- (t1w_template_wf , coregistration_wf , [
349
- ("outputnode.out_file" , "inputnode.in_t1w" ),
350
- ]),
351
375
352
- (inputnode , coreg_report_wf , [
353
- ("t1w" , "inputnode.source_file" ),
354
- ]),
355
- (t1w_preproc_wf , coreg_report_wf , [
356
- ("outputnode.t1w_preproc" , "inputnode.t1w_preproc" ),
357
- ("outputnode.t1w_mask" , "inputnode.in_mask" ),
358
- ]),
359
- (coregistration_wf , coreg_report_wf , [
360
- ("outputnode.t2w_preproc" , "inputnode.t2w_preproc" )
361
- ]),
362
- ])
363
-
364
- wf .connect ([
365
- # reports
366
- (inputnode , anat_reports_wf , [
367
- ("t1w" , "inputnode.source_file" ),
368
- ]),
369
- (outputnode , anat_reports_wf , [
370
- ("anat_preproc" , "inputnode.t1w_preproc" ),
371
- ("anat_mask" , "inputnode.t1w_mask" ),
372
- ("anat_dseg" , "inputnode.t1w_dseg" ),
373
- ("std_preproc" , "inputnode.std_t1w" ),
374
- ("std_mask" , "inputnode.std_mask" ),
375
- ]),
376
- (t1w_template_wf , anat_reports_wf , [
377
- ("outputnode.out_report" , "inputnode.t1w_conform_report" ),
378
- ]),
379
- (anat_norm_wf , anat_reports_wf , [
380
- ("poutputnode.template" , "inputnode.template" ),
381
- ]),
382
- # derivatives
383
- (t1w_template_wf , anat_derivatives_wf , [
384
- ("outputnode.valid_list" , "inputnode.source_files" ),
385
- ("outputnode.realign_xfms" , "inputnode.t1w_ref_xfms" ),
386
- ]),
387
- (t2w_template_wf , anat_derivatives_wf , [
388
- ("outputnode.valid_list" , "inputnode.t2w_source_files" ),
389
- ]),
390
- (anat_norm_wf , anat_derivatives_wf , [
391
- ("outputnode.template" , "inputnode.template" ),
392
- ("outputnode.anat2std_xfm" , "inputnode.anat2std_xfm" ),
393
- ("outputnode.std2anat_xfm" , "inputnode.std2anat_xfm" ),
394
- ]),
395
- (anat_ribbon_wf , anat_derivatives_wf , [
396
- ("outputnode.anat_ribbon" , "inputnode.anat_ribbon" ),
397
- ]),
398
- (anat_seg_wf , anat_derivatives_wf , [
399
- ("outputnode.anat_dseg" , "inputnode.t1w_dseg" ),
400
- ("outputnode.anat_tpms" , "inputnode.t1w_tpms" ),
401
- ]),
402
- ])
403
- # fmt:on
376
+ if precomp_aseg :
377
+ # Ensure the segmentation is conformed along with the T1w
378
+ t1w_template_wf .inputs .inputnode .anat_aseg = precomp_aseg
379
+ wf .connect ([
380
+ (t1w_template_wf , anat_seg_wf , [("outputnode.anat_aseg" , "inputnode.anat_aseg" )]),
381
+ ])
404
382
405
383
if not freesurfer :
406
384
return wf
@@ -419,20 +397,23 @@ def init_infant_anat_wf(
419
397
use_aseg = use_aseg ,
420
398
)
421
399
400
+ # Anatomical ribbon file using HCP signed-distance volume method
401
+ anat_ribbon_wf = init_anat_ribbon_wf ()
402
+
422
403
# fmt:off
423
404
wf .connect ([
424
405
(inputnode , surface_recon_wf , [
425
406
("subject_id" , "inputnode.subject_id" ),
426
- ("subjects_dir" , "inputnode.subjects_dir" ),
427
- ( "t2w" , "inputnode.t2w" ),
428
- ]),
407
+ ("subjects_dir" , "inputnode.subjects_dir" )]) ,
408
+ ( t2w_preproc_wf , surface_recon_wf , [
409
+ ( "outputnode.anat_preproc" , "inputnode.t2w" ) ]),
429
410
(anat_seg_wf , surface_recon_wf , [
430
411
("outputnode.anat_aseg" , "inputnode.ants_segs" ),
431
412
]),
432
413
(t1w_template_wf , surface_recon_wf , [
433
414
("outputnode.out_file" , "inputnode.t1w" ),
434
415
]),
435
- (t1w_preproc_wf , surface_recon_wf , [
416
+ (coregistration_wf , surface_recon_wf , [
436
417
("outputnode.t1w_brain" , "inputnode.skullstripped_t1" ),
437
418
("outputnode.t1w_preproc" , "inputnode.corrected_t1" ),
438
419
]),
@@ -446,7 +427,7 @@ def init_infant_anat_wf(
446
427
("outputnode.out_aparc" , "anat_aparc" ),
447
428
("outputnode.out_aseg" , "anat_aseg" ),
448
429
]),
449
- (t1w_preproc_wf , anat_ribbon_wf , [
430
+ (coregistration_wf , anat_ribbon_wf , [
450
431
("outputnode.t1w_mask" , "inputnode.t1w_mask" ),
451
432
]),
452
433
(surface_recon_wf , anat_ribbon_wf , [
@@ -455,6 +436,9 @@ def init_infant_anat_wf(
455
436
(anat_ribbon_wf , outputnode , [
456
437
("outputnode.anat_ribbon" , "anat_ribbon" )
457
438
]),
439
+ (anat_ribbon_wf , anat_derivatives_wf , [
440
+ ("outputnode.anat_ribbon" , "inputnode.anat_ribbon" ),
441
+ ]),
458
442
(surface_recon_wf , anat_reports_wf , [
459
443
("outputnode.subject_id" , "inputnode.subject_id" ),
460
444
("outputnode.subjects_dir" , "inputnode.subjects_dir" ),
0 commit comments