Skip to content

Commit

Permalink
Merge pull request #3394 from fabioboh/master
Browse files Browse the repository at this point in the history
ENH: added Apply VDM functionality to FieldMap SPM interface
  • Loading branch information
effigies authored Oct 21, 2021
2 parents 20331fd + 4575810 commit eab4b2a
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@
"name": "Geisler, Daniel",
"orcid": "0000-0003-2076-5329"
},
{
"affiliation": "Division of Psychological and Social Medicine and Developmental Neuroscience, Faculty of Medicine, Technische Universit\u00e4t Dresden, Dresden, Germany",
"name": "Bernardoni, Fabio",
"orcid": "0000-0002-5112-405X"
},
{
"name": "Salvatore, John"
},
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/spm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""SPM is a software package for the analysis of brain imaging data sequences."""
from .base import Info, SPMCommand, logger, no_spm, scans_for_fname, scans_for_fnames
from .preprocess import (
ApplyVDM,
FieldMap,
SliceTiming,
Realign,
Expand Down
138 changes: 133 additions & 5 deletions nipype/interfaces/spm/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@


class FieldMapInputSpec(SPMCommandInputSpec):

jobtype = traits.Enum(
"calculatevdm",
"applyvdm",
usedefault=True,
desc="one of: calculatevdm, applyvdm",
deprecated="1.9.0", # Two minor releases in the future
desc="Must be 'calculatevdm'; to apply VDM, use the ApplyVDM interface.",
)

phase_file = File(
mandatory=True,
exists=True,
Expand Down Expand Up @@ -231,22 +233,148 @@ class FieldMap(SPMCommand):

def _format_arg(self, opt, spec, val):
"""Convert input to appropriate format for spm"""

if opt in ["phase_file", "magnitude_file", "anat_file", "epi_file"]:

return scans_for_fname(ensure_list(val))

return super(FieldMap, self)._format_arg(opt, spec, val)

def _parse_inputs(self):
"""validate spm fieldmap options if set to None ignore"""

einputs = super(FieldMap, self)._parse_inputs()
return [{self.inputs.jobtype: einputs[0]}]
return [{"calculatevdm": einputs[0]}]

def _list_outputs(self):
outputs = self._outputs().get()
jobtype = self.inputs.jobtype
if jobtype == "calculatevdm":
outputs["vdm"] = fname_presuffix(self.inputs.phase_file, prefix="vdm5_sc")

outputs["vdm"] = fname_presuffix(self.inputs.phase_file, prefix="vdm5_sc")

return outputs


class ApplyVDMInputSpec(SPMCommandInputSpec):

in_files = InputMultiObject(
ImageFileSPM(exists=True),
field="data.scans",
mandatory=True,
copyfile=True,
desc="list of filenames to apply the vdm to",
)
vdmfile = File(
field="data.vdmfile",
desc="Voxel displacement map to use",
mandatory=True,
copyfile=True,
)
distortion_direction = traits.Int(
2,
field="roptions.pedir",
desc="phase encode direction input data have been acquired with",
usedefault=True,
)
write_which = traits.ListInt(
[2, 1],
field="roptions.which",
minlen=2,
maxlen=2,
usedefault=True,
desc="If the first value is non-zero, reslice all images. If the second value is non-zero, reslice a mean image.",
)
interpolation = traits.Range(
value=4,
low=0,
high=7,
field="roptions.rinterp",
desc="degree of b-spline used for interpolation",
)
write_wrap = traits.List(
traits.Int(),
minlen=3,
maxlen=3,
field="roptions.wrap",
desc=("Check if interpolation should wrap in [x,y,z]"),
)
write_mask = traits.Bool(
field="roptions.mask", desc="True/False mask time series images"
)
out_prefix = traits.String(
"u",
field="roptions.prefix",
usedefault=True,
desc="fieldmap corrected output prefix",
)


class ApplyVDMOutputSpec(TraitedSpec):
out_files = OutputMultiPath(
traits.Either(traits.List(File(exists=True)), File(exists=True)),
desc=("These will be the fieldmap corrected files."),
)
mean_image = File(exists=True, desc="Mean image")


class ApplyVDM(SPMCommand):
"""Use the fieldmap toolbox from spm to apply the voxel displacement map (VDM) to some epi files.
http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=173
.. important::
This interface does not deal with real/imag magnitude images nor
with the two phase files case.
"""

input_spec = ApplyVDMInputSpec
output_spec = ApplyVDMOutputSpec
_jobtype = "tools"
_jobname = "fieldmap"

def _format_arg(self, opt, spec, val):
"""Convert input to appropriate format for spm"""

if opt in ["in_files", "vdmfile"]:
return scans_for_fname(ensure_list(val))
return super(FieldMap, self)._format_arg(opt, spec, val)

def _parse_inputs(self):
"""validate spm fieldmap options if set to None ignore"""

einputs = super(ApplyVDM, self)._parse_inputs()

return [{"applymap": einputs[0]}]

def _list_outputs(self):
outputs = self._outputs().get()
jobtype = self.inputs.jobtype
resliced_all = self.inputs.write_which[0] > 0
resliced_mean = self.inputs.write_which[1] > 0
if resliced_mean:
if isinstance(self.inputs.in_files[0], list):
first_image = self.inputs.in_files[0][0]
else:
first_image = self.inputs.in_files[0]
outputs["mean_image"] = fname_presuffix(first_image, prefix="meanu")

if resliced_all:
outputs["out_files"] = []
for idx, imgf in enumerate(ensure_list(self.inputs.in_files)):
appliedvdm_run = []
if isinstance(imgf, list):
for i, inner_imgf in enumerate(ensure_list(imgf)):
newfile = fname_presuffix(
inner_imgf, prefix=self.inputs.out_prefix
)
appliedvdm_run.append(newfile)
else:
appliedvdm_run = fname_presuffix(
imgf, prefix=self.inputs.out_prefix
)
outputs["out_files"].append(appliedvdm_run)
return outputs


Expand Down
70 changes: 70 additions & 0 deletions nipype/interfaces/spm/tests/test_auto_ApplyVDM.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from ..preprocess import ApplyVDM


def test_ApplyVDM_inputs():
input_map = dict(
distortion_direction=dict(
field="roptions.pedir",
usedefault=True,
),
in_files=dict(
copyfile=True,
field="data.scans",
mandatory=True,
),
interpolation=dict(
field="roptions.rinterp",
),
matlab_cmd=dict(),
mfile=dict(
usedefault=True,
),
out_prefix=dict(
field="roptions.prefix",
usedefault=True,
),
paths=dict(),
use_mcr=dict(),
use_v8struct=dict(
min_ver="8",
usedefault=True,
),
vdmfile=dict(
copyfile=True,
extensions=None,
field="data.vdmfile",
mandatory=True,
),
write_mask=dict(
field="roptions.mask",
),
write_which=dict(
field="roptions.which",
maxlen=2,
minlen=2,
usedefault=True,
),
write_wrap=dict(
field="roptions.wrap",
),
)
inputs = ApplyVDM.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_ApplyVDM_outputs():
output_map = dict(
mean_image=dict(
extensions=None,
),
out_files=dict(),
)
outputs = ApplyVDM.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
1 change: 1 addition & 0 deletions nipype/interfaces/spm/tests/test_auto_FieldMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def test_FieldMap_inputs():
usedefault=True,
),
jobtype=dict(
deprecated="1.9.0",
usedefault=True,
),
magnitude_file=dict(
Expand Down
1 change: 1 addition & 0 deletions nipype/sphinxext/plot_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
Provide a customized template for preparing restructured text.
"""

import sys
import os
import shutil
Expand Down

0 comments on commit eab4b2a

Please sign in to comment.