29
29
30
30
31
31
def init_phdiff_wf (omp_nthreads , name = 'phdiff_wf' ):
32
- """
32
+ r """
33
33
Distortion correction of EPI sequences using phase-difference maps.
34
34
35
35
Estimates the fieldmap using a phase-difference image and one or more
36
36
magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)`
37
- acquisitions. The `original code was taken from nipype
37
+ acquisitions.
38
+ The most delicate bit of this workflow is the phase-unwrapping process: phase maps
39
+ are clipped in the range :math:`[0 \dotsb 2 \cdot \pi )`.
40
+ To find the integer number of offsets that make a region continously smooth with
41
+ its neighbour, FSL PRELUDE is run [Jenkinson1998]_.
42
+ FSL PRELUDE takes wrapped maps in the range 0 to 6.28, `as per the user guide
43
+ <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE/Guide#Step_2_-_Getting_.28wrapped.29_phase_in_radians>`__.
44
+ For the phase-difference maps, recentering back to :math:`[-\pi \dotsb \pi )` is necessary.
45
+ After some massaging and the application of the effective echo spacing parameter,
46
+ the phase-difference maps can be converted into a *B0 field map* in Hz units.
47
+ The `original code was taken from nipype
38
48
<https://github.com/nipy/nipype/blob/0.12.1/nipype/workflows/dmri/fsl/artifacts.py#L514>`_.
39
49
40
50
Workflow Graph
@@ -98,11 +108,15 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
98
108
# nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate')
99
109
100
110
# phase diff -> radians
101
- phmap2rads = pe .Node (PhaseMap2rads (), name = 'phmap2rads' )
111
+ phmap2rads = pe .Node (PhaseMap2rads (), name = 'phmap2rads' ,
112
+ run_without_submitting = True )
102
113
103
114
# FSL PRELUDE will perform phase-unwrapping
104
115
prelude = pe .Node (fsl .PRELUDE (), name = 'prelude' )
105
116
117
+ recenter = pe .Node (niu .Function (function = _recenter ),
118
+ name = 'recenter' , run_without_submitting = True )
119
+
106
120
denoise = pe .Node (fsl .SpatialFilter (operation = 'median' , kernel_shape = 'sphere' ,
107
121
kernel_size = 3 ), name = 'denoise' )
108
122
@@ -112,11 +126,6 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
112
126
113
127
compfmap = pe .Node (Phasediff2Fieldmap (), name = 'compfmap' )
114
128
115
- # The phdiff2fmap interface is equivalent to:
116
- # rad2rsec (using rads2radsec from niflow.nipype1.workflows.dmri.fsl.utils)
117
- # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE')
118
- # rsec2hz (divide by 2pi)
119
-
120
129
workflow .connect ([
121
130
(inputnode , compfmap , [('metadata' , 'metadata' )]),
122
131
(inputnode , magmrg , [('magnitude' , 'in_files' )]),
@@ -126,7 +135,8 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
126
135
(bet , prelude , [('mask_file' , 'mask_file' )]),
127
136
(inputnode , phmap2rads , [('phasediff' , 'in_file' )]),
128
137
(phmap2rads , prelude , [('out_file' , 'phase_file' )]),
129
- (prelude , denoise , [('unwrapped_phase_file' , 'in_file' )]),
138
+ (prelude , recenter , [('unwrapped_phase_file' , 'in_file' )]),
139
+ (recenter , denoise , [('out' , 'in_file' )]),
130
140
(denoise , demean , [('out_file' , 'in_file' )]),
131
141
(demean , cleanup_wf , [('out' , 'inputnode.in_file' )]),
132
142
(bet , cleanup_wf , [('mask_file' , 'inputnode.in_mask' )]),
@@ -137,3 +147,21 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
137
147
])
138
148
139
149
return workflow
150
+
151
+
152
+ def _recenter (in_file , offset = None ):
153
+ """Recenter the phase-map distribution to the -pi..pi range."""
154
+ from os .path import basename
155
+ import numpy as np
156
+ import nibabel as nb
157
+ from nipype .utils .filemanip import fname_presuffix
158
+
159
+ if offset is None :
160
+ offset = np .pi
161
+
162
+ nii = nb .load (in_file )
163
+ data = nii .get_fdata (dtype = 'float32' ) - offset
164
+
165
+ out_file = fname_presuffix (basename (in_file ), suffix = '_recentered' )
166
+ nb .Nifti1Image (data , nii .affine , nii .header ).to_filename (out_file )
167
+ return out_file
0 commit comments