1
1
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2
2
# vi: set ft=python sts=4 ts=4 sw=4 et:
3
3
"""Within-baby registration of a T1w into a T2w image."""
4
+ from typing import Optional
5
+
4
6
from nipype .interfaces import utility as niu
5
7
from nipype .pipeline import engine as pe
6
8
from pkg_resources import resource_filename as pkgr_fn
7
9
8
10
9
11
def init_coregistration_wf (
10
12
* ,
11
- bspline_fitting_distance = 200 ,
12
- mem_gb = 3.0 ,
13
- name = "coregistration_wf" ,
14
- omp_nthreads = None ,
15
- sloppy = False ,
16
- debug = False ,
13
+ bspline_fitting_distance : int = 200 ,
14
+ mem_gb : float = 3.0 ,
15
+ omp_nthreads : Optional [int ] = None ,
16
+ sloppy : bool = False ,
17
+ debug : bool = False ,
18
+ precomputed_mask : bool = False ,
19
+ name : str = "coregistration_wf" ,
17
20
):
18
21
"""
19
22
Set-up a T2w-to-T1w within-baby co-registration framework.
@@ -49,29 +52,35 @@ def init_coregistration_wf(
49
52
Run in *sloppy* mode.
50
53
debug : :obj:`bool`
51
54
Produce intermediate registration files
55
+ precomputed_mask : :obj:`bool`
56
+ A precomputed mask for the T1w is available. In this case, generate a
57
+ quick mask to assist in coregistration, but use the precomputed mask
58
+ as the final output.
52
59
53
60
54
61
Inputs
55
62
------
56
63
in_t1w : :obj:`str`
57
- The unprocessed input T1w image.
58
- in_t2w_preproc : :obj:`str`
59
- The preprocessed input T2w image, from the brain extraction workflow.
64
+ The preprocessed input T1w image (Denoising/INU/Clipping)
65
+ in_t2w : :obj:`str`
66
+ The preprocessed input T2w image (Denoising/INU/Clipping)
60
67
in_mask : :obj:`str`
61
- The brainmask, as obtained in T2w space.
68
+ The brainmask.
69
+ If `precomputed_mask` is False, will be in T2w space.
70
+ If `precomputed_mask` is True, will be in T1w space.
62
71
in_probmap : :obj:`str`
63
72
The probabilistic brainmask, as obtained in T2w space.
64
73
65
74
Outputs
66
75
-------
67
- t1w_preproc : :obj:`str`
68
- The preprocessed T1w image (INU and clipping).
69
- t2w_preproc : :obj:`str`
76
+ t1w_coreg : :obj:`str`
77
+ The preprocessed T1w image (INU and clipping), in its native space .
78
+ t2w_coreg : :obj:`str`
70
79
The preprocessed T2w image (INU and clipping), aligned into the T1w's space.
71
80
t1w_brain : :obj:`str`
72
81
The preprocessed, brain-extracted T1w image.
73
82
t1w_mask : :obj:`str`
74
- The binary brainmask projected from the T2w .
83
+ The binary brainmask in T1w space .
75
84
t1w2t2w_xfm : :obj:`str`
76
85
The T1w-to-T2w mapping.
77
86
@@ -81,10 +90,12 @@ def init_coregistration_wf(
81
90
from niworkflows .interfaces .fixes import FixHeaderRegistration as Registration
82
91
from niworkflows .interfaces .nibabel import ApplyMask , Binarize , BinaryDilation
83
92
93
+ from nibabies .utils .misc import get_file
94
+
84
95
workflow = pe .Workflow (name )
85
96
86
97
inputnode = pe .Node (
87
- niu .IdentityInterface (fields = ["in_t1w" , "in_t2w_preproc " , "in_mask" , "in_probmap" ]),
98
+ niu .IdentityInterface (fields = ["in_t1w" , "in_t2w " , "in_mask" , "in_probmap" ]),
88
99
name = "inputnode" ,
89
100
)
90
101
outputnode = pe .Node (
@@ -100,15 +111,14 @@ def init_coregistration_wf(
100
111
name = "outputnode" ,
101
112
)
102
113
103
- fixed_masks_arg = pe .Node (niu .Merge (3 ), name = "fixed_masks_arg" , run_without_submitting = True )
104
-
105
114
# Dilate t2w mask for easier t1->t2 registration
115
+ fixed_masks_arg = pe .Node (niu .Merge (3 ), name = "fixed_masks_arg" , run_without_submitting = True )
106
116
reg_mask = pe .Node (BinaryDilation (radius = 8 , iterations = 3 ), name = "reg_mask" )
107
117
refine_mask = pe .Node (BinaryDilation (radius = 8 , iterations = 1 ), name = "refine_mask" )
108
118
109
- # Set up T2w -> T1w within-subject registration
119
+ # Set up T1w -> T2w within-subject registration
110
120
coreg = pe .Node (
111
- Registration (from_file = pkgr_fn ("nibabies.data " , "within_subject_t1t2.json" )),
121
+ Registration (from_file = get_file ("nibabies" , "data/ within_subject_t1t2.json" )),
112
122
name = "coreg" ,
113
123
n_procs = omp_nthreads ,
114
124
mem_gb = mem_gb ,
@@ -119,10 +129,6 @@ def init_coregistration_wf(
119
129
coreg .inputs .output_inverse_warped_image = sloppy
120
130
coreg .inputs .output_warped_image = sloppy
121
131
122
- map_mask = pe .Node (ApplyTransforms (interpolation = "Gaussian" ), name = "map_mask" , mem_gb = 1 )
123
- map_t2w = pe .Node (ApplyTransforms (interpolation = "BSpline" ), name = "map_t2w" , mem_gb = 1 )
124
- thr_mask = pe .Node (Binarize (thresh_low = 0.80 ), name = "thr_mask" )
125
-
126
132
final_n4 = pe .Node (
127
133
N4BiasFieldCorrection (
128
134
dimension = 3 ,
@@ -137,40 +143,69 @@ def init_coregistration_wf(
137
143
n_procs = omp_nthreads ,
138
144
name = "final_n4" ,
139
145
)
146
+ # Move the T2w into T1w space, and apply the mask to the T1w
147
+ map_t2w = pe .Node (ApplyTransforms (interpolation = "BSpline" ), name = "map_t2w" , mem_gb = 1 )
140
148
apply_mask = pe .Node (ApplyMask (), name = "apply_mask" )
141
149
142
150
# fmt: off
143
151
workflow .connect ([
144
- (inputnode , map_mask , [("in_t1w" , "reference_image" )]),
145
152
(inputnode , final_n4 , [("in_t1w" , "input_image" )]),
146
153
(inputnode , coreg , [("in_t1w" , "moving_image" ),
147
- ("in_t2w_preproc" , "fixed_image" )]),
148
- (inputnode , map_mask , [("in_probmap" , "input_image" )]),
149
- (inputnode , reg_mask , [("in_mask" , "in_file" )]),
150
- (inputnode , refine_mask , [("in_mask" , "in_file" )]),
151
- (reg_mask , fixed_masks_arg , [("out_file" , "in1" )]),
152
- (reg_mask , fixed_masks_arg , [("out_file" , "in2" )]),
154
+ ("in_t2w" , "fixed_image" )]),
155
+ (reg_mask , fixed_masks_arg , [
156
+ ("out_file" , "in1" ),
157
+ ("out_file" , "in2" )]),
153
158
(refine_mask , fixed_masks_arg , [("out_file" , "in3" )]),
154
- (inputnode , map_t2w , [("in_t1w" , "reference_image" )]),
155
- (inputnode , map_t2w , [("in_t2w_preproc" , "input_image" )]),
159
+ (inputnode , map_t2w , [
160
+ ("in_t1w" , "reference_image" ),
161
+ ("in_t2w" , "input_image" )]),
156
162
(fixed_masks_arg , coreg , [("out" , "fixed_image_masks" )]),
157
- (coreg , map_mask , [
158
- ("reverse_transforms" , "transforms" ),
159
- ("reverse_invert_flags" , "invert_transform_flags" ),
160
- ]),
161
163
(coreg , map_t2w , [
162
164
("reverse_transforms" , "transforms" ),
163
165
("reverse_invert_flags" , "invert_transform_flags" ),
164
166
]),
165
- (map_mask , thr_mask , [("output_image" , "in_file" )]),
166
- (map_mask , final_n4 , [("output_image" , "weight_image" )]),
167
167
(final_n4 , apply_mask , [("output_image" , "in_file" )]),
168
- (thr_mask , apply_mask , [("out_mask" , "in_mask" )]),
169
168
(final_n4 , outputnode , [("output_image" , "t1w_preproc" )]),
170
169
(map_t2w , outputnode , [("output_image" , "t2w_preproc" )]),
171
- (thr_mask , outputnode , [("out_mask" , "t1w_mask" )]),
172
170
(apply_mask , outputnode , [("out_file" , "t1w_brain" )]),
173
171
(coreg , outputnode , [("forward_transforms" , "t1w2t2w_xfm" )]),
174
172
])
175
173
# fmt: on
174
+
175
+ if precomputed_mask :
176
+ # The input mask is already in T1w space.
177
+ # Generate a quick, rough mask of the T2w to be used to facilitate co-registration.
178
+ from sdcflows .interfaces .brainmask import BrainExtraction
179
+
180
+ masker = pe .Node (BrainExtraction (), name = "t2w-masker" )
181
+ # fmt:off
182
+ workflow .connect ([
183
+ (masker , reg_mask , [("out_mask" , "in_file" )]),
184
+ (masker , refine_mask , [("out_mask" , "in_file" )]),
185
+ (inputnode , apply_mask , [("in_mask" , "in_mask" )]),
186
+ (inputnode , outputnode , [("in_mask" , "t1w_mask" )]),
187
+ ])
188
+ # fmt:on
189
+ else :
190
+ # The T2w mask from the brain extraction workflow will be mapped to T1w space
191
+ map_mask = pe .Node (ApplyTransforms (interpolation = "Gaussian" ), name = "map_mask" , mem_gb = 1 )
192
+ thr_mask = pe .Node (Binarize (thresh_low = 0.80 ), name = "thr_mask" )
193
+ # fmt:off
194
+ workflow .connect ([
195
+ (inputnode , map_mask , [
196
+ ("in_t1w" , "reference_image" ),
197
+ ("in_probmap" , "input_image" )]),
198
+ (inputnode , reg_mask , [("in_mask" , "in_file" )]),
199
+ (inputnode , refine_mask , [("in_mask" , "in_file" )]),
200
+ (coreg , map_mask , [
201
+ ("reverse_transforms" , "transforms" ),
202
+ ("reverse_invert_flags" , "invert_transform_flags" )]),
203
+ (map_mask , thr_mask , [("output_image" , "in_file" )]),
204
+ (map_mask , final_n4 , [("output_image" , "weight_image" )]),
205
+ (final_n4 , apply_mask , [("output_image" , "in_file" )]),
206
+ (final_n4 , outputnode , [("output_image" , "t1w_preproc" )]),
207
+ (thr_mask , outputnode , [("out_mask" , "t1w_mask" )]),
208
+ (thr_mask , apply_mask , [("out_mask" , "in_mask" )]),
209
+ ])
210
+ # fmt:on
176
211
return workflow
0 commit comments