Skip to content

Commit 378e295

Browse files
authored
Merge pull request #1948 from tsalo/mriscombine
[ENH] Add MRIsCombine to FreeSurfer utils.
2 parents cf2d093 + 7c3125d commit 378e295

File tree

6 files changed

+176
-3
lines changed

6 files changed

+176
-3
lines changed

nipype/interfaces/freesurfer/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
MRIFill, MRIsInflate, Sphere, FixTopology, EulerNumber,
2222
RemoveIntersection, MakeSurfaces, Curvature, CurvatureStats,
2323
Jacobian, MRIsCalc, VolumeMask, ParcellationStats, Contrast,
24-
RelabelHypointensities, Aparc2Aseg, Apas2Aseg, MRIsExpand)
24+
RelabelHypointensities, Aparc2Aseg, Apas2Aseg, MRIsExpand, MRIsCombine)
2525
from .longitudinal import (RobustTemplate, FuseSegmentations)
2626
from .registration import (MPRtoMNI305, RegisterAVItoTalairach, EMRegister, Register,
2727
Paint)

nipype/interfaces/freesurfer/base.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,44 @@ def version(self):
162162
return ver.rstrip().split('-v')[-1]
163163

164164

165+
class FSSurfaceCommand(FSCommand):
166+
"""Support for FreeSurfer surface-related functions.
167+
For some functions, if the output file is not specified starting with
168+
'lh.' or 'rh.', FreeSurfer prepends the prefix from the input file to the
169+
output filename. Output out_file must be adjusted to accommodate this.
170+
By including the full path in the filename, we can also avoid this behavior.
171+
"""
172+
def _get_filecopy_info(self):
173+
self._normalize_filenames()
174+
return super(FSSurfaceCommand, self)._get_filecopy_info()
175+
176+
def _normalize_filenames(self):
177+
"""Filename normalization routine to perform only when run in Node
178+
context
179+
"""
180+
pass
181+
182+
@staticmethod
183+
def _associated_file(in_file, out_name):
184+
"""Based on MRIsBuildFileName in freesurfer/utils/mrisurf.c
185+
186+
Use in_file prefix to indicate hemisphere for out_name, rather than
187+
inspecting the surface data structure.
188+
Also, output to in_file's directory if path information not provided
189+
for out_name.
190+
"""
191+
path, base = os.path.split(out_name)
192+
if path == '':
193+
path, in_file = os.path.split(in_file)
194+
hemis = ('lh.', 'rh.')
195+
if in_file[:3] in hemis and base[:3] not in hemis:
196+
base = in_file[:3] + base
197+
return os.path.abspath(os.path.join(path, base))
198+
199+
165200
class FSScriptCommand(FSCommand):
166-
""" Support for Freesurfer script commands with log inputs.terminal_output """
201+
""" Support for Freesurfer script commands with log inputs.terminal_output
202+
"""
167203
_terminal_output = 'file'
168204
_always_run = False
169205

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..base import FSSurfaceCommand
4+
5+
6+
def test_FSSurfaceCommand_inputs():
7+
input_map = dict(args=dict(argstr='%s',
8+
),
9+
environ=dict(nohash=True,
10+
usedefault=True,
11+
),
12+
ignore_exception=dict(nohash=True,
13+
usedefault=True,
14+
),
15+
subjects_dir=dict(),
16+
terminal_output=dict(nohash=True,
17+
),
18+
)
19+
inputs = FSSurfaceCommand.input_spec()
20+
21+
for key, metadata in list(input_map.items()):
22+
for metakey, value in list(metadata.items()):
23+
assert getattr(inputs.traits()[key], metakey) == value
24+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..utils import MRIsCombine
4+
5+
6+
def test_MRIsCombine_inputs():
7+
input_map = dict(args=dict(argstr='%s',
8+
),
9+
environ=dict(nohash=True,
10+
usedefault=True,
11+
),
12+
ignore_exception=dict(nohash=True,
13+
usedefault=True,
14+
),
15+
in_files=dict(argstr='--combinesurfs %s',
16+
mandatory=True,
17+
position=1,
18+
),
19+
out_file=dict(argstr='%s',
20+
genfile=True,
21+
mandatory=True,
22+
position=-1,
23+
),
24+
subjects_dir=dict(),
25+
terminal_output=dict(nohash=True,
26+
),
27+
)
28+
inputs = MRIsCombine.input_spec()
29+
30+
for key, metadata in list(input_map.items()):
31+
for metakey, value in list(metadata.items()):
32+
assert getattr(inputs.traits()[key], metakey) == value
33+
34+
35+
def test_MRIsCombine_outputs():
36+
output_map = dict(out_file=dict(),
37+
)
38+
outputs = MRIsCombine.output_spec()
39+
40+
for key, metadata in list(output_map.items()):
41+
for metakey, value in list(metadata.items()):
42+
assert getattr(outputs.traits()[key], metakey) == value

nipype/interfaces/freesurfer/utils.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from ...utils.filemanip import fname_presuffix, split_filename
2222
from ..base import (TraitedSpec, File, traits, OutputMultiPath, isdefined,
2323
CommandLine, CommandLineInputSpec)
24-
from .base import (FSCommand, FSTraitedSpec,
24+
from .base import (FSCommand, FSTraitedSpec, FSSurfaceCommand,
2525
FSScriptCommand, FSScriptOutputSpec,
2626
FSTraitedSpecOpenMP, FSCommandOpenMP)
2727
__docformat__ = 'restructuredtext'
@@ -954,6 +954,77 @@ def _gen_outfilename(self):
954954
return name + ext + "_converted." + self.inputs.out_datatype
955955

956956

957+
class MRIsCombineInputSpec(FSTraitedSpec):
958+
"""
959+
Uses Freesurfer's mris_convert to combine two surface files into one.
960+
"""
961+
in_files = traits.List(File(Exists=True), maxlen=2, minlen=2,
962+
mandatory=True, position=1, argstr='--combinesurfs %s',
963+
desc='Two surfaces to be combined.')
964+
out_file = File(argstr='%s', position=-1, genfile=True,
965+
mandatory=True,
966+
desc='Output filename. Combined surfaces from in_files.')
967+
968+
969+
class MRIsCombineOutputSpec(TraitedSpec):
970+
"""
971+
Uses Freesurfer's mris_convert to combine two surface files into one.
972+
"""
973+
out_file = File(exists=True, desc='Output filename. Combined surfaces from '
974+
'in_files.')
975+
976+
977+
class MRIsCombine(FSSurfaceCommand):
978+
"""
979+
Uses Freesurfer's mris_convert to combine two surface files into one.
980+
981+
For complete details, see the `mris_convert Documentation.
982+
<https://surfer.nmr.mgh.harvard.edu/fswiki/mris_convert>`_
983+
984+
If given an out_file that does not begin with 'lh.' or 'rh.',
985+
mris_convert will prepend 'lh.' to the file name.
986+
To avoid this behavior, consider setting out_file = './<filename>', or
987+
leaving out_file blank.
988+
989+
Example
990+
-------
991+
992+
>>> import nipype.interfaces.freesurfer as fs
993+
>>> mris = fs.MRIsCombine()
994+
>>> mris.inputs.in_files = ['lh.pial', 'rh.pial']
995+
>>> mris.inputs.out_file = 'bh.pial'
996+
>>> mris.cmdline # doctest: +ALLOW_UNICODE
997+
'mris_convert --combinesurfs lh.pial rh.pial bh.pial'
998+
>>> mris.run() # doctest: +SKIP
999+
"""
1000+
_cmd = 'mris_convert'
1001+
input_spec = MRIsCombineInputSpec
1002+
output_spec = MRIsCombineOutputSpec
1003+
1004+
def _list_outputs(self):
1005+
outputs = self._outputs().get()
1006+
outputs['out_file'] = self._associated_file(self.inputs.in_files[0],
1007+
self.inputs.out_file)
1008+
return outputs
1009+
1010+
@staticmethod
1011+
def _associated_file(in_file, out_name):
1012+
"""Unlike the standard _associated_file, which uses the prefix from
1013+
in_file, in MRIsCombine, it uses 'lh.' as the prefix for the output
1014+
file no matter what the inputs are.
1015+
"""
1016+
path, base = os.path.split(out_name)
1017+
if path == '':
1018+
hemis = ('lh.', 'rh.')
1019+
if base[:3] not in hemis:
1020+
base = 'lh.' + base
1021+
return os.path.abspath(os.path.join(path, base))
1022+
1023+
def _normalize_filenames(self):
1024+
if isdefined(self.inputs.out_file):
1025+
self.inputs.out_file = os.path.abspath(self.inputs.out_file)
1026+
1027+
9571028
class MRITessellateInputSpec(FSTraitedSpec):
9581029
"""
9591030
Uses Freesurfer's mri_tessellate to create surfaces by tessellating a given input volume

nipype/testing/data/rh.pial

Whitespace-only changes.

0 commit comments

Comments
 (0)