20
20
from nipype .interfaces import ants , fsl , utility as niu
21
21
from nipype .pipeline import engine as pe
22
22
from niflow .nipype1 .workflows .dmri .fsl .utils import (
23
- siemens2rads , demean_image , cleanup_edge_pipeline )
23
+ demean_image , cleanup_edge_pipeline )
24
24
from niworkflows .engine .workflows import LiterateWorkflow as Workflow
25
25
from niworkflows .interfaces .images import IntraModalMerge
26
26
from niworkflows .interfaces .masks import BETRPT
27
27
28
- from ..interfaces .fmap import Phasediff2Fieldmap
28
+ from ..interfaces .fmap import Phasediff2Fieldmap , PhaseMap2rads
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 [Jenkinson2003]_.
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
@@ -68,6 +78,11 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
68
78
fmap : pathlike
69
79
The estimated fieldmap in Hz
70
80
81
+ References
82
+ ----------
83
+ .. [Jenkinson2003] Jenkinson, M. (2003) Fast, automated, N-dimensional phase-unwrapping
84
+ algorithm. MRM 49(1):193-197. doi:`10.1002/mrm.10354 <10.1002/mrm.10354>`__.
85
+
71
86
"""
72
87
workflow = Workflow (name = name )
73
88
workflow .__desc__ = """\
@@ -98,11 +113,15 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
98
113
# nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate')
99
114
100
115
# phase diff -> radians
101
- pha2rads = pe .Node (niu .Function (function = siemens2rads ), name = 'pha2rads' )
116
+ phmap2rads = pe .Node (PhaseMap2rads (), name = 'phmap2rads' ,
117
+ run_without_submitting = True )
102
118
103
119
# FSL PRELUDE will perform phase-unwrapping
104
120
prelude = pe .Node (fsl .PRELUDE (), name = 'prelude' )
105
121
122
+ recenter = pe .Node (niu .Function (function = _recenter ),
123
+ name = 'recenter' , run_without_submitting = True )
124
+
106
125
denoise = pe .Node (fsl .SpatialFilter (operation = 'median' , kernel_shape = 'sphere' ,
107
126
kernel_size = 3 ), name = 'denoise' )
108
127
@@ -112,21 +131,17 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
112
131
113
132
compfmap = pe .Node (Phasediff2Fieldmap (), name = 'compfmap' )
114
133
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
134
workflow .connect ([
121
135
(inputnode , compfmap , [('metadata' , 'metadata' )]),
122
136
(inputnode , magmrg , [('magnitude' , 'in_files' )]),
123
137
(magmrg , n4 , [('out_avg' , 'input_image' )]),
124
138
(n4 , prelude , [('output_image' , 'magnitude_file' )]),
125
139
(n4 , bet , [('output_image' , 'in_file' )]),
126
140
(bet , prelude , [('mask_file' , 'mask_file' )]),
127
- (inputnode , pha2rads , [('phasediff' , 'in_file' )]),
128
- (pha2rads , prelude , [('out' , 'phase_file' )]),
129
- (prelude , denoise , [('unwrapped_phase_file' , 'in_file' )]),
141
+ (inputnode , phmap2rads , [('phasediff' , 'in_file' )]),
142
+ (phmap2rads , prelude , [('out_file' , 'phase_file' )]),
143
+ (prelude , recenter , [('unwrapped_phase_file' , 'in_file' )]),
144
+ (recenter , denoise , [('out' , 'in_file' )]),
130
145
(denoise , demean , [('out_file' , 'in_file' )]),
131
146
(demean , cleanup_wf , [('out' , 'inputnode.in_file' )]),
132
147
(bet , cleanup_wf , [('mask_file' , 'inputnode.in_mask' )]),
@@ -137,3 +152,22 @@ def init_phdiff_wf(omp_nthreads, name='phdiff_wf'):
137
152
])
138
153
139
154
return workflow
155
+
156
+
157
+ def _recenter (in_file ):
158
+ """Recenter the phase-map distribution to the -pi..pi range."""
159
+ from os import getcwd
160
+ import numpy as np
161
+ import nibabel as nb
162
+ from nipype .utils .filemanip import fname_presuffix
163
+
164
+ nii = nb .load (in_file )
165
+ data = nii .get_fdata (dtype = 'float32' )
166
+ msk = data != 0
167
+ msk [data == 0 ] = False
168
+ data [msk ] -= np .median (data [msk ])
169
+
170
+ out_file = fname_presuffix (in_file , suffix = '_recentered' ,
171
+ newpath = getcwd ())
172
+ nb .Nifti1Image (data , nii .affine , nii .header ).to_filename (out_file )
173
+ return out_file
0 commit comments