Skip to content

Commit b25521a

Browse files
committed
Added workflow using fsl 5.0 for correction
Susceptibility correction with fieldmap
1 parent 5bbfcf9 commit b25521a

File tree

6 files changed

+177
-36
lines changed

6 files changed

+177
-36
lines changed

nipype/workflows/dmri/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from . import camino, mrtrix, fsl
1+
import camino, mrtrix, fsl
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from dti import create_bedpostx_pipeline
2-
from preprocess import create_eddy_correct_pipeline, create_epidewarp_pipeline, create_dmri_preprocessing
2+
3+
from epi import (fieldmap_correction, create_eddy_correct_pipeline,
4+
create_epidewarp_pipeline, create_dmri_preprocessing)
5+
36
from tbss import (create_tbss_1_preproc, create_tbss_2_reg,
47
create_tbss_3_postreg, create_tbss_4_prestats,
58
create_tbss_all, create_tbss_non_FA)

nipype/workflows/dmri/fsl/preprocess.py renamed to nipype/workflows/dmri/fsl/epi.py

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,127 @@ def create_eddy_correct_pipeline(name="eddy_correct"):
233233
return pipeline
234234

235235

236+
237+
238+
def fieldmap_correction(name="fieldmap_correction"):
239+
"""
240+
Fieldmap-based retrospective correction of EPI images for the susceptibility distortion
241+
artifact (Jezzard et al., 1995). Fieldmap images are assumed to be already registered
242+
to EPI data, and a brain mask is required.
243+
244+
Replaces the former workflow, still available as create_epidewarp_pipeline(). The difference
245+
with respect the epidewarp pipeline is that now the workflow uses the new fsl_prepare_fieldmap
246+
available as of FSL 5.0.
247+
248+
249+
250+
Example
251+
-------
252+
253+
>>> nipype_epicorrect = fieldmap_correction("nipype_epidewarp")
254+
>>> nipype_epicorrect.inputs.inputnode.in_file = 'diffusion.nii'
255+
>>> nipype_epicorrect.inputs.inputnode.in_mask = 'brainmask.nii'
256+
>>> nipype_epicorrect.inputs.inputnode.fieldmap_pha = 'phase.nii'
257+
>>> nipype_epicorrect.inputs.inputnode.fieldmap_mag = 'magnitude.nii'
258+
>>> nipype_epicorrect.inputs.inputnode.te_diff = 2.46
259+
>>> nipype_epicorrect.inputs.inputnode.epi_echospacing = 0.77
260+
>>> nipype_epicorrect.inputs.inputnode.pi_accel_factor = 1.0
261+
>>> nipype_epicorrect.inputs.inputnode.encoding_direction = 'y'
262+
>>> nipype_epicorrect.run() # doctest: +SKIP
263+
264+
Inputs::
265+
266+
inputnode.in_file - The volume acquired with EPI sequence
267+
inputnode.in_mask - A brain mask
268+
inputnode.fieldmap_pha - The phase difference map from the fieldmapping, registered to in_file
269+
inputnode.fieldmap_mag - The magnitud maps (usually 4D, one magnitude per GRE scan)
270+
from the fieldmapping, registered to in_file
271+
inputnode.te_diff - Time difference between TE in ms of the fieldmapping (usually a GRE sequence).
272+
inputnode.epi_echospacing - The echo spacing (aka dwell time) in the EPI sequence
273+
inputnode.encoding_dir - The phase encoding direction in EPI acquisition (default y)
274+
inputnode.pi_accel_factor - Acceleration factor used for EPI parallel imaging (GRAPPA)
275+
inputnode.vsm_sigma - Sigma value of the gaussian smoothing filter applied to the vsm (voxel shift map)
276+
277+
278+
Outputs::
279+
280+
outputnode.epi_corrected
281+
282+
"""
283+
284+
inputnode = pe.Node(niu.IdentityInterface(
285+
fields=["in_file",
286+
"in_mask",
287+
"fieldmap_pha",
288+
"fieldmap_mag",
289+
"te_diff",
290+
"epi_echospacing",
291+
"vsm_sigma",
292+
"encoding_direction"
293+
]), name="inputnode"
294+
)
295+
296+
pipeline = pe.Workflow(name=name)
297+
298+
# Keep first frame from magnitude
299+
select_mag = pe.Node(fsl.utils.ExtractROI(
300+
t_size=1, t_min=0), name="select_magnitude")
301+
302+
# Mask magnitude (it is required by PreparedFieldMap)
303+
mask_mag = pe.Node( fsl.maths.ApplyMask(), name='mask_magnitude' )
304+
305+
# Run fsl_prepare_fieldmap
306+
fslprep = pe.Node( fsl.PrepareFieldmap(), name="prepare_fieldmap" )
307+
308+
fill_phase = pe.Node(niu.Function(input_names=["in_file"], output_names=[
309+
"out_file"], function=_fill_phase), name='fill_phasediff')
310+
311+
# Use FUGUE to generate the voxel shift map (vsm)
312+
vsm = pe.Node(fsl.FUGUE(save_shift=True), name="generate_vsm")
313+
314+
# VSM demean is not anymore present in the epi_reg script
315+
#vsm_mean = pe.Node(niu.Function(input_names=["in_file", "mask_file", "in_unwarped"], output_names=[
316+
# "out_file"], function=_vsm_remove_mean), name="vsm_mean_shift")
317+
318+
# fugue_epi
319+
dwi_split = pe.Node(niu.Function(input_names=[
320+
'in_file'], output_names=['out_files'], function=_split_dwi), name='dwi_split')
321+
322+
# 'fugue -i %s -u %s --loadshift=%s --mask=%s' % ( vol_name, out_vol_name, vsm_name, mask_name )
323+
dwi_applyxfm = pe.MapNode(fsl.FUGUE(
324+
icorr=True, save_shift=False), iterfield=['in_file'], name='dwi_fugue')
325+
# Merge back all volumes
326+
dwi_merge = pe.Node(fsl.utils.Merge(
327+
dimension='t'), name='dwi_merge')
328+
329+
outputnode = pe.Node(
330+
niu.IdentityInterface(fields=["epi_corrected"]),
331+
name="outputnode")
332+
333+
pipeline.connect([
334+
(inputnode, select_mag, [('fieldmap_mag', 'in_file')])
335+
,(inputnode, fslprep, [('fieldmap_pha', 'in_phase'),('te_diff', 'delta_TE') ])
336+
,(inputnode, mask_mag, [('in_mask', 'mask_file' )])
337+
,(select_mag, mask_mag, [('roi_file', 'in_file')])
338+
,(mask_mag, fslprep, [('out_file', 'in_magnitude')])
339+
,(inputnode, vsm, [('fieldmap_mag', 'in_file')])
340+
,(fslprep, fill_phase, [('out_fieldmap', 'in_file')])
341+
,(fill_phase, vsm, [('out_file', 'phasemap_file')])
342+
,(inputnode, vsm, [(('te_diff', _ms2sec), 'asym_se_time'), ('vsm_sigma', 'smooth2d'),
343+
(('epi_echospacing', _ms2sec), 'dwell_time')])
344+
,(mask_mag, vsm, [('out_file', 'mask_file')])
345+
,(inputnode, dwi_split, [('in_file', 'in_file')])
346+
,(dwi_split, dwi_applyxfm, [('out_files', 'in_file')])
347+
,(mask_mag, dwi_applyxfm, [('out_file', 'mask_file')])
348+
,(vsm, dwi_applyxfm, [('shift_out_file', 'shift_in_file')])
349+
,(dwi_applyxfm, dwi_merge, [('unwarped_file', 'in_files')])
350+
,(dwi_merge, outputnode, [('merged_file', 'epi_corrected')])
351+
])
352+
353+
354+
return pipeline
355+
356+
236357
def create_epidewarp_pipeline(name="epidewarp", fieldmap_registration=False):
237358
""" Replaces the epidewarp.fsl script (http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl)
238359
for susceptibility distortion correction of dMRI & fMRI acquired with EPI sequences and the fieldmap
@@ -453,6 +574,10 @@ def _compute_dwelltime(dwell_time=0.68, pi_factor=1.0, is_reverse_encoding=False
453574

454575
return dwell_time
455576

577+
def _effective_echospacing( dwell_time, pi_factor=1.0 ):
578+
dwelltime = 1.0e-3 * dwelltime * ( 1.0/pi_factor )
579+
return dwelltime
580+
456581

457582
def _prepare_phasediff(in_file):
458583
import nibabel as nib
@@ -500,7 +625,7 @@ def _fill_phase(in_file):
500625
name, fext = os.path.splitext(os.path.basename(in_file))
501626
if fext == '.gz':
502627
name, _ = os.path.splitext(name)
503-
out_file = os.path.abspath('./%s_phase_unwrapped.nii.gz' % name)
628+
out_file = os.path.abspath('./%s_fill.nii.gz' % name)
504629
nib.save(out_nii, out_file)
505630
return out_file
506631

nipype/workflows/dmri/fsl/tests/test_dti.py

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,7 @@
1010
import warnings
1111
import tempfile
1212
import shutil
13-
from nipype.workflows.dmri.fsl.dti import create_eddy_correct_pipeline,\
14-
create_bedpostx_pipeline
15-
16-
17-
@skipif(no_fsl)
18-
@skipif(no_fsl_course_data)
19-
def test_create_eddy_correct_pipeline():
20-
fsl_course_dir = os.path.abspath('fsl_course_data')
21-
22-
dwi_file = os.path.join(fsl_course_dir, "fdt/subj1/data.nii.gz")
23-
24-
nipype_eddycorrect = create_eddy_correct_pipeline("nipype_eddycorrect")
25-
nipype_eddycorrect.inputs.inputnode.in_file = dwi_file
26-
nipype_eddycorrect.inputs.inputnode.ref_num = 0
27-
28-
with warnings.catch_warnings():
29-
warnings.simplefilter("ignore")
30-
original_eddycorrect = pe.Node(interface=fsl.EddyCorrect(), name="original_eddycorrect")
31-
original_eddycorrect.inputs.in_file = dwi_file
32-
original_eddycorrect.inputs.ref_num = 0
33-
34-
test = pe.Node(util.AssertEqual(), name="eddy_corrected_dwi_test")
35-
36-
pipeline = pe.Workflow(name="test_eddycorrect")
37-
pipeline.base_dir = tempfile.mkdtemp(prefix="nipype_test_eddycorrect_")
38-
39-
pipeline.connect([(nipype_eddycorrect, test, [("outputnode.eddy_corrected", "volume1")]),
40-
(original_eddycorrect, test, [("eddy_corrected", "volume2")]),
41-
])
42-
43-
pipeline.run(plugin='Linear')
44-
shutil.rmtree(pipeline.base_dir)
13+
from nipype.workflows.dmri.fsl.dti import create_bedpostx_pipeline
4514

4615

4716
@skipif(no_fsl)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
3+
from nipype.testing import (skipif)
4+
import nipype.workflows.fmri.fsl as fsl_wf
5+
import nipype.interfaces.fsl as fsl
6+
import nipype.interfaces.utility as util
7+
from nipype.interfaces.fsl import no_fsl, no_fsl_course_data
8+
9+
import nipype.pipeline.engine as pe
10+
import warnings
11+
import tempfile
12+
import shutil
13+
from nipype.workflows.dmri.fsl.epi import create_eddy_correct_pipeline
14+
15+
16+
@skipif(no_fsl)
17+
@skipif(no_fsl_course_data)
18+
def test_create_eddy_correct_pipeline():
19+
fsl_course_dir = os.path.abspath('fsl_course_data')
20+
21+
dwi_file = os.path.join(fsl_course_dir, "fdt/subj1/data.nii.gz")
22+
23+
nipype_eddycorrect = create_eddy_correct_pipeline("nipype_eddycorrect")
24+
nipype_eddycorrect.inputs.inputnode.in_file = dwi_file
25+
nipype_eddycorrect.inputs.inputnode.ref_num = 0
26+
27+
with warnings.catch_warnings():
28+
warnings.simplefilter("ignore")
29+
original_eddycorrect = pe.Node(interface=fsl.EddyCorrect(), name="original_eddycorrect")
30+
original_eddycorrect.inputs.in_file = dwi_file
31+
original_eddycorrect.inputs.ref_num = 0
32+
33+
test = pe.Node(util.AssertEqual(), name="eddy_corrected_dwi_test")
34+
35+
pipeline = pe.Workflow(name="test_eddycorrect")
36+
pipeline.base_dir = tempfile.mkdtemp(prefix="nipype_test_eddycorrect_")
37+
38+
pipeline.connect([(nipype_eddycorrect, test, [("outputnode.eddy_corrected", "volume1")]),
39+
(original_eddycorrect, test, [("eddy_corrected", "volume2")]),
40+
])
41+
42+
pipeline.run(plugin='Linear')
43+
shutil.rmtree(pipeline.base_dir)
44+

nipype/workflows/dmri/mrtrix/connectivity_mapping.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import nipype.algorithms.misc as misc
1010
import inspect
1111
import os, os.path as op # system functions
12-
from ..fsl.dti import create_eddy_correct_pipeline
12+
from ..fsl.epi import create_eddy_correct_pipeline
1313
from ..connectivity.nx import create_networkx_pipeline, create_cmats_to_csv_pipeline
1414
from nipype.interfaces.utility import Function
1515
from ...misc.utils import select_aparc_annot

0 commit comments

Comments
 (0)