1
1
"""Prepare anatomical images for processing."""
2
+ from __future__ import annotations
3
+
2
4
from nipype .interfaces import utility as niu
3
5
from nipype .pipeline import engine as pe
4
6
from niworkflows .engine .workflows import LiterateWorkflow
7
+ from niworkflows .interfaces .fixes import FixHeaderApplyTransforms as ApplyTransforms
5
8
6
9
7
10
def init_anat_template_wf (
@@ -12,6 +15,8 @@ def init_anat_template_wf(
12
15
longitudinal : bool = False ,
13
16
bspline_fitting_distance : int = 200 ,
14
17
sloppy : bool = False ,
18
+ has_mask : bool = False ,
19
+ has_aseg : bool = False ,
15
20
name : str = "anat_template_wf" ,
16
21
) -> LiterateWorkflow :
17
22
"""
@@ -45,6 +50,11 @@ def init_anat_template_wf(
45
50
------
46
51
anat_files
47
52
List of structural images
53
+ anat_mask
54
+ mask_reference
55
+ anat_aseg
56
+ aseg_reference
57
+
48
58
Outputs
49
59
-------
50
60
anat_ref
@@ -55,12 +65,15 @@ def init_anat_template_wf(
55
65
List of affine transforms to realign input images to final reference
56
66
out_report
57
67
Conformation report
68
+ anat_mask
69
+ Mask (if provided), resampled to the anatomical reference
70
+ anat_aseg
71
+ Aseg (if provided), resampled to the anatomical reference
58
72
"""
59
73
from nipype .interfaces .ants import N4BiasFieldCorrection
60
74
from nipype .interfaces .image import Reorient
61
75
from niworkflows .interfaces .freesurfer import PatchedLTAConvert as LTAConvert
62
76
from niworkflows .interfaces .freesurfer import StructuralReference
63
- from niworkflows .interfaces .header import ValidateImage
64
77
from niworkflows .interfaces .images import Conform , TemplateDimensions
65
78
from niworkflows .interfaces .nibabel import IntensityClip
66
79
from niworkflows .interfaces .nitransforms import ConcatenateXFMs
@@ -80,7 +93,18 @@ def init_anat_template_wf(
80
93
"""
81
94
82
95
inputnode = pe .Node (
83
- niu .IdentityInterface (fields = ["anat_files" , "anat_mask" , "anat_aseg" ]), name = "inputnode"
96
+ niu .IdentityInterface (
97
+ fields = [
98
+ "anat_files" ,
99
+ # Each derivative requires a reference file, which will be used to find which
100
+ # transform to apply in the case when multiple runs are present
101
+ "anat_mask" ,
102
+ "mask_reference" ,
103
+ "anat_aseg" ,
104
+ "aseg_reference" ,
105
+ ]
106
+ ),
107
+ name = "inputnode" ,
84
108
)
85
109
outputnode = pe .Node (
86
110
niu .IdentityInterface (
@@ -89,6 +113,8 @@ def init_anat_template_wf(
89
113
"anat_valid_list" ,
90
114
"anat_realign_xfm" ,
91
115
"out_report" ,
116
+ "anat_mask" ,
117
+ "anat_aseg" ,
92
118
],
93
119
),
94
120
name = "outputnode" ,
@@ -110,6 +136,28 @@ def init_anat_template_wf(
110
136
])
111
137
# fmt:on
112
138
139
+ if has_mask :
140
+ mask_conform = pe .Node (Conform (), name = 'mask_conform' )
141
+ # fmt:off
142
+ wf .connect ([
143
+ (inputnode , mask_conform , [('anat_mask' , 'in_file' )]),
144
+ (anat_ref_dimensions , mask_conform , [
145
+ ('target_zooms' , 'target_zooms' ),
146
+ ('target_shape' , 'target_shape' )]),
147
+ ])
148
+ # fmt:on
149
+
150
+ if has_aseg :
151
+ aseg_conform = pe .Node (Conform (), name = 'aseg_conform' )
152
+ # fmt:off
153
+ wf .connect ([
154
+ (inputnode , aseg_conform , [('anat_aseg' , 'in_file' )]),
155
+ (anat_ref_dimensions , aseg_conform , [
156
+ ('target_zooms' , 'target_zooms' ),
157
+ ('target_shape' , 'target_shape' )]),
158
+ ])
159
+ # fmt:on
160
+
113
161
if num_files == 1 :
114
162
get1st = pe .Node (niu .Select (index = [0 ]), name = "get1st" )
115
163
outputnode .inputs .anat_realign_xfm = [
@@ -122,6 +170,10 @@ def init_anat_template_wf(
122
170
(get1st , outputnode , [('out' , 'anat_ref' )]),
123
171
])
124
172
# fmt:on
173
+ if has_mask :
174
+ wf .connect (mask_conform , 'out_file' , outputnode , 'anat_mask' )
175
+ if has_aseg :
176
+ wf .connect (aseg_conform , 'out_file' , outputnode , 'anat_aseg' )
125
177
return wf
126
178
127
179
anat_conform_xfm = pe .MapNode (
@@ -180,6 +232,52 @@ def init_anat_template_wf(
180
232
run_without_submitting = True ,
181
233
)
182
234
235
+ if has_mask :
236
+ mask_ref_idx = pe .Node (
237
+ niu .Function (function = get_reference ), name = 'mask_ref_idx' , run_without_submitting = True
238
+ )
239
+ mask_xfm = pe .Node (niu .Select (), name = 'mask_xfm' , run_without_submitting = True )
240
+ applyxfm_mask = pe .Node (
241
+ ApplyTransforms (interpolation = 'MultiLabel' ), name = 'applyxfm_mask' , mem_gb = 1
242
+ )
243
+ mask_reorient = pe .Node (Reorient (), name = "mask_reorient" )
244
+ # fmt:off
245
+ wf .connect ([
246
+ (inputnode , mask_ref_idx , [('mask_reference' , 'anat_reference' )]),
247
+ (anat_ref_dimensions , mask_ref_idx , [('t1w_valid_list' , 'anatomicals' )]),
248
+ (concat_xfms , mask_xfm , [('out_xfm' , 'inlist' )]),
249
+ (mask_ref_idx , mask_xfm , [('out' , 'index' )]),
250
+ (mask_conform , applyxfm_mask , [('out_file' , 'input_image' )]),
251
+ (anat_reorient , applyxfm_mask , [('out_file' , 'reference_image' )]),
252
+ (mask_xfm , applyxfm_mask , [('out' , 'transforms' )]),
253
+ (applyxfm_mask , mask_reorient , [('output_image' , 'in_file' )]),
254
+ (mask_reorient , outputnode , [('out_file' , 'anat_mask' )]),
255
+ ])
256
+ # fmt:on
257
+
258
+ if has_aseg :
259
+ aseg_ref_idx = pe .Node (
260
+ niu .Function (function = get_reference ), name = 'aseg_ref_idx' , run_without_submitting = True
261
+ )
262
+ aseg_xfm = pe .Node (niu .Select (), name = 'aseg_xfm' , run_without_submitting = True )
263
+ applyxfm_aseg = pe .Node (
264
+ ApplyTransforms (interpolation = 'MultiLabel' ), name = 'applyxfm_aseg' , mem_gb = 1
265
+ )
266
+ aseg_reorient = pe .Node (Reorient (), name = "aseg_reorient" )
267
+ # fmt:off
268
+ wf .connect ([
269
+ (inputnode , aseg_ref_idx , [('aseg_reference' , 'anat_reference' )]),
270
+ (anat_ref_dimensions , aseg_ref_idx , [('t1w_valid_list' , 'anatomicals' )]),
271
+ (concat_xfms , aseg_xfm , [('out_xfm' , 'inlist' )]),
272
+ (aseg_ref_idx , aseg_xfm , [('out' , 'index' )]),
273
+ (aseg_conform , applyxfm_aseg , [('out_file' , 'input_image' )]),
274
+ (anat_reorient , applyxfm_aseg , [('out_file' , 'reference_image' )]),
275
+ (aseg_xfm , applyxfm_aseg , [('out' , 'transforms' )]),
276
+ (applyxfm_aseg , aseg_reorient , [('output_image' , 'in_file' )]),
277
+ (applyxfm_aseg , outputnode , [('out_file' , 'anat_aseg' )]),
278
+ ])
279
+ # fmt:on
280
+
183
281
def _set_threads (in_list , maximum ):
184
282
return min (len (in_list ), maximum )
185
283
@@ -204,3 +302,7 @@ def _set_threads(in_list, maximum):
204
302
])
205
303
# fmt:on
206
304
return wf
305
+
306
+
307
+ def get_reference (anatomicals : list , anat_reference : str ) -> int :
308
+ return anatomicals .index (anat_reference )
0 commit comments