@@ -128,6 +128,8 @@ def init_bold_reg_wf(
128
128
Affine transform from T1 space to BOLD space (ITK format)
129
129
fallback
130
130
Boolean indicating whether BBR was rejected (mri_coreg registration returned)
131
+ metadata
132
+ Output metadata from the registration workflow
131
133
132
134
See Also
133
135
--------
@@ -154,7 +156,7 @@ def init_bold_reg_wf(
154
156
)
155
157
156
158
outputnode = pe .Node (
157
- niu .IdentityInterface (fields = ['itk_bold_to_t1' , 'itk_t1_to_bold' , 'fallback' ]),
159
+ niu .IdentityInterface (fields = ['itk_bold_to_t1' , 'itk_t1_to_bold' , 'fallback' , 'metadata' ]),
158
160
name = 'outputnode' ,
159
161
)
160
162
@@ -188,6 +190,7 @@ def init_bold_reg_wf(
188
190
('outputnode.itk_bold_to_t1' , 'itk_bold_to_t1' ),
189
191
('outputnode.itk_t1_to_bold' , 'itk_t1_to_bold' ),
190
192
('outputnode.fallback' , 'fallback' ),
193
+ ('outputnode.metadata' , 'metadata' ),
191
194
]),
192
195
]) # fmt:skip
193
196
@@ -268,11 +271,14 @@ def init_bbreg_wf(
268
271
Affine transform from T1 space to BOLD space (ITK format)
269
272
fallback
270
273
Boolean indicating whether BBR was rejected (mri_coreg registration returned)
274
+ metadata
275
+ Output metadata from the registration workflow
271
276
272
277
"""
273
278
from nipype .interfaces .freesurfer import BBRegister
274
279
from niworkflows .engine .workflows import LiterateWorkflow as Workflow
275
280
from niworkflows .interfaces .nitransforms import ConcatenateXFMs
281
+ from niworkflows .interfaces .utility import DictMerge
276
282
277
283
from fmriprep .interfaces .patches import FreeSurferSource , MRICoreg
278
284
@@ -309,7 +315,7 @@ def init_bbreg_wf(
309
315
name = 'inputnode' ,
310
316
)
311
317
outputnode = pe .Node (
312
- niu .IdentityInterface (['itk_bold_to_t1' , 'itk_t1_to_bold' , 'fallback' ]),
318
+ niu .IdentityInterface (['itk_bold_to_t1' , 'itk_t1_to_bold' , 'fallback' , 'metadata' ]),
313
319
name = 'outputnode' ,
314
320
)
315
321
@@ -341,6 +347,8 @@ def init_bbreg_wf(
341
347
dof = bold2anat_dof ,
342
348
contrast_type = 't2' ,
343
349
out_lta_file = True ,
350
+ # Bug in nipype prevents using init_cost_file=True
351
+ init_cost_file = 'bbregister.initcost' ,
344
352
),
345
353
name = 'bbregister' ,
346
354
mem_gb = 12 ,
@@ -357,6 +365,12 @@ def init_bbreg_wf(
357
365
merge_ltas = pe .Node (niu .Merge (2 ), name = 'merge_ltas' , run_without_submitting = True )
358
366
concat_xfm = pe .Node (ConcatenateXFMs (inverse = True ), name = 'concat_xfm' )
359
367
368
+ # Set up GeneratedBy metadata and add a merge node for cost, if available
369
+ gen_by = pe .Node (niu .Merge (2 ), run_without_submitting = True , name = 'gen_by' )
370
+ select_gen = pe .Node (niu .Select (index = 0 ), run_without_submitting = True , name = 'select_gen' )
371
+ metadata = pe .Node (niu .Merge (2 ), run_without_submitting = True , name = 'metadata' )
372
+ merge_meta = pe .Node (DictMerge (), run_without_submitting = True , name = 'merge_meta' )
373
+
360
374
workflow .connect ([
361
375
(inputnode , merge_ltas , [('fsnative2t1w_xfm' , 'in2' )]),
362
376
# Wire up the co-registration alternatives
@@ -365,10 +379,20 @@ def init_bbreg_wf(
365
379
(merge_ltas , concat_xfm , [('out' , 'in_xfms' )]),
366
380
(concat_xfm , outputnode , [('out_xfm' , 'itk_bold_to_t1' )]),
367
381
(concat_xfm , outputnode , [('out_inv' , 'itk_t1_to_bold' )]),
382
+ # Wire up the metadata alternatives
383
+ (gen_by , select_gen , [('out' , 'inlist' )]),
384
+ (select_gen , metadata , [('out' , 'in1' )]),
385
+ (metadata , merge_meta , [('out' , 'in_dicts' )]),
386
+ (merge_meta , outputnode , [('out_dict' , 'metadata' )]),
368
387
]) # fmt:skip
369
388
370
389
# Do not initialize with header, use mri_coreg
371
390
if bold2anat_init != 'header' :
391
+ gen_by .inputs .in2 = {
392
+ 'GeneratedBy' : [
393
+ {'Name' : 'mri_coreg' , 'Version' : mri_coreg .interface .version or '<unknown>' }
394
+ ]
395
+ }
372
396
workflow .connect ([
373
397
(inputnode , mri_coreg , [('subjects_dir' , 'subjects_dir' ),
374
398
('subject_id' , 'subject_id' ),
@@ -400,6 +424,26 @@ def init_bbreg_wf(
400
424
(bbregister , transforms , [('out_lta_file' , 'in1' )]),
401
425
]) # fmt:skip
402
426
427
+ gen_by .inputs .in1 = {
428
+ 'GeneratedBy' : [
429
+ {'Name' : 'bbregister' , 'Version' : bbregister .interface .version or '<unknown>' }
430
+ ]
431
+ }
432
+
433
+ costs = pe .Node (niu .Merge (2 ), run_without_submitting = True , name = 'costs' )
434
+ select_cost = pe .Node (niu .Select (index = 0 ), run_without_submitting = True , name = 'select_cost' )
435
+ read_cost = pe .Node (niu .Function (function = _read_cost ), name = 'read_cost' )
436
+
437
+ workflow .connect ([
438
+ (bbregister , costs , [
439
+ ('min_cost_file' , 'in1' ),
440
+ ('init_cost_file' , 'in2' ),
441
+ ]),
442
+ (costs , select_cost , [('out' , 'inlist' )]),
443
+ (select_cost , read_cost , [('out' , 'cost_file' )]),
444
+ (read_cost , metadata , [('out' , 'in2' )]),
445
+ ]) # fmt:skip
446
+
403
447
# Short-circuit workflow building, use boundary-based registration
404
448
if use_bbr is True :
405
449
outputnode .inputs .fallback = False
@@ -413,6 +457,8 @@ def init_bbreg_wf(
413
457
(transforms , compare_transforms , [('out' , 'lta_list' )]),
414
458
(compare_transforms , outputnode , [('out' , 'fallback' )]),
415
459
(compare_transforms , select_transform , [('out' , 'index' )]),
460
+ (compare_transforms , select_gen , [('out' , 'index' )]),
461
+ (compare_transforms , select_cost , [('out' , 'index' )]),
416
462
]) # fmt:skip
417
463
418
464
return workflow
@@ -493,6 +539,8 @@ def init_fsl_bbr_wf(
493
539
Affine transform from T1 space to BOLD space (ITK format)
494
540
fallback
495
541
Boolean indicating whether BBR was rejected (rigid FLIRT registration returned)
542
+ metadata
543
+ Output metadata from the registration workflow
496
544
497
545
"""
498
546
from nipype .interfaces .freesurfer import MRICoreg
@@ -532,7 +580,7 @@ def init_fsl_bbr_wf(
532
580
name = 'inputnode' ,
533
581
)
534
582
outputnode = pe .Node (
535
- niu .IdentityInterface (['itk_bold_to_t1' , 'itk_t1_to_bold' , 'fallback' ]),
583
+ niu .IdentityInterface (['itk_bold_to_t1' , 'itk_t1_to_bold' , 'fallback' , 'metadata' ]),
536
584
name = 'outputnode' ,
537
585
)
538
586
@@ -549,6 +597,9 @@ def init_fsl_bbr_wf(
549
597
'T2w intermediate for FSL is not implemented, registering with T1w instead.'
550
598
)
551
599
600
+ metadata = pe .Node (niu .Merge (2 ), run_without_submitting = True , name = 'metadata' )
601
+ select_meta = pe .Node (niu .Select (index = 0 ), run_without_submitting = True , name = 'select_meta' )
602
+
552
603
# Mask T1w_preproc with T1w_mask to make T1w_brain
553
604
mask_t1w_brain = pe .Node (ApplyMask (), name = 'mask_t1w_brain' )
554
605
@@ -565,6 +616,12 @@ def init_fsl_bbr_wf(
565
616
mem_gb = DEFAULT_MEMORY_MIN_GB ,
566
617
)
567
618
619
+ metadata .inputs .in2 = {
620
+ 'GeneratedBy' : [
621
+ {'Name' : 'mri_coreg' , 'Version' : mri_coreg .interface .version or '<unknown>' }
622
+ ]
623
+ }
624
+
568
625
workflow .connect ([
569
626
(inputnode , mask_t1w_brain , [
570
627
('t1w_preproc' , 'in_file' ),
@@ -578,6 +635,9 @@ def init_fsl_bbr_wf(
578
635
('out_xfm' , 'itk_bold_to_t1' ),
579
636
('out_inv' , 'itk_t1_to_bold' ),
580
637
]),
638
+ # Wire up the metadata alternatives
639
+ (metadata , select_meta , [('out' , 'inlist' )]),
640
+ (select_meta , outputnode , [('out' , 'metadata' )]),
581
641
]) # fmt:skip
582
642
583
643
# Short-circuit workflow building, use rigid registration
@@ -604,6 +664,10 @@ def init_fsl_bbr_wf(
604
664
LOGGER .warning ('FSLDIR unset - using packaged BBR schedule' )
605
665
flt_bbr .inputs .schedule = data .load ('flirtsch/bbr.sch' )
606
666
667
+ metadata .inputs .in1 = {
668
+ 'GeneratedBy' : [{'Name' : 'flirt' , 'Version' : flt_bbr .interface .version or '<unknown>' }]
669
+ }
670
+
607
671
workflow .connect ([
608
672
(inputnode , wm_mask , [('t1w_dseg' , 'in_seg' )]),
609
673
(inputnode , flt_bbr , [('in_file' , 'in_file' )]),
@@ -658,6 +722,8 @@ def init_fsl_bbr_wf(
658
722
(transforms , select_transform , [('out' , 'inlist' )]),
659
723
(compare_transforms , select_transform , [('out' , 'index' )]),
660
724
(select_transform , xfm2itk , [('out' , 'in_xfm' )]),
725
+ # Select metadata
726
+ (compare_transforms , select_meta , [('out' , 'index' )]),
661
727
]) # fmt:skip
662
728
663
729
return workflow
@@ -749,3 +815,10 @@ def _conditional_downsampling(in_file, in_mask, zoom_th=4.0):
749
815
nb .Nifti1Image (newmaskdata , newmask .affine , hdr ).to_filename (out_mask )
750
816
751
817
return str (out_file ), str (out_mask )
818
+
819
+
820
+ def _read_cost (cost_file ) -> dict [str , float ]:
821
+ """Read a cost from a file."""
822
+ # Cost file contains mincost, WM intensity, Ctx intensity, Pct Contrast
823
+ with open (cost_file ) as fobj :
824
+ return {'FinalCost' : float (fobj .read ().split ()[0 ])}
0 commit comments