Skip to content

Commit

Permalink
ENH: Add new dwifslpreproc interface for MRtrix3 (#3278)
Browse files Browse the repository at this point in the history
* added mrtrix3 dwifslpreproc as interface

* use defaults added for outputs

* Added dwifslpreproc interface to __init__

* edited doc string to correct order

* added doctest skip

* added MRtrix3 gradient empty test data

* specified positions, requires for gradient exports

* finalized docstring example and pytest

* added entry to contribution list

* simplify phase-encoding design input

Moved `-rpe_` to `argstr` for simpler input.

Co-authored-by: Chris Markiewicz <[email protected]>

* Apply suggestions from code review

Fixed typos and improved `-eddy` and `-topup` optional inputs.

Co-authored-by: Chris Markiewicz <[email protected]>

* Update nipype/interfaces/mrtrix3/preprocess.py

Co-authored-by: Xihe Xie <[email protected]>

* fixed input positions according to review

* fixed doc string for new positions

Co-authored-by: Chris Markiewicz <[email protected]>
  • Loading branch information
axiezai and effigies authored Dec 8, 2020
1 parent 54f5029 commit 47fe00b
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,11 @@
{
"name": "Tambini, Arielle"
},
{
"affiliation": "Weill Cornell Medicine",
"name": "Xie, Xihe",
"orcid": "0000-0001-6595-2473"
},
{
"affiliation": "Max Planck Institute for Human Cognitive and Brain Sciences, Leipzig, Germany.",
"name": "Mihai, Paul Glad",
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/mrtrix3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ResponseSD,
ACTPrepareFSL,
ReplaceFSwithFIRST,
DWIPreproc,
DWIDenoise,
MRDeGibbs,
DWIBiasCorrect,
Expand Down
134 changes: 134 additions & 0 deletions nipype/interfaces/mrtrix3/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ class DWIBiasCorrect(MRTrix3Base):
_cmd = "dwibiascorrect"
input_spec = DWIBiasCorrectInputSpec
output_spec = DWIBiasCorrectOutputSpec

def _format_arg(self, name, trait_spec, value):
if name in ("use_ants", "use_fsl"):
ver = self.version
Expand All @@ -241,6 +242,139 @@ def _format_arg(self, name, trait_spec, value):
return f"-{trait_spec.argstr}"
return super()._format_arg(name, trait_spec, value)


class DWIPreprocInputSpec(MRTrix3BaseInputSpec):
in_file = File(
exists=True, argstr="%s", position=0, mandatory=True, desc="input DWI image"
)
out_file = File(
"preproc.mif",
argstr="%s",
mandatory=True,
position=1,
usedefault=True,
desc="output file after preprocessing",
)
rpe_options = traits.Enum(
"none",
"pair",
"all",
"header",
argstr="-rpe_%s",
position=2,
mandatory=True,
desc='Specify acquisition phase-encoding design. "none" for no reversed phase-encoding image, "all" for all DWIs have opposing phase-encoding acquisition, "pair" for using a pair of b0 volumes for inhomogeneity field estimation only, and "header" for phase-encoding information can be found in the image header(s)',
)
pe_dir = traits.Str(
argstr="-pe_dir %s",
mandatory=True,
desc="Specify the phase encoding direction of the input series, can be a signed axis number (e.g. -0, 1, +2), an axis designator (e.g. RL, PA, IS), or NIfTI axis codes (e.g. i-, j, k)",
)
ro_time = traits.Float(
argstr="-readout_time %f",
desc="Total readout time of input series (in seconds)",
)
in_epi = File(
exists=True,
argstr="-se_epi %s",
desc="Provide an additional image series consisting of spin-echo EPI images, which is to be used exclusively by topup for estimating the inhomogeneity field (i.e. it will not form part of the output image series)",
)
align_seepi = traits.Bool(
argstr="-align_seepi",
desc="Achieve alignment between the SE-EPI images used for inhomogeneity field estimation, and the DWIs",
)
eddy_options = traits.Str(
argstr='-eddy_options "%s"',
desc="Manually provide additional command-line options to the eddy command",
)
topup_options = traits.Str(
argstr='-topup_options "%s"',
desc="Manually provide additional command-line options to the topup command",
)
export_grad_mrtrix = traits.Bool(
argstr="-export_grad_mrtrix", desc="export new gradient files in mrtrix format"
)
export_grad_fsl = traits.Bool(
argstr="-export_grad_fsl", desc="export gradient files in FSL format"
)
out_grad_mrtrix = File(
"grad.b",
argstr="%s",
usedefault=True,
requires=["export_grad_mrtrix"],
desc="name of new gradient file",
)
out_grad_fsl = traits.Tuple(
File("grad.bvecs", usedefault=True, desc="bvecs"),
File("grad.bvals", usedefault=True, desc="bvals"),
argstr="%s, %s",
requires=["export_grad_fsl"],
desc="Output (bvecs, bvals) gradients FSL format",
)


class DWIPreprocOutputSpec(TraitedSpec):
out_file = File(argstr="%s", desc="output preprocessed image series")
out_grad_mrtrix = File(
"grad.b",
argstr="%s",
usedefault=True,
desc="preprocessed gradient file in mrtrix3 format",
)
out_fsl_bvec = File(
"grad.bvecs",
argstr="%s",
usedefault=True,
desc="exported fsl gradient bvec file",
)
out_fsl_bval = File(
"grad.bvals",
argstr="%s",
usedefault=True,
desc="exported fsl gradient bval file",
)


class DWIPreproc(MRTrix3Base):
"""
Perform diffusion image pre-processing using FSL's eddy tool; including inhomogeneity distortion correction using FSL's topup tool if possible
For more information, see
<https://mrtrix.readthedocs.io/en/latest/reference/commands/dwifslpreproc.html>
Example
-------
>>> import nipype.interfaces.mrtrix3 as mrt
>>> preproc = mrt.DWIPreproc()
>>> preproc.inputs.in_file = 'dwi.mif'
>>> preproc.inputs.rpe_options = 'none'
>>> preproc.inputs.out_file = "preproc.mif"
>>> preproc.inputs.eddy_options = '--slm=linear --repol' # linear second level model and replace outliers
>>> preproc.inputs.export_grad_mrtrix = True # export final gradient table in MRtrix format
>>> preproc.inputs.ro_time = 0.165240 # 'TotalReadoutTime' in BIDS JSON metadata files
>>> preproc.inputs.pe_dir = 'j' # 'PhaseEncodingDirection' in BIDS JSON metadata files
>>> preproc.cmdline
'dwifslpreproc dwi.mif preproc.mif -rpe_none -eddy_options "--slm=linear --repol" -export_grad_mrtrix grad.b -pe_dir j -readout_time 0.165240'
>>> preproc.run() # doctest: +SKIP
"""

_cmd = "dwifslpreproc"
input_spec = DWIPreprocInputSpec
output_spec = DWIPreprocOutputSpec

def _list_outputs(self):
outputs = self.output_spec().get()
outputs["out_file"] = op.abspath(self.inputs.out_file)
if self.inputs.export_grad_mrtrix:
outputs["out_grad_mrtrix"] = op.abspath(self.inputs.out_grad_mrtrix)
if self.inputs.export_grad_fsl:
outputs["out_fsl_bvec"] = op.abspath(self.inputs.out_grad_fsl[0])
outputs["out_fsl_bval"] = op.abspath(self.inputs.out_grad_fsl[1])

return outputs


class ResponseSDInputSpec(MRTrix3BaseInputSpec):
algorithm = traits.Enum(
"msmt_5tt",
Expand Down
54 changes: 54 additions & 0 deletions nipype/interfaces/mrtrix3/tests/test_auto_DWIPreproc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from ..preprocess import DWIPreproc


def test_DWIPreproc_inputs():
input_map = dict(
align_seepi=dict(argstr="-align_seepi"),
args=dict(argstr="%s"),
bval_scale=dict(argstr="-bvalue_scaling %s"),
eddy_options=dict(argstr='-eddy_options "%s"'),
environ=dict(nohash=True, usedefault=True),
export_grad_fsl=dict(argstr="-export_grad_fsl"),
export_grad_mrtrix=dict(argstr="-export_grad_mrtrix"),
grad_file=dict(argstr="-grad %s", extensions=None, xor=["grad_fsl"]),
grad_fsl=dict(argstr="-fslgrad %s %s", xor=["grad_file"]),
in_bval=dict(extensions=None),
in_bvec=dict(argstr="-fslgrad %s %s", extensions=None),
in_epi=dict(argstr="-se_epi %s", extensions=None),
in_file=dict(argstr="%s", extensions=None, mandatory=True, position=0),
nthreads=dict(argstr="-nthreads %d", nohash=True),
out_file=dict(
argstr="%s", extensions=None, mandatory=True, position=1, usedefault=True
),
out_grad_fsl=dict(argstr="%s, %s", requires=["export_grad_fsl"]),
out_grad_mrtrix=dict(
argstr="%s",
extensions=None,
requires=["export_grad_mrtrix"],
usedefault=True,
),
pe_dir=dict(argstr="-pe_dir %s", mandatory=True),
ro_time=dict(argstr="-readout_time %f"),
rpe_options=dict(argstr="-rpe_%s", mandatory=True, position=2),
topup_options=dict(argstr='-topup_options "%s"'),
)
inputs = DWIPreproc.input_spec()

for key, metadata in list(input_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(inputs.traits()[key], metakey) == value


def test_DWIPreproc_outputs():
output_map = dict(
out_file=dict(argstr="%s", extensions=None),
out_fsl_bval=dict(argstr="%s", extensions=None, usedefault=True),
out_fsl_bvec=dict(argstr="%s", extensions=None, usedefault=True),
out_grad_mrtrix=dict(argstr="%s", extensions=None, usedefault=True),
)
outputs = DWIPreproc.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
Empty file added nipype/testing/data/grad.b
Empty file.

0 comments on commit 47fe00b

Please sign in to comment.