Skip to content

ENH: Add new dwifslpreproc interface for MRtrix3 #3278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Dec 8, 2020
Merged
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
146 changes: 146 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,151 @@ 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=-10, mandatory=True, desc="input DWI image"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these position arguments really necessary for all inputs?

Copy link
Contributor Author

@axiezai axiezai Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary, this was my lazy way of ensuring the interface command stayed consistent with the examples from the mrtrix documentation.

I think if position is left empty, the inputs appear right after dwifslpreproc? I was going for a format of dwifslpreproc in_file out_file -rpe_options -other_options to follow the examples here.

Is there a way to achieve that without specifying positions for everything?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's necessary, I think you could set position of 0 for in_file, 1 for out_file, 2 for rpe_options, and leave it unset for the rest. If it's not actually necessary for an option, I would personally leave it unset, because you're indicating to future maintainers that they need to be very particular about this.

)
out_file = File(
"preproc.mif",
argstr="%s",
mandatory=True,
position=-9,
usedefault=True,
desc="output file after preprocessing",
)
rpe_options = traits.Enum(
"none",
"pair",
"all",
"header",
argstr="-rpe_%s",
position=-8,
mandatory=True,
desc="Specify acquisition phase-encoding design, one of the -rpe_* options must be provided",
)
pe_dir = traits.Str(
argstr="-pe_dir %s",
position=-7,
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",
position=-6,
desc="Total readout time of input series (in seconds)",
)
in_epi = File(
exists=True,
argstr="-se_epi %s",
position=-5,
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",
position=-4,
desc="Achieve alignment between the SE-EPI images used for inhomogeneity field estimation, and the DWIs",
)
eddy_options = traits.Str(
argstr="-eddy_options \"%s\"",
position=-3,
desc="Manually provide additional command-line options to the eddy command",
)
topup_options = traits.Str(
argstr="-topup_options \"%s\"",
position=-3,
desc="Manually provide additional command-line options to the topup command",
)
export_grad_mrtrix = traits.Bool(
argstr="-export_grad_mrtrix",
position=-2,
desc="export new gradient files in mrtrix format",
)
export_grad_fsl = traits.Bool(
argstr="-export_grad_fsl",
position=-2,
desc="export gradient files in FSL format",
)
out_grad_mrtrix = File(
"grad.b",
argstr="%s",
usedefault=True,
position=-1,
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",
position=-1,
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 -pe_dir j -readout_time 0.165240 -eddy_options "--slm=linear --repol" -export_grad_mrtrix grad.b'
>>> 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
55 changes: 55 additions & 0 deletions nipype/interfaces/mrtrix3/tests/test_auto_DWIPreproc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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", position=-4),
args=dict(argstr="%s"),
bval_scale=dict(argstr="-bvalue_scaling %s"),
eddy_options=dict(argstr="-eddy_options %s", position=-3),
environ=dict(nohash=True, usedefault=True),
export_grad_fsl=dict(argstr="-export_grad_fsl", position=-2),
export_grad_mrtrix=dict(argstr="-export_grad_mrtrix", position=-2),
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, position=-5),
in_file=dict(argstr="%s", extensions=None, mandatory=True, position=-10),
nthreads=dict(argstr="-nthreads %d", nohash=True),
out_file=dict(
argstr="%s", extensions=None, mandatory=True, position=-9, usedefault=True
),
out_grad_fsl=dict(argstr="%s, %s", position=-1, requires=["export_grad_fsl"]),
out_grad_mrtrix=dict(
argstr="%s",
extensions=None,
position=-1,
requires=["export_grad_mrtrix"],
usedefault=True,
),
pe_dir=dict(argstr="-pe_dir %s", mandatory=True, position=-7),
ro_time=dict(argstr="-readout_time %f", position=-6),
rpe_options=dict(argstr="%s", mandatory=True, position=-8),
topup_options=dict(argstr="-topup_options %s", position=-3),
)
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.