/
dwi_preprocessing_using_phasediff_fieldmap_pipeline.py
357 lines (309 loc) · 16.4 KB
/
dwi_preprocessing_using_phasediff_fieldmap_pipeline.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# coding: utf8
import clinica.pipelines.engine as cpe
from nipype import config
__author__ = ["Alexandre Routier"]
__copyright__ = "Copyright 2016-2019 The Aramis Lab Team"
__credits__ = ["Nipype", "Junhao Wen"]
__license__ = "See LICENSE.txt file"
__version__ = "0.1.0"
__status__ = "Development"
# Use hash instead of parameters for iterables folder names
# Otherwise path will be too long and generate OSError
cfg = dict(execution={'parameterize_dirs': False})
config.update_config(cfg)
class DwiPreprocessingUsingPhaseDiffFieldmap(cpe.Pipeline):
"""DWI Preprocessing using phase difference fieldmap.
Args:
input_dir(str): Input directory in a BIDS hierarchy.
output_dir(str): Output directory in a CAPS hierarchy.
subjects_sessions_list(str): The Subjects-Sessions list file (in .tsv
format).
Returns:
A clinica pipeline object containing the DWIPreprocessingUsingPhaseDiffFieldmap pipeline.
Raises:
"""
def __init__(self,
bids_directory=None,
caps_directory=None,
tsv_file=None,
base_dir=None,
name=None,
low_bval=5):
"""
Args:
low_bval (int): Define the b0 volumes as all volume
bval <= lowbval. (Default = 5)
"""
import warnings
super(DwiPreprocessingUsingPhaseDiffFieldmap, self).__init__(
bids_directory=bids_directory,
caps_directory=caps_directory,
tsv_file=tsv_file,
base_dir=base_dir,
name=name)
self._low_bval = low_bval
if self._low_bval < 0:
raise ValueError('The low_bval is equals to '
+ str(self._low_bval)
+ ': it should be zero or close to zero.')
if self._low_bval > 100:
warnings.warn('Warning: The low_bval parameter is huge ('
+ str(self._low_bval)
+ '), it should be close to zero', UserWarning)
def check_custom_dependencies(self):
"""Check dependencies that can not be listed in the `info.json` file.
"""
pass
def get_input_fields(self):
"""Specify the list of possible inputs of this pipeline.
Returns:
A list of (string) input fields name.
"""
input_list = ['dwi', 'bvec', 'bval', 'delta_echo_time',
'effective_echo_spacing', 'phase_encoding_direction',
'fmap_magnitude', 'fmap_phasediff']
return input_list
def get_output_fields(self):
"""Specify the list of possible outputs of this pipeline.
Returns:
A list of (string) output fields name.
"""
output_list = ['preproc_dwi', 'preproc_bvec', 'preproc_bval',
'b0_mask']
return output_list
def build_input_node(self):
"""Build and connect an input node to the pipeline.
"""
import nipype.interfaces.utility as nutil
import nipype.pipeline.engine as npe
from clinica.utils.dwi import check_dwi_volume
from clinica.utils.inputs import clinica_file_reader
import clinica.pipelines.dwi_preprocessing_using_phasediff_fieldmap.dwi_preprocessing_using_phasediff_fieldmap_utils as utils
from clinica.utils.exceptions import ClinicaBIDSError, ClinicaException
import clinica.utils.input_files as input_files
from clinica.utils.stream import cprint
all_errors = []
# DWI
try:
dwi_bids = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.DWI_NII)
except ClinicaException as e:
all_errors.append(e)
# DWI json
try:
dwi_json = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.DWI_JSON)
# Create list_eff_echo_spacings and list_enc_directions
list_eff_echo_spacings = []
list_enc_directions = []
for json in dwi_json:
[eff_echo_spacing, enc_direction] = utils.parameters_from_dwi_metadata(json)
list_eff_echo_spacings.append(eff_echo_spacing)
list_enc_directions.append(enc_direction)
except ClinicaException as e:
all_errors.append(e)
# bval files
try:
bval_files = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.DWI_BVAL)
except ClinicaException as e:
all_errors.append(e)
# bvec files
try:
bvec_files = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.DWI_BVEC)
except ClinicaException as e:
all_errors.append(e)
# dwi_bids, bvec_files, bval_files may not exist
if len(all_errors) == 0:
for (dwi, bvec, bval) in zip(dwi_bids, bvec_files, bval_files):
check_dwi_volume(in_dwi=dwi, in_bvec=bvec, in_bval=bval)
# Phasediff json
try:
fmap_phasediff_json = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.FMAP_PHASEDIFF_JSON)
# Then deduce delta echo times
list_delta_echo_times = [utils.delta_echo_time_from_bids_fmap(json_phasediff)
for json_phasediff in fmap_phasediff_json]
except ClinicaException as e:
all_errors.append(e)
# Phasediff nifti
try:
phasediff_nifti = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.FMAP_PHASEDIFF_NII)
except ClinicaException as e:
all_errors.append(e)
# Magnitude1
try:
magnitude1 = clinica_file_reader(self.subjects,
self.sessions,
self.bids_directory,
input_files.FMAP_MAGNITUDE1_NII)
except ClinicaException as e:
all_errors.append(e)
if len(all_errors) > 0:
error_message = 'Clinica faced error(s) while trying to read files in your CAPS directory.\n'
for msg in all_errors:
error_message += str(msg)
raise ClinicaBIDSError(error_message)
cprint("List JSON parameters for DWI Preprocessing:")
cprint("- PhaseEncodingDirections")
cprint(list_enc_directions)
cprint("- EffectiveEchoSpacing")
cprint(list_eff_echo_spacings)
cprint("- DeltaEchoTime")
cprint(list_delta_echo_times)
read_input_node = npe.Node(name="LoadingCLIArguments",
interface=nutil.IdentityInterface(
fields=self.get_input_fields(),
mandatory_inputs=True),
iterables=[('dwi', dwi_bids),
('bvec', bvec_files),
('bval', bval_files),
('delta_echo_time', list_delta_echo_times),
('effective_echo_spacing', list_eff_echo_spacings),
('phase_encoding_direction', list_enc_directions),
('fmap_magnitude', magnitude1),
('fmap_phasediff', phasediff_nifti)],
synchronize=True)
self.connect([
(read_input_node, self.input_node, [('fmap_magnitude', 'fmap_magnitude')]),
(read_input_node, self.input_node, [('fmap_phasediff', 'fmap_phasediff')]),
(read_input_node, self.input_node, [('dwi', 'dwi')]),
(read_input_node, self.input_node, [('bval', 'bval')]),
(read_input_node, self.input_node, [('bvec', 'bvec')]),
(read_input_node, self.input_node, [('delta_echo_time', 'delta_echo_time')]),
(read_input_node, self.input_node, [('effective_echo_spacing', 'effective_echo_spacing')]),
(read_input_node, self.input_node, [('phase_encoding_direction', 'phase_encoding_direction')])
])
def build_output_node(self):
"""Build and connect an output node to the pipeline.
"""
import nipype.interfaces.utility as nutil
import nipype.pipeline.engine as npe
import nipype.interfaces.io as nio
from clinica.utils.nipype import fix_join
import clinica.pipelines.dwi_preprocessing_using_phasediff_fieldmap.dwi_preprocessing_using_phasediff_fieldmap_utils as utils
# Find container path from DWI filename
# =====================================
container_path = npe.Node(nutil.Function(
input_names=['dwi_filename'],
output_names=['container'],
function=utils.dwi_container_from_filename),
name='container_path')
rename_into_caps = npe.Node(nutil.Function(
input_names=['in_bids_dwi', 'fname_dwi', 'fname_bval',
'fname_bvec', 'fname_brainmask'],
output_names=['out_caps_dwi', 'out_caps_bval', 'out_caps_bvec',
'out_caps_brainmask'],
function=utils.rename_into_caps),
name='rename_into_caps')
# Writing results into CAPS
# =========================
write_results = npe.Node(name='write_results',
interface=nio.DataSink())
write_results.inputs.base_directory = self.caps_directory
write_results.inputs.parameterization = False
self.connect([
(self.input_node, container_path, [('dwi', 'dwi_filename')]), # noqa
(self.input_node, rename_into_caps, [('dwi', 'in_bids_dwi')]), # noqa
(self.output_node, rename_into_caps, [('preproc_dwi', 'fname_dwi'), # noqa
('preproc_bval', 'fname_bval'), # noqa
('preproc_bvec', 'fname_bvec'), # noqa
('b0_mask', 'fname_brainmask')]), # noqa
(container_path, write_results, [(('container', fix_join, 'dwi'), 'container')]), # noqa
(rename_into_caps, write_results, [('out_caps_dwi', 'preprocessing.@preproc_dwi'), # noqa
('out_caps_bval', 'preprocessing.@preproc_bval'), # noqa
('out_caps_bvec', 'preprocessing.@preproc_bvec'), # noqa
('out_caps_brainmask', 'preprocessing.@b0_mask')]) # noqa
])
def build_core_nodes(self):
"""Build and connect the core nodes of the pipeline.
"""
import nipype.interfaces.utility as nutil
import nipype.pipeline.engine as npe
import nipype.interfaces.fsl as fsl
from nipype.workflows.dmri.fsl.utils import apply_all_corrections
from clinica.utils.dwi import prepare_reference_b0
from clinica.workflows.dwi_preprocessing import ecc_pipeline
from clinica.workflows.dwi_preprocessing import hmc_pipeline
from clinica.workflows.dwi_preprocessing import remove_bias
import clinica.pipelines.dwi_preprocessing_using_phasediff_fieldmap.dwi_preprocessing_using_phasediff_fieldmap_workflows as workflows
# Nodes creation
# ==============
# Prepare b0 image for further corrections
prepare_b0 = npe.Node(name="PrepareB0", interface=nutil.Function(
input_names=['in_dwi', 'in_bval', 'in_bvec', 'low_bval'],
output_names=['out_reference_b0', 'out_b0_dwi_merge',
'out_updated_bval', 'out_updated_bvec'],
function=prepare_reference_b0))
prepare_b0.inputs.low_bval = self._low_bval
# Mask b0 for computations purposes
mask_b0_pre = npe.Node(fsl.BET(frac=0.3, mask=True, robust=True),
name='PreMaskB0')
# Head-motion correction
hmc = hmc_pipeline(name='HeadMotionCorrection')
# Eddy-currents correction
ecc = ecc_pipeline(name='EddyCurrentCorrection')
# Susceptibility distortion correction using fmap
sdc = workflows.susceptibility_distortion_correction_using_phasediff_fmap(
register_fmap_on_b0=True,
name='SusceptibilityDistortionCorrection'
)
# Apply all corrections
unwarp = apply_all_corrections(name='ApplyAllCorrections')
# Remove bias correction
bias = remove_bias(name='RemoveBias')
#
# Connection
# ==========
self.connect([
# Preliminary step (possible computation of a mean b0)
(self.input_node, prepare_b0, [('dwi', 'in_dwi'), # noqa
('bval', 'in_bval'), # noqa
('bvec', 'in_bvec')]), # noqa
# Mask b0 before corrections
(prepare_b0, mask_b0_pre, [('out_reference_b0', 'in_file')]), # noqa
# Head-motion correction
(prepare_b0, hmc, [('out_b0_dwi_merge', 'inputnode.in_file'), # noqa
('out_updated_bval', 'inputnode.in_bval'), # noqa
('out_updated_bvec', 'inputnode.in_bvec')]), # noqa
(mask_b0_pre, hmc, [('mask_file', 'inputnode.in_mask')]), # noqa
# Eddy-current correction
(hmc, ecc, [('outputnode.out_xfms', 'inputnode.in_xfms')]), # noqa
(prepare_b0, ecc, [('out_b0_dwi_merge', 'inputnode.in_file')]), # noqa
(prepare_b0, ecc, [('out_updated_bval', 'inputnode.in_bval')]), # noqa
(mask_b0_pre, ecc, [('mask_file', 'inputnode.in_mask')]), # noqa
# Magnetic susceptibility correction
(ecc, sdc, [('outputnode.out_file', 'inputnode.in_dwi')]), # noqa
(mask_b0_pre, sdc, [('mask_file', 'inputnode.in_mask')]), # noqa
(self.input_node, sdc, [('fmap_phasediff', 'inputnode.in_fmap_phasediff')]), # noqa
(self.input_node, sdc, [('fmap_magnitude', 'inputnode.in_fmap_magnitude')]), # noqa
(self.input_node, sdc, [('delta_echo_time', 'inputnode.delta_echo_time')]), # noqa
(self.input_node, sdc, [('effective_echo_spacing', 'inputnode.effective_echo_spacing')]), # noqa
(self.input_node, sdc, [('phase_encoding_direction', 'inputnode.phase_encoding_direction')]), # noqa
# Apply all corrections
(prepare_b0, unwarp, [('out_b0_dwi_merge', 'inputnode.in_dwi')]), # noqa
(hmc, unwarp, [('outputnode.out_xfms', 'inputnode.in_hmc')]), # noqa
(ecc, unwarp, [('outputnode.out_xfms', 'inputnode.in_ecc')]), # noqa
(sdc, unwarp, [('outputnode.out_warp', 'inputnode.in_sdc')]), # noqa
# Bias correction
(unwarp, bias, [('outputnode.out_file', 'inputnode.in_file')]),
# Outputnode
(bias, self.output_node, [('outputnode.out_file', 'preproc_dwi')]), # noqa
(hmc, self.output_node, [('outputnode.out_bvec', 'preproc_bvec')]), # noqa
(prepare_b0, self.output_node, [('out_updated_bval', 'preproc_bval')]), # noqa
(bias, self.output_node, [('outputnode.b0_mask', 'b0_mask')]) # noqa
])