2323"""
2424Estimating the susceptibility distortions without fieldmaps.
2525"""
26+
2627from nipype .pipeline import engine as pe
2728from nipype .interfaces import utility as niu
29+ from nipype .interfaces .base import Undefined
2830from niworkflows .engine .workflows import LiterateWorkflow as Workflow
2931
3032from ... import data
@@ -46,6 +48,8 @@ def init_syn_sdc_wf(
4648 debug = False ,
4749 name = "syn_sdc_wf" ,
4850 omp_nthreads = 1 ,
51+ sd_prior = True ,
52+ ** kwargs ,
4953):
5054 """
5155 Build the *fieldmap-less* susceptibility-distortion estimation workflow.
@@ -117,7 +121,6 @@ def init_syn_sdc_wf(
117121 FixHeaderRegistration as Registration ,
118122 )
119123 from niworkflows .interfaces .nibabel import (
120- Binarize ,
121124 IntensityClip ,
122125 RegridToZooms ,
123126 )
@@ -171,7 +174,6 @@ def init_syn_sdc_wf(
171174 name = "warp_dir" ,
172175 )
173176 warp_dir .inputs .nlevels = 2
174- atlas_msk = pe .Node (Binarize (thresh_low = atlas_threshold ), name = "atlas_msk" )
175177 anat_dilmsk = pe .Node (BinaryDilation (), name = "anat_dilmsk" )
176178 amask2epi = pe .Node (
177179 ApplyTransforms (interpolation = "MultiLabel" , transforms = "identity" ),
@@ -208,7 +210,7 @@ def init_syn_sdc_wf(
208210 )
209211
210212 fixed_masks = pe .Node (
211- niu .Merge (3 ),
213+ niu .Merge (2 ),
212214 name = "fixed_masks" ,
213215 mem_gb = DEFAULT_MEMORY_MIN_GB ,
214216 run_without_submitting = True ,
@@ -220,9 +222,7 @@ def init_syn_sdc_wf(
220222
221223 # SyN Registration Core
222224 syn = pe .Node (
223- Registration (
224- from_file = data .load (f"sd_syn{ '_sloppy' * sloppy } .json" )
225- ),
225+ Registration (from_file = data .load (f"sd_syn{ '_sloppy' * sloppy } .json" )),
226226 name = "syn" ,
227227 n_procs = omp_nthreads ,
228228 )
@@ -233,9 +233,7 @@ def init_syn_sdc_wf(
233233 syn .inputs .args = "--write-interval-volumes 2"
234234
235235 # Extract the corresponding fieldmap in Hz
236- extract_field = pe .Node (
237- DisplacementsField2Fieldmap (), name = "extract_field"
238- )
236+ extract_field = pe .Node (DisplacementsField2Fieldmap (), name = "extract_field" )
239237
240238 unwarp = pe .Node (ApplyCoeffsField (jacobian = False ), name = "unwarp" )
241239
@@ -267,7 +265,6 @@ def init_syn_sdc_wf(
267265 workflow .connect ([
268266 (inputnode , readout_time , [(("epi_ref" , _pop ), "in_file" ),
269267 (("epi_ref" , _pull ), "metadata" )]),
270- (inputnode , atlas_msk , [("sd_prior" , "in_file" )]),
271268 (inputnode , clip_epi , [(("epi_ref" , _pop ), "in_file" )]),
272269 (inputnode , unwarp , [(("epi_ref" , _pop ), "in_data" )]),
273270 (inputnode , amask2epi , [("epi_mask" , "reference_image" )]),
@@ -293,7 +290,6 @@ def init_syn_sdc_wf(
293290 (lap_epi , lap_epi_norm , [("output_image" , "in_file" )]),
294291 (lap_epi_norm , epi_merge , [("out" , "in2" )]),
295292 (find_zooms , zooms_epi , [("out" , "zooms" )]),
296- (atlas_msk , fixed_masks , [("out_mask" , "in3" )]),
297293 (anat_dilmsk , amask2epi , [("out_file" , "input_image" )]),
298294 (amask2epi , epi_umask , [("output_image" , "in2" )]),
299295 (readout_time , warp_dir , [("pe_direction" , "pe_dir" )]),
@@ -331,6 +327,7 @@ def init_syn_preprocessing_wf(
331327 omp_nthreads = 1 ,
332328 auto_bold_nss = False ,
333329 t1w_inversion = False ,
330+ sd_prior = True ,
334331):
335332 """
336333 Prepare EPI references and co-registration to anatomical for SyN.
@@ -356,6 +353,8 @@ def init_syn_preprocessing_wf(
356353 of BOLD images.
357354 t1w_inversion : :obj:`bool`
358355 Run T1w intensity inversion so that it looks more like a T2 contrast.
356+ sd_prior : :obj:`bool`
357+ Enable using a prior map to regularize the SyN cost function.
359358
360359 Inputs
361360 ------
@@ -426,26 +425,6 @@ def init_syn_preprocessing_wf(
426425
427426 deob_epi = pe .Node (Deoblique (), name = "deob_epi" )
428427
429- # Mapping & preparing prior knowledge
430- # Concatenate transform files:
431- # 1) MNI -> anat; 2) ATLAS -> MNI
432- transform_list = pe .Node (
433- niu .Merge (3 ),
434- name = "transform_list" ,
435- mem_gb = DEFAULT_MEMORY_MIN_GB ,
436- run_without_submitting = True ,
437- )
438- transform_list .inputs .in3 = data .load ("fmap_atlas_2_MNI152NLin2009cAsym_affine.mat" )
439- prior2epi = pe .Node (
440- ApplyTransforms (
441- invert_transform_flags = [True , False , False ],
442- input_image = str (data .load ("fmap_atlas.nii.gz" )),
443- ),
444- name = "prior2epi" ,
445- n_procs = omp_nthreads ,
446- mem_gb = 0.3 ,
447- )
448-
449428 anat2epi = pe .Node (
450429 ApplyTransforms (invert_transform_flags = [True ]),
451430 name = "anat2epi" ,
@@ -502,9 +481,44 @@ def _remove_first_mask(in_file):
502481
503482 sampling_ref = pe .Node (GenerateSamplingReference (), name = "sampling_ref" )
504483
505- # fmt:off
484+ if sd_prior :
485+ # Mapping & preparing prior knowledge
486+ # Concatenate transform files:
487+ # 1) MNI -> anat; 2) ATLAS -> MNI
488+ transform_list = pe .Node (
489+ niu .Merge (3 ),
490+ name = "transform_list" ,
491+ mem_gb = DEFAULT_MEMORY_MIN_GB ,
492+ run_without_submitting = True ,
493+ )
494+ transform_list .inputs .in3 = data .load (
495+ "fmap_atlas_2_MNI152NLin2009cAsym_affine.mat"
496+ )
497+ prior2epi = pe .Node (
498+ ApplyTransforms (
499+ invert_transform_flags = [True , False , False ],
500+ input_image = str (data .load ("fmap_atlas.nii.gz" )),
501+ ),
502+ name = "prior2epi" ,
503+ n_procs = omp_nthreads ,
504+ mem_gb = 0.3 ,
505+ )
506+
507+ workflow .connect ([
508+ (inputnode , transform_list , [("std2anat_xfm" , "in2" )]),
509+ (epi2anat , transform_list , [("forward_transforms" , "in1" )]),
510+ (transform_list , prior2epi , [("out" , "transforms" )]),
511+ (sampling_ref , prior2epi , [("out_file" , "reference_image" )]),
512+ (prior2epi , outputnode , [("output_image" , "sd_prior" )]),
513+ ]) # fmt:skip
514+
515+ else :
516+ # no prior to be used
517+ # MG: Future goal is to allow using alternative mappings
518+ # i.e. in the case of infants, where priors change depending on development
519+ outputnode .inputs .sd_prior = Undefined
520+
506521 workflow .connect ([
507- (inputnode , transform_list , [("std2anat_xfm" , "in2" )]),
508522 (inputnode , epi_reference_wf , [("in_epis" , "inputnode.in_files" )]),
509523 (inputnode , merge_output , [("in_meta" , "meta_list" )]),
510524 (inputnode , anat_dilmsk , [("mask_anat" , "in_file" )]),
@@ -523,9 +537,6 @@ def _remove_first_mask(in_file):
523537 (epi_dilmsk , epi2anat , [
524538 (("out_file" , _remove_first_mask ), "moving_image_masks" )]),
525539 (deob_epi , sampling_ref , [("out_file" , "fixed_image" )]),
526- (epi2anat , transform_list , [("forward_transforms" , "in1" )]),
527- (transform_list , prior2epi , [("out" , "transforms" )]),
528- (sampling_ref , prior2epi , [("out_file" , "reference_image" )]),
529540 (ref_anat , anat2epi , [("output_image" , "input_image" )]),
530541 (epi2anat , anat2epi , [("forward_transforms" , "transforms" )]),
531542 (sampling_ref , anat2epi , [("out_file" , "reference_image" )]),
@@ -536,9 +547,7 @@ def _remove_first_mask(in_file):
536547 (mask_dtype , outputnode , [("out" , "anat_mask" )]),
537548 (merge_output , outputnode , [("out" , "epi_ref" )]),
538549 (epi_brain , outputnode , [("out_mask" , "epi_mask" )]),
539- (prior2epi , outputnode , [("output_image" , "sd_prior" )]),
540- ])
541- # fmt:on
550+ ]) # fmt:skip
542551
543552 if debug :
544553 from niworkflows .interfaces .nibabel import RegridToZooms
0 commit comments