2424
2525import typing as ty
2626
27+ import bids
2728from nipype import logging
2829from nipype .interfaces import (
2930 freesurfer as fs ,
4748from niworkflows .interfaces .freesurfer import (
4849 StructuralReference ,
4950)
51+ from niworkflows .interfaces .gradunwarp import GradUnwarp
5052from niworkflows .interfaces .header import ValidateImage
5153from niworkflows .interfaces .images import Conform , TemplateDimensions
5254from niworkflows .interfaces .nibabel import ApplyMask , Binarize
5355from niworkflows .interfaces .nitransforms import ConcatenateXFMs
5456from niworkflows .utils .misc import add_suffix
5557from niworkflows .utils .spaces import Reference , SpatialReferences
58+ from niworkflows .workflows .gradunwarp import init_gradunwarp_wf
5659
5760from ..data import load_resource
5861from ..interfaces import DerivativesDataSink
9497def init_anat_preproc_wf (
9598 * ,
9699 bids_root : str ,
100+ layout : bids .BIDSLayout ,
97101 output_dir : str ,
98102 freesurfer : bool ,
99103 hires : bool ,
@@ -113,6 +117,7 @@ def init_anat_preproc_wf(
113117 name : str = 'anat_preproc_wf' ,
114118 skull_strip_fixed_seed : bool = False ,
115119 fs_no_resume : bool = False ,
120+ gradunwarp_file : str | None = None ,
116121):
117122 """
118123 Stage the anatomical preprocessing steps of *sMRIPrep*.
@@ -150,6 +155,8 @@ def init_anat_preproc_wf(
150155 ----------
151156 bids_root : :obj:`str`
152157 Path of the input BIDS dataset root
158+ layout : BIDSLayout object
159+ BIDS dataset layout
153160 output_dir : :obj:`str`
154161 Directory in which to save derivatives
155162 freesurfer : :obj:`bool`
@@ -189,6 +196,8 @@ def init_anat_preproc_wf(
189196 EXPERT: Import pre-computed FreeSurfer reconstruction without resuming.
190197 The user is responsible for ensuring that all necessary files are present.
191198 (default: ``False``).
199+ gradunwarp_file : :obj:`str`, optional
200+ Gradient unwarping filename (default: None)
192201
193202 Inputs
194203 ------
@@ -265,6 +274,7 @@ def init_anat_preproc_wf(
265274
266275 anat_fit_wf = init_anat_fit_wf (
267276 bids_root = bids_root ,
277+ layout = layout ,
268278 output_dir = output_dir ,
269279 freesurfer = freesurfer ,
270280 hires = hires ,
@@ -282,6 +292,7 @@ def init_anat_preproc_wf(
282292 omp_nthreads = omp_nthreads ,
283293 skull_strip_fixed_seed = skull_strip_fixed_seed ,
284294 fs_no_resume = fs_no_resume ,
295+ gradunwarp_file = gradunwarp_file ,
285296 )
286297 template_iterator_wf = init_template_iterator_wf (spaces = spaces , sloppy = sloppy )
287298 ds_std_volumes_wf = init_ds_anat_volumes_wf (
@@ -448,6 +459,7 @@ def init_anat_preproc_wf(
448459def init_anat_fit_wf (
449460 * ,
450461 bids_root : str ,
462+ layout : bids .BIDSLayout ,
451463 output_dir : str ,
452464 freesurfer : bool ,
453465 hires : bool ,
@@ -466,6 +478,7 @@ def init_anat_fit_wf(
466478 name = 'anat_fit_wf' ,
467479 skull_strip_fixed_seed : bool = False ,
468480 fs_no_resume : bool = False ,
481+ gradunwarp_file : str | None = None ,
469482):
470483 """
471484 Stage the anatomical preprocessing steps of *sMRIPrep*.
@@ -511,6 +524,8 @@ def init_anat_fit_wf(
511524 ----------
512525 bids_root : :obj:`str`
513526 Path of the input BIDS dataset root
527+ layout : BIDSLayout object
528+ BIDS dataset layout
514529 output_dir : :obj:`str`
515530 Directory in which to save derivatives
516531 freesurfer : :obj:`bool`
@@ -546,6 +561,12 @@ def init_anat_fit_wf(
546561 Do not use a random seed for skull-stripping - will ensure
547562 run-to-run replicability when used with --omp-nthreads 1
548563 (default: ``False``).
564+ fs_no_resume : bool
565+ EXPERT: Import pre-computed FreeSurfer reconstruction without resuming.
566+ The user is responsible for ensuring that all necessary files are present.
567+ (default: ``False``).
568+ gradunwarp_file : :obj:`str`, optional
569+ Gradient unwarping filename (default: None)
549570
550571 Inputs
551572 ------
@@ -760,12 +781,14 @@ def init_anat_fit_wf(
760781non-uniformity (INU) with `N4BiasFieldCorrection` [@n4], distributed with ANTs { ants_ver }
761782[@ants, RRID:SCR_004757]"""
762783 desc += '.\n ' if num_t1w > 1 else ', and used as T1w-reference throughout the workflow.\n '
763-
784+ t1w_metas = [ layout . get_file ( t ). get_metadata () for t in t1w ]
764785 anat_template_wf = init_anat_template_wf (
765786 longitudinal = longitudinal ,
766787 omp_nthreads = omp_nthreads ,
767788 num_files = num_t1w ,
768789 contrast = 'T1w' ,
790+ gradunwarp_file = gradunwarp_file ,
791+ metadata = t1w_metas ,
769792 name = 'anat_template_wf' ,
770793 )
771794 ds_template_wf = init_ds_template_wf (output_dir = output_dir , num_t1w = num_t1w )
@@ -1131,11 +1154,14 @@ def init_anat_fit_wf(
11311154
11321155 if t2w and not have_t2w :
11331156 LOGGER .info ('ANAT Stage 7: Creating T2w template' )
1157+ t2w_metas = [layout .get_file (t ).get_metadata () for t in t1w ]
11341158 t2w_template_wf = init_anat_template_wf (
11351159 longitudinal = longitudinal ,
11361160 omp_nthreads = omp_nthreads ,
11371161 num_files = len (t2w ),
11381162 contrast = 'T2w' ,
1163+ metadata = t2w_metas ,
1164+ gradunwarp_file = gradunwarp_file ,
11391165 name = 't2w_template_wf' ,
11401166 )
11411167 bbreg = pe .Node (
@@ -1376,6 +1402,8 @@ def init_anat_template_wf(
13761402 omp_nthreads : int ,
13771403 num_files : int ,
13781404 contrast : str ,
1405+ metadata : dict ,
1406+ gradunwarp_file : str | None = None ,
13791407 name : str = 'anat_template_wf' ,
13801408):
13811409 """
@@ -1388,7 +1416,8 @@ def init_anat_template_wf(
13881416
13891417 from smriprep.workflows.anatomical import init_anat_template_wf
13901418 wf = init_anat_template_wf(
1391- longitudinal=False, omp_nthreads=1, num_files=1, contrast="T1w"
1419+ longitudinal=False, omp_nthreads=1, num_files=1, contrast="T1w",
1420+ gradunwarp_file=None,
13921421 )
13931422
13941423 Parameters
@@ -1402,6 +1431,8 @@ def init_anat_template_wf(
14021431 Number of images
14031432 contrast : :obj:`str`
14041433 Name of contrast, for reporting purposes, e.g., T1w, T2w, PDw
1434+ gradunwarp_file : :obj:`str`, optional
1435+ Gradient unwarping filename (default: None)
14051436 name : :obj:`str`, optional
14061437 Workflow name (default: anat_template_wf)
14071438
@@ -1449,9 +1480,44 @@ def init_anat_template_wf(
14491480 )
14501481 anat_conform = pe .MapNode (Conform (), iterfield = 'in_file' , name = 'anat_conform' )
14511482
1483+ # -1 Gradient unwarping (optional)
1484+ if gradunwarp_file :
1485+ nds = [
1486+ (
1487+ meta .get ('NonlinearGradientCorrection' , None )
1488+ or 'ND' in meta .get ('ImageType' , [])
1489+ or False
1490+ )
1491+ for meta in metadata
1492+ ]
1493+ if any (nds ) and not all (nds ):
1494+ raise RuntimeError (
1495+ f'Inconsistent distortion correction metadata across { contrast } images.'
1496+ )
1497+ if not any (nds ):
1498+ gradunwarp_file = None
1499+ if gradunwarp_file :
1500+ gradunwarp_ver = GradUnwarp .version ()
1501+ workflow .__desc__ += f"""\
1502+ { "Each" if num_files > 1 else "The" } { contrast } image was corrected for gradient
1503+ non-linearity with `gradunwarp` [@gradunwarp] { gradunwarp_ver } [@gradunwarp]\n """
1504+ gradunwarp_wf = init_gradunwarp_wf ('gradunward_T1w' )
1505+ gradunwarp_wf .inputs .inputnode .grad_file = gradunwarp_file
1506+ # fmt:off
1507+ workflow .connect ([
1508+ (inputnode , gradunwarp_wf , [('anat_files' , 'inputnode.input_file' )]),
1509+ (gradunwarp_wf , anat_ref_dimensions , [('outputnode.corrected_file' , 't1w_list' )]),
1510+ ])
1511+ else :
1512+ workflow .connect (
1513+ [
1514+ (inputnode , anat_ref_dimensions , [('anat_files' , 't1w_list' )]),
1515+ ]
1516+ )
1517+ # fmt:on
1518+
14521519 # fmt:off
14531520 workflow .connect ([
1454- (inputnode , anat_ref_dimensions , [('anat_files' , 't1w_list' )]),
14551521 (anat_ref_dimensions , denoise , [('t1w_valid_list' , 'input_image' )]),
14561522 (anat_ref_dimensions , anat_conform , [
14571523 ('target_zooms' , 'target_zooms' ),
0 commit comments