Skip to content

Commit 24e52aa

Browse files
authored
Merge pull request #2904 from josephmje/mrtrix_preprocessing
[ENH]: Add mrdegibbs and dwibiascorrect from mrtrix3
2 parents fcbe28e + c08051d commit 24e52aa

File tree

7 files changed

+322
-8
lines changed

7 files changed

+322
-8
lines changed

.zenodo.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@
330330
"name": "Liem, Franz",
331331
"orcid": "0000-0003-0646-4810"
332332
},
333+
{
334+
"affiliation": "The Centre for Addiction and Mental Health",
335+
"name": "Joseph, Michael",
336+
"orcid": "0000-0002-0068-230X"
337+
},
333338
{
334339
"affiliation": "UniversityHospital Heidelberg, Germany",
335340
"name": "Kleesiek, Jens"

nipype/interfaces/fsl/epi.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,7 @@ def _list_outputs(self):
13831383
# If the output directory isn't defined, the interface seems to use
13841384
# the default but not set its value in `self.inputs.output_dir`
13851385
if not isdefined(self.inputs.output_dir):
1386-
out_dir = os.path.abspath(os.path.basename(self.inputs.base_name) + '.qc.nii.gz')
1386+
out_dir = os.path.abspath(os.path.basename(self.inputs.base_name) + '.qc')
13871387
else:
13881388
out_dir = os.path.abspath(self.inputs.output_dir)
13891389

@@ -1421,4 +1421,3 @@ def _list_outputs(self):
14211421
outputs['clean_volumes'] = clean_volumes
14221422

14231423
return outputs
1424-

nipype/interfaces/mrtrix3/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .utils import (Mesh2PVE, Generate5tt, BrainMask, TensorMetrics,
77
ComputeTDI, TCK2VTK, MRMath, MRConvert, DWIExtract)
88
from .preprocess import (ResponseSD, ACTPrepareFSL, ReplaceFSwithFIRST,
9-
DWIDenoise)
9+
DWIDenoise, MRDeGibbs, DWIBiasCorrect)
1010
from .tracking import Tractography
1111
from .reconst import FitTensor, EstimateFOD
1212
from .connectivity import LabelConfig, LabelConvert, BuildConnectome

nipype/interfaces/mrtrix3/preprocess.py

Lines changed: 174 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ class DWIDenoiseInputSpec(MRTrix3BaseInputSpec):
2828
desc='set the window size of the denoising filter. (default = 5,5,5)')
2929
noise = File(
3030
argstr='-noise %s',
31-
desc='noise map')
31+
desc='the output noise map')
3232
out_file = File(name_template='%s_denoised',
3333
name_source='in_file',
3434
keep_extension=True,
35-
argstr="%s",
35+
argstr='%s',
3636
position=-1,
37-
desc="the output denoised DWI image")
37+
desc='the output denoised DWI image',
38+
genfile=True)
3839

3940
class DWIDenoiseOutputSpec(TraitedSpec):
40-
out_file = File(desc="the output denoised DWI image", exists=True)
41+
noise = File(desc='the output noise map', exists=True)
42+
out_file = File(desc='the output denoised DWI image', exists=True)
4143

4244
class DWIDenoise(MRTrix3Base):
4345
"""
@@ -74,6 +76,174 @@ class DWIDenoise(MRTrix3Base):
7476
input_spec = DWIDenoiseInputSpec
7577
output_spec = DWIDenoiseOutputSpec
7678

79+
def _list_outputs(self):
80+
outputs = self.output_spec().get()
81+
outputs['out_file'] = op.abspath(self.inputs.out_file)
82+
if self.inputs.noise != Undefined:
83+
outputs['noise'] = op.abspath(self.inputs.noise)
84+
return outputs
85+
86+
87+
class MRDeGibbsInputSpec(MRTrix3BaseInputSpec):
88+
in_file = File(
89+
exists=True,
90+
argstr='%s',
91+
position=-2,
92+
mandatory=True,
93+
desc='input DWI image')
94+
axes = traits.ListInt(
95+
default_value=[0,1],
96+
usedefault=True,
97+
sep=',',
98+
minlen=2,
99+
maxlen=2,
100+
argstr='-axes %s',
101+
desc='indicate the plane in which the data was acquired (axial = 0,1; '
102+
'coronal = 0,2; sagittal = 1,2')
103+
nshifts = traits.Int(
104+
default_value=20,
105+
usedefault=True,
106+
argstr='-nshifts %d',
107+
desc='discretization of subpixel spacing (default = 20)')
108+
minW = traits.Int(
109+
default_value=1,
110+
usedefault=True,
111+
argstr='-minW %d',
112+
desc='left border of window used for total variation (TV) computation '
113+
'(default = 1)')
114+
maxW = traits.Int(
115+
default_value=3,
116+
usedefault=True,
117+
argstr='-maxW %d',
118+
desc='right border of window used for total variation (TV) computation '
119+
'(default = 3)')
120+
out_file = File(name_template='%s_unr',
121+
name_source='in_file',
122+
keep_extension=True,
123+
argstr='%s',
124+
position=-1,
125+
desc='the output unringed DWI image',
126+
genfile=True)
127+
128+
class MRDeGibbsOutputSpec(TraitedSpec):
129+
out_file = File(desc='the output unringed DWI image', exists=True)
130+
131+
class MRDeGibbs(MRTrix3Base):
132+
"""
133+
Remove Gibbs ringing artifacts.
134+
135+
This application attempts to remove Gibbs ringing artefacts from MRI images
136+
using the method of local subvoxel-shifts proposed by Kellner et al.
137+
138+
This command is designed to run on data directly after it has been
139+
reconstructed by the scanner, before any interpolation of any kind has
140+
taken place. You should not run this command after any form of motion
141+
correction (e.g. not after dwipreproc). Similarly, if you intend running
142+
dwidenoise, you should run this command afterwards, since it has the
143+
potential to alter the noise structure, which would impact on dwidenoise's
144+
performance.
145+
146+
Note that this method is designed to work on images acquired with full
147+
k-space coverage. Running this method on partial Fourier ('half-scan') data
148+
may lead to suboptimal and/or biased results, as noted in the original
149+
reference below. There is currently no means of dealing with this; users
150+
should exercise caution when using this method on partial Fourier data, and
151+
inspect its output for any obvious artefacts.
152+
153+
For more information, see
154+
<https://mrtrix.readthedocs.io/en/latest/reference/commands/mrdegibbs.html>
155+
156+
Example
157+
-------
158+
159+
>>> import nipype.interfaces.mrtrix3 as mrt
160+
>>> unring = mrt.MRDeGibbs()
161+
>>> unring.inputs.in_file = 'dwi.mif'
162+
>>> unring.cmdline
163+
'mrdegibbs -axes 0,1 -maxW 3 -minW 1 -nshifts 20 dwi.mif dwi_unr.mif'
164+
>>> unring.run() # doctest: +SKIP
165+
"""
166+
167+
_cmd = 'mrdegibbs'
168+
input_spec = MRDeGibbsInputSpec
169+
output_spec = MRDeGibbsOutputSpec
170+
171+
172+
class DWIBiasCorrectInputSpec(MRTrix3BaseInputSpec):
173+
in_file = File(
174+
exists=True,
175+
argstr='%s',
176+
position=-2,
177+
mandatory=True,
178+
desc='input DWI image')
179+
in_mask = File(
180+
argstr='-mask %s',
181+
desc='input mask image for bias field estimation')
182+
_xor_methods = ('use_ants', 'use_fsl')
183+
use_ants = traits.Bool(
184+
default_value=True,
185+
usedefault=True,
186+
argstr='-ants',
187+
desc='use ANTS N4 to estimate the inhomogeneity field',
188+
xor=_xor_methods)
189+
use_fsl = traits.Bool(
190+
argstr='-fsl',
191+
desc='use FSL FAST to estimate the inhomogeneity field',
192+
xor=_xor_methods,
193+
min_ver='5.0.10')
194+
_xor_grads = ('mrtrix_grad', 'fsl_grad')
195+
mrtrix_grad = File(
196+
argstr='-grad %s',
197+
desc='diffusion gradient table in MRtrix format',
198+
xor=_xor_grads)
199+
fsl_grad = File(
200+
argstr='-fslgrad %s %s',
201+
desc='diffusion gradient table in FSL bvecs/bvals format',
202+
xor=_xor_grads)
203+
bias = File(
204+
argstr='-bias %s',
205+
desc='bias field')
206+
out_file = File(name_template='%s_biascorr',
207+
name_source='in_file',
208+
keep_extension=True,
209+
argstr='%s',
210+
position=-1,
211+
desc='the output bias corrected DWI image',
212+
genfile=True)
213+
214+
class DWIBiasCorrectOutputSpec(TraitedSpec):
215+
bias = File(desc='the output bias field', exists=True)
216+
out_file = File(desc='the output bias corrected DWI image', exists=True)
217+
218+
class DWIBiasCorrect(MRTrix3Base):
219+
"""
220+
Perform B1 field inhomogeneity correction for a DWI volume series.
221+
222+
For more information, see
223+
<https://mrtrix.readthedocs.io/en/latest/reference/scripts/dwibiascorrect.html>
224+
225+
Example
226+
-------
227+
228+
>>> import nipype.interfaces.mrtrix3 as mrt
229+
>>> bias_correct = mrt.DWIBiasCorrect()
230+
>>> bias_correct.inputs.in_file = 'dwi.mif'
231+
>>> bias_correct.cmdline
232+
'dwibiascorrect -ants dwi.mif dwi_biascorr.mif'
233+
>>> bias_correct.run() # doctest: +SKIP
234+
"""
235+
236+
_cmd = 'dwibiascorrect'
237+
input_spec = DWIBiasCorrectInputSpec
238+
output_spec = DWIBiasCorrectOutputSpec
239+
240+
def _list_outputs(self):
241+
outputs = self.output_spec().get()
242+
outputs['out_file'] = op.abspath(self.inputs.out_file)
243+
if self.inputs.bias != Undefined:
244+
outputs['bias'] = op.abspath(self.inputs.bias)
245+
return outputs
246+
77247

78248
class ResponseSDInputSpec(MRTrix3BaseInputSpec):
79249
algorithm = traits.Enum(
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..preprocess import DWIBiasCorrect
4+
5+
6+
def test_DWIBiasCorrect_inputs():
7+
input_map = dict(
8+
args=dict(argstr='%s', ),
9+
bias=dict(argstr='-bias %s', ),
10+
bval_scale=dict(argstr='-bvalue_scaling %s', ),
11+
environ=dict(
12+
nohash=True,
13+
usedefault=True,
14+
),
15+
fsl_grad=dict(
16+
argstr='-fslgrad %s %s',
17+
xor=('mrtrix_grad', 'fsl_grad'),
18+
),
19+
grad_file=dict(argstr='-grad %s', ),
20+
grad_fsl=dict(argstr='-fslgrad %s %s', ),
21+
in_bval=dict(),
22+
in_bvec=dict(argstr='-fslgrad %s %s', ),
23+
in_file=dict(
24+
argstr='%s',
25+
mandatory=True,
26+
position=-2,
27+
),
28+
in_mask=dict(argstr='-mask %s', ),
29+
mrtrix_grad=dict(
30+
argstr='-grad %s',
31+
xor=('mrtrix_grad', 'fsl_grad'),
32+
),
33+
nthreads=dict(
34+
argstr='-nthreads %d',
35+
nohash=True,
36+
),
37+
out_file=dict(
38+
argstr='%s',
39+
genfile=True,
40+
keep_extension=True,
41+
name_source='in_file',
42+
name_template='%s_biascorr',
43+
position=-1,
44+
),
45+
use_ants=dict(
46+
argstr='-ants',
47+
usedefault=True,
48+
xor=('use_ants', 'use_fsl'),
49+
),
50+
use_fsl=dict(
51+
argstr='-fsl',
52+
min_ver='5.0.10',
53+
xor=('use_ants', 'use_fsl'),
54+
),
55+
)
56+
inputs = DWIBiasCorrect.input_spec()
57+
58+
for key, metadata in list(input_map.items()):
59+
for metakey, value in list(metadata.items()):
60+
assert getattr(inputs.traits()[key], metakey) == value
61+
def test_DWIBiasCorrect_outputs():
62+
output_map = dict(
63+
bias=dict(),
64+
out_file=dict(),
65+
)
66+
outputs = DWIBiasCorrect.output_spec()
67+
68+
for key, metadata in list(output_map.items()):
69+
for metakey, value in list(metadata.items()):
70+
assert getattr(outputs.traits()[key], metakey) == value

nipype/interfaces/mrtrix3/tests/test_auto_DWIDenoise.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def test_DWIDenoise_inputs():
3232
),
3333
out_file=dict(
3434
argstr='%s',
35+
genfile=True,
3536
keep_extension=True,
3637
name_source='in_file',
3738
name_template='%s_denoised',
@@ -44,7 +45,10 @@ def test_DWIDenoise_inputs():
4445
for metakey, value in list(metadata.items()):
4546
assert getattr(inputs.traits()[key], metakey) == value
4647
def test_DWIDenoise_outputs():
47-
output_map = dict(out_file=dict(), )
48+
output_map = dict(
49+
noise=dict(),
50+
out_file=dict(),
51+
)
4852
outputs = DWIDenoise.output_spec()
4953

5054
for key, metadata in list(output_map.items()):
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..preprocess import MRDeGibbs
4+
5+
6+
def test_MRDeGibbs_inputs():
7+
input_map = dict(
8+
args=dict(argstr='%s', ),
9+
axes=dict(
10+
argstr='-axes %s',
11+
maxlen=2,
12+
minlen=2,
13+
sep=',',
14+
usedefault=True,
15+
),
16+
bval_scale=dict(argstr='-bvalue_scaling %s', ),
17+
environ=dict(
18+
nohash=True,
19+
usedefault=True,
20+
),
21+
grad_file=dict(argstr='-grad %s', ),
22+
grad_fsl=dict(argstr='-fslgrad %s %s', ),
23+
in_bval=dict(),
24+
in_bvec=dict(argstr='-fslgrad %s %s', ),
25+
in_file=dict(
26+
argstr='%s',
27+
mandatory=True,
28+
position=-2,
29+
),
30+
maxW=dict(
31+
argstr='-maxW %d',
32+
usedefault=True,
33+
),
34+
minW=dict(
35+
argstr='-minW %d',
36+
usedefault=True,
37+
),
38+
nshifts=dict(
39+
argstr='-nshifts %d',
40+
usedefault=True,
41+
),
42+
nthreads=dict(
43+
argstr='-nthreads %d',
44+
nohash=True,
45+
),
46+
out_file=dict(
47+
argstr='%s',
48+
genfile=True,
49+
keep_extension=True,
50+
name_source='in_file',
51+
name_template='%s_unr',
52+
position=-1,
53+
),
54+
)
55+
inputs = MRDeGibbs.input_spec()
56+
57+
for key, metadata in list(input_map.items()):
58+
for metakey, value in list(metadata.items()):
59+
assert getattr(inputs.traits()[key], metakey) == value
60+
def test_MRDeGibbs_outputs():
61+
output_map = dict(out_file=dict(), )
62+
outputs = MRDeGibbs.output_spec()
63+
64+
for key, metadata in list(output_map.items()):
65+
for metakey, value in list(metadata.items()):
66+
assert getattr(outputs.traits()[key], metakey) == value

0 commit comments

Comments
 (0)