-
Notifications
You must be signed in to change notification settings - Fork 533
[ENH/WIP] Add SPM Fieldmap Tool wrapper #1905
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
Changes from 1 commit
d1cee47
0fd77fc
302be56
39715f2
c397662
88fee4a
dd7569f
a4994ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,147 @@ | |
__docformat__ = 'restructuredtext' | ||
|
||
|
||
class FieldMapInputSpec(SPMCommandInputSpec): | ||
jobtype = traits.Enum('calculatevdm', 'applyvdm', usedefault=True, | ||
desc='one of: calculatevdm, applyvdm') | ||
phase = File(mandatory=True, exists=True, copyfile=False, | ||
field='subj.data.presubphasemag.phase', | ||
desc='presubstracted phase file') | ||
magnitude = File(mandatory=True, exists=True, copyfile=False, | ||
field='subj.data.presubphasemag.magnitude', | ||
desc='presubstracted magnitude file') | ||
et = traits.List(traits.Float(), minlen=2, maxlen=2, mandatory=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe:
? |
||
field='subj.defaults.defaultsval.et', | ||
desc='short and long echo times') | ||
maskbrain = traits.Bool(True, usedefault=True, | ||
field='subj.defaults.defaultsval.maskbrain', | ||
desc='masking or no masking of the brain') | ||
blipdir = traits.Enum(1, -1, mandatory=True, | ||
field='subj.defaults.defaultsval.blipdir', | ||
desc='polarity of the phase-encode blips') | ||
tert = traits.Float(mandatory=True, | ||
field='subj.defaults.defaultsval.tert', | ||
desc='total EPI readout time') | ||
epifm = traits.Bool(False, usedefault=True, | ||
field='subj.defaults.defaultsval.epifm', | ||
desc='epi-based field map'); | ||
ajm = traits.Bool(False, usedefault=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, again, I would consider |
||
field='subj.defaults.defaultsval.ajm', | ||
desc='jacobian modulation'); | ||
# Unwarping defaults parameters | ||
method = traits.Enum('Mark3D', 'Mark2D', 'Huttonish', usedefault=True, | ||
desc='One of: Mark3D, Mark2D, Huttonish', | ||
field='subj.defaults.defaultsval.uflags.method'); | ||
fwhm = traits.Range(low=0, value=10, usedefault=True, | ||
field='subj.defaults.defaultsval.uflags.fwhm', | ||
desc='gaussian smoothing kernel width'); | ||
pad = traits.Range(low=0, value=0, usedefault=True, | ||
field='subj.defaults.defaultsval.uflags.pad', | ||
desc='padding kernel width'); | ||
ws = traits.Bool(True, usedefault=True, | ||
field='subj.defaults.defaultsval.uflags.ws', | ||
desc='weighted smoothing'); | ||
# Brain mask defaults parameters | ||
template = traits.File(copyfile=False, exists=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use |
||
field='subj.defaults.defaultsval.mflags.template', | ||
desc='template image for brain masking'); | ||
fwhm = traits.Range(low=0, value=5, usedefault=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You've got |
||
field='subj.defaults.defaultsval.mflags.fwhm', | ||
desc='gaussian smoothing kernel width'); | ||
nerode = traits.Range(low=0, value=2, usedefault=True, | ||
field='subj.defaults.defaultsval.mflags.nerode', | ||
desc='number of erosions'); | ||
ndilate = traits.Range(low=0, value=4, usedefault=True, | ||
field='subj.defaults.defaultsval.mflags.ndilate', | ||
desc='number of erosions'); | ||
thresh = traits.Float(0.5, usedefault=True, | ||
field='subj.defaults.defaultsval.mflags.thresh', | ||
desc='threshold used to create brain mask from segmented data'); | ||
reg = traits.Float(0.02, usedefault=True, | ||
field='subj.defaults.defaultsval.mflags.reg', | ||
desc='regularization value used in the segmentation'); | ||
# EPI unwarping for quality check | ||
epi = traits.File(copyfile=False, exists=True, mandatory=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File from base |
||
field='subj.session.epi', | ||
desc='EPI to unwarp'); | ||
matchvdm = traits.Bool(True, usedefault=True, | ||
field='subj.matchvdm', | ||
desc='match VDM to EPI'); | ||
sessname = traits.String('_run-', usedefault=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please, modify the top Then use it here directly: |
||
field='subj.sessname', | ||
desc='VDM filename extension'); | ||
writeunwarped = traits.Bool(False, usedefault=True, | ||
field='subj.writeunwarped', | ||
desc='write unwarped EPI'); | ||
anat = traits.File(copyfile=False, exists=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File from base |
||
field='subj.anat', | ||
desc='anatomical image for comparison'); | ||
matchanat = traits.Bool(True, usedefault=True, | ||
field='subj.matchanat', | ||
desc='match anatomical image to EPI'); | ||
|
||
|
||
class FieldMapOutputSpec(TraitedSpec): | ||
vdm = File(exists=True, desc='voxel difference map') | ||
|
||
|
||
class FieldMap(SPMCommand): | ||
"""Use spm to calculate fieldmap vdm. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what "vdm" means? (should be in the documentation, so please expand the acronym in this docstring) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. VDM stands for Voxel Displacement Map. Docstring will be modified in the next commit. |
||
|
||
http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=19 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please fix link, this leads to page 16 (Slice timing correction) |
||
|
||
To do | ||
----- | ||
Deal with real/imag magnitude images and with the two phase files case. | ||
|
||
Examples | ||
-------- | ||
>>> from nipype.interfaces.spm import FieldMap | ||
>>> fm = FieldMap() | ||
>>> fm.inputs.phase = 'phasediff.nii' | ||
>>> fm.inputs.magnitude = 'magnitude1.nii' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
>>> fm.inputs.et = [5.19, 7.65] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a tuple, not a list. |
||
>>> fm.inputs.blipdir = 1 | ||
>>> fm.inputs.tert = 15.6 | ||
>>> fm.inputs.epi = 'bold.nii' | ||
>>> fm.run() # doctest: +SKIP | ||
|
||
""" | ||
|
||
input_spec = FieldMapInputSpec | ||
output_spec = FieldMapOutputSpec | ||
_jobtype = 'tools' | ||
_jobname = 'fieldmap' | ||
|
||
def _format_arg(self, opt, spec, val): | ||
"""Convert input to appropriate format for spm | ||
""" | ||
if opt == 'phase' or opt == 'magnitude' or opt == 'anat': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return scans_for_fname(filename_to_list(val)) | ||
if opt == 'epi' or opt == 'magnitude': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
However, I don't see any difference with including these two options with the previous condition, finally, it always falls into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I must have done this for a reason that I can't remember anymore... I will merge these |
||
return scans_for_fname(filename_to_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() | ||
jobtype = self.inputs.jobtype | ||
return [{'%s' % (jobtype): einputs[0]}] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the advantage of this over the following? return [{self.inputs.jobtype: einputs[0]}] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like better your style, but I originally used the syntax of another wrapper (typically |
||
|
||
def _list_outputs(self): | ||
outputs = self._outputs().get() | ||
jobtype = self.inputs.jobtype | ||
if jobtype == "calculatevdm": | ||
outputs['vdm'] = [] | ||
for phase in filename_to_list(self.inputs.phase): | ||
outputs['vdm'].append(fname_presuffix(phase, prefix='vdm5_sc')) | ||
outputs['vdm'] = list_to_filename(outputs['vdm']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused by this. if self.inputs.jobtype == 'calculatevdm':
outputs['vdm'] = fname_presuffix(self.inputs.phase, prefix='vdm5_sc') Or am I missing something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're not, I just adapted another more complicated wrapper's piece of code without thinking about making it clearer. Thanks for noticing! |
||
|
||
return outputs | ||
|
||
|
||
class SliceTimingInputSpec(SPMCommandInputSpec): | ||
in_files = InputMultiPath(traits.Either(traits.List(File(exists=True)), | ||
File(exists=True)), field='scans', | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We often use the suffix
_file
to indicate inputs that should be files. So consider making thisphase_file
. Similarly below withmagnitude_file
.Not a big deal, if you think that would be disorienting to SPM users.