Skip to content

ENH: Adds interfaces for MRtrix utils shconv and sh2amp #3280

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 24 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1f3d251
added predicted_signal option to tensor
tclose Sep 29, 2020
a9a8904
added SHConv and SH2Amp interfaces to mrtrix utils
tclose Dec 2, 2020
ac7490c
added name to authors list
tclose Dec 2, 2020
974ca38
touched up docs and made commands inherit from commandline instead of…
tclose Dec 2, 2020
ccd25c6
changed isdefined checks of EstimateFOD and check
tclose Dec 2, 2020
f245ba4
fixed up doctest strings and added dummy files
tclose Dec 2, 2020
c81259b
run black over touched files
tclose Dec 2, 2020
ab96460
ran make specs
tclose Dec 2, 2020
cd4bbc6
fixed up doctest failure
tclose Dec 2, 2020
35a762f
Update nipype/interfaces/mrtrix3/reconst.py
tclose May 6, 2021
7e670ff
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
8671983
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
308436d
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
f4eefeb
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
70e3bcf
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
a6e440d
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
5266a7c
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
c5d7ffe
Update nipype/interfaces/mrtrix3/utils.py
tclose May 6, 2021
fd8cc40
Update nipype/interfaces/mrtrix3/utils.py
tclose Jun 11, 2021
76ccb77
Update nipype/interfaces/mrtrix3/utils.py
tclose Jun 11, 2021
af8e01f
Update nipype/interfaces/mrtrix3/utils.py
tclose Jun 11, 2021
f698f4b
Update nipype/interfaces/mrtrix3/utils.py
tclose Jun 11, 2021
5e5ffbf
Merge branch 'master' of github.com:nipy/nipype into predicted_signal
tclose Jun 11, 2021
e096c19
TEST: make specs
effigies Jun 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,11 @@
{
"name": "Marina, Ana"
},
{
"affiliation": "University of Sydney",
"name": "Close, Thomas",
"orcid": "0000-0002-4160-2134"
},
{
"name": "Davison, Andrew"
},
Expand Down
2 changes: 2 additions & 0 deletions nipype/interfaces/mrtrix3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
MRConvert,
MRResize,
DWIExtract,
SHConv,
SH2Amp,
)
from .preprocess import (
ResponseSD,
Expand Down
34 changes: 31 additions & 3 deletions nipype/interfaces/mrtrix3/reconst.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import os.path as op

from ..base import traits, TraitedSpec, File, Undefined, InputMultiObject
from ..base import traits, TraitedSpec, File, InputMultiObject, isdefined
from .base import MRTrix3BaseInputSpec, MRTrix3Base


Expand Down Expand Up @@ -50,10 +50,18 @@ class FitTensorInputSpec(MRTrix3BaseInputSpec):
"only applies to the non-linear methods"
),
)
predicted_signal = File(
argstr="-predicted_signal %s",
desc=(
"specify a file to contain the predicted signal from the tensor "
"fits. This can be used to calculate the residual signal"
),
)


class FitTensorOutputSpec(TraitedSpec):
out_file = File(exists=True, desc="the output DTI file")
predicted_signal = File(desc="Predicted signal from fitted tensors")


class FitTensor(MRTrix3Base):
Expand Down Expand Up @@ -81,6 +89,8 @@ class FitTensor(MRTrix3Base):
def _list_outputs(self):
outputs = self.output_spec().get()
outputs["out_file"] = op.abspath(self.inputs.out_file)
if isdefined(self.inputs.predicted_signal):
outputs["predicted_signal"] = op.abspath(self.inputs.predicted_signal)
return outputs


Expand Down Expand Up @@ -144,12 +154,23 @@ class EstimateFODInputSpec(MRTrix3BaseInputSpec):
"[ az el ] pairs for the directions."
),
)
predicted_signal = File(
argstr="-predicted_signal %s",
desc=(
"specify a file to contain the predicted signal from the FOD "
"estimates. This can be used to calculate the residual signal."
"Note that this is only valid if algorithm == 'msmt_csd'. "
"For single shell reconstructions use a combination of SHConv "
"and SH2Amp instead."
),
)


class EstimateFODOutputSpec(TraitedSpec):
wm_odf = File(argstr="%s", desc="output WM ODF")
gm_odf = File(argstr="%s", desc="output GM ODF")
csf_odf = File(argstr="%s", desc="output CSF ODF")
predicted_signal = File(desc="output predicted signal")


class EstimateFOD(MRTrix3Base):
Expand Down Expand Up @@ -183,10 +204,17 @@ class EstimateFOD(MRTrix3Base):
def _list_outputs(self):
outputs = self.output_spec().get()
outputs["wm_odf"] = op.abspath(self.inputs.wm_odf)
if self.inputs.gm_odf != Undefined:
if isdefined(self.inputs.gm_odf):
outputs["gm_odf"] = op.abspath(self.inputs.gm_odf)
if self.inputs.csf_odf != Undefined:
if isdefined(self.inputs.csf_odf):
outputs["csf_odf"] = op.abspath(self.inputs.csf_odf)
if isdefined(self.inputs.predicted_signal):
if self.inputs.algorithm != "msmt_csd":
raise Exception(
"'predicted_signal' option can only be used with "
"the 'msmt_csd' algorithm"
)
outputs["predicted_signal"] = op.abspath(self.inputs.predicted_signal)
return outputs


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ def test_ConstrainedSphericalDeconvolution_inputs():
argstr="-nthreads %d",
nohash=True,
),
predicted_signal=dict(
argstr="-predicted_signal %s",
extensions=None,
),
shell=dict(
argstr="-shell %s",
sep=",",
Expand Down Expand Up @@ -112,6 +116,9 @@ def test_ConstrainedSphericalDeconvolution_outputs():
argstr="%s",
extensions=None,
),
predicted_signal=dict(
extensions=None,
),
wm_odf=dict(
argstr="%s",
extensions=None,
Expand Down
7 changes: 7 additions & 0 deletions nipype/interfaces/mrtrix3/tests/test_auto_EstimateFOD.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ def test_EstimateFOD_inputs():
argstr="-nthreads %d",
nohash=True,
),
predicted_signal=dict(
argstr="-predicted_signal %s",
extensions=None,
),
shell=dict(
argstr="-shell %s",
sep=",",
Expand Down Expand Up @@ -115,6 +119,9 @@ def test_EstimateFOD_outputs():
argstr="%s",
extensions=None,
),
predicted_signal=dict(
extensions=None,
),
wm_odf=dict(
argstr="%s",
extensions=None,
Expand Down
7 changes: 7 additions & 0 deletions nipype/interfaces/mrtrix3/tests/test_auto_FitTensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def test_FitTensor_inputs():
position=-1,
usedefault=True,
),
predicted_signal=dict(
argstr="-predicted_signal %s",
extensions=None,
),
reg_term=dict(
argstr="-regularisation %f",
max_ver="0.3.13",
Expand All @@ -71,6 +75,9 @@ def test_FitTensor_outputs():
out_file=dict(
extensions=None,
),
predicted_signal=dict(
extensions=None,
),
)
outputs = FitTensor.output_spec()

Expand Down
55 changes: 55 additions & 0 deletions nipype/interfaces/mrtrix3/tests/test_auto_SH2Amp.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 ..utils import SH2Amp


def test_SH2Amp_inputs():
input_map = dict(
args=dict(
argstr="%s",
),
directions=dict(
argstr="%s",
extensions=None,
mandatory=True,
position=-2,
),
environ=dict(
nohash=True,
usedefault=True,
),
in_file=dict(
argstr="%s",
extensions=None,
mandatory=True,
position=-3,
),
nonnegative=dict(
argstr="-nonnegative",
),
out_file=dict(
argstr="%s",
extensions=None,
name_source=["in_file"],
name_template="%s_amp.mif",
position=-1,
usedefault=True,
),
)
inputs = SH2Amp.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_SH2Amp_outputs():
output_map = dict(
out_file=dict(
extensions=None,
),
)
outputs = SH2Amp.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
52 changes: 52 additions & 0 deletions nipype/interfaces/mrtrix3/tests/test_auto_SHConv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from ..utils import SHConv


def test_SHConv_inputs():
input_map = dict(
args=dict(
argstr="%s",
),
environ=dict(
nohash=True,
usedefault=True,
),
in_file=dict(
argstr="%s",
extensions=None,
mandatory=True,
position=-3,
),
out_file=dict(
argstr="%s",
extensions=None,
name_source=["in_file"],
name_template="%s_shconv.mif",
position=-1,
usedefault=True,
),
response=dict(
argstr="%s",
extensions=None,
mandatory=True,
position=-2,
),
)
inputs = SHConv.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_SHConv_outputs():
output_map = dict(
out_file=dict(
extensions=None,
),
)
outputs = SHConv.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
122 changes: 122 additions & 0 deletions nipype/interfaces/mrtrix3/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,3 +765,125 @@ class MRResize(MRTrix3Base):
_cmd = "mrresize"
input_spec = MRResizeInputSpec
output_spec = MRResizeOutputSpec


class SHConvInputSpec(CommandLineInputSpec):
in_file = File(
exists=True,
argstr="%s",
mandatory=True,
position=-3,
desc="input ODF image",
)
# General options
response = File(
exists=True,
mandatory=True,
argstr="%s",
position=-2,
desc=("The response function"),
)
out_file = File(
name_template="%s_shconv.mif",
name_source=["in_file"],
argstr="%s",
position=-1,
usedefault=True,
desc="the output spherical harmonics",
)


class SHConvOutputSpec(TraitedSpec):
out_file = File(exists=True, desc="the output convoluted spherical harmonics file")


class SHConv(CommandLine):
"""
Convolve spherical harmonics with a tissue response function. Useful for
checking residuals of ODF estimates.


Example
-------

>>> import nipype.interfaces.mrtrix3 as mrt
>>> sh = mrt.SHConv()
>>> sh.inputs.in_file = 'csd.mif'
>>> sh.inputs.response = 'response.txt'
>>> sh.cmdline
'shconv csd.mif response.txt csd_shconv.mif'
>>> sh.run() # doctest: +SKIP
"""

_cmd = "shconv"
input_spec = SHConvInputSpec
output_spec = SHConvOutputSpec

def _list_outputs(self):
outputs = self.output_spec().get()
outputs["out_file"] = op.abspath(self.inputs.out_file)
return outputs


class SH2AmpInputSpec(CommandLineInputSpec):
in_file = File(
exists=True,
argstr="%s",
mandatory=True,
position=-3,
desc="input ODF image",
)
# General options
directions = File(
exists=True,
mandatory=True,
argstr="%s",
position=-2,
desc=(
"The gradient directions along which to sample the spherical "
"harmonics MRtrix format"
),
)
out_file = File(
name_template="%s_amp.mif",
name_source=["in_file"],
argstr="%s",
position=-1,
usedefault=True,
desc="the output spherical harmonics",
)
nonnegative = traits.Bool(
argstr="-nonnegative", desc="cap all negative amplitudes to zero"
)


class SH2AmpOutputSpec(TraitedSpec):
out_file = File(exists=True, desc="the output convoluted spherical harmonics file")


class SH2Amp(CommandLine):
"""
Sample spherical harmonics on a set of gradient orientations. Useful for
checking residuals of ODF estimates.


Example
-------

>>> import nipype.interfaces.mrtrix3 as mrt
>>> sh = mrt.SH2Amp()
>>> sh.inputs.in_file = 'sh.mif'
>>> sh.inputs.directions = 'grads.txt'
>>> sh.cmdline
'sh2amp sh.mif grads.txt sh_amp.mif'
>>> sh.run() # doctest: +SKIP
"""

_cmd = "sh2amp"
input_spec = SH2AmpInputSpec
output_spec = SH2AmpOutputSpec

def _list_outputs(self):
outputs = self.output_spec().get()
outputs["out_file"] = op.abspath(self.inputs.out_file)
return outputs
Empty file added nipype/testing/data/grads.txt
Empty file.
Empty file added nipype/testing/data/sh.mif
Empty file.