Skip to content

Commit e2591e9

Browse files
committed
Merge remote-tracking branch 'nipy/master'
2 parents 39348e8 + e89436c commit e2591e9

26 files changed

+693
-49
lines changed

CHANGES

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
Upcoming Release
22
=====================
33

4+
* ENH: AFNI motion parameter support for FrameWiseDisplacement (https://github.com/nipy/nipype/pull/1840)
5+
* ENH: Add ANTs KellyKapowski interface (https://github.com/nipy/nipype/pull/1845)
6+
* FIX: AFNI interface bug setting OMP_NUM_THREADS to 1 (https://github.com/nipy/nipype/pull/1728)
7+
* FIX: Select Eddy run command at runtime (https://github.com/nipy/nipype/pull/1871)
8+
* FIX: Increase FLIRT's flexibility with apply_xfm (https://github.com/nipy/nipype/pull/1875)
9+
* DOC: Update FSL preprocess docstrings (https://github.com/nipy/nipype/pull/1881)
10+
* ENH: Support GIFTI outputs in SampleToSurface (https://github.com/nipy/nipype/pull/1886)
11+
* FIX: Configparser differences between PY2 and PY3 (https://github.com/nipy/nipype/pull/1890)
12+
* ENH: Add mris_expand interface (https://github.com/nipy/nipype/pull/1893)
13+
* FIX: Split over-eager globs in FreeSurferSource (https://github.com/nipy/nipype/pull/1894)
14+
* FIX: Store undefined by default so that xor checks don't trip (https://github.com/nipy/nipype/pull/1903)
15+
* FIX: Gantt chart generator PY3 compatibility (https://github.com/nipy/nipype/pull/1907)
16+
* FIX: Add DOF and --fsl-dof options to BBRegister (https://github.com/nipy/nipype/pull/1917)
17+
* ENH: Auto-derive input_names in Function (https://github.com/nipy/nipype/pull/1918)
18+
* FIX: Minor fixes for NonSteadyStateDetector (https://github.com/nipy/nipype/pull/1926)
19+
* DOC: Add duecredit references for AFNI and FSL (https://github.com/nipy/nipype/pull/1930)
20+
* ENH: Added zenodo (https://zenodo.org/) file (https://github.com/nipy/nipype/pull/1924)
21+
* ENH: Disable symlinks on CIFS filesystems (https://github.com/nipy/nipype/pull/1941)
422
* ENH: Sphinx extension to plot workflows (https://github.com/nipy/nipype/pull/1896)
523
* ENH: Added non-steady state detector for EPI data (https://github.com/nipy/nipype/pull/1839)
624
* ENH: Enable new BBRegister init options for FSv6+ (https://github.com/nipy/nipype/pull/1811)

nipype/interfaces/ants/registration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ class RegistrationInputSpec(ANTSCommandInputSpec):
378378
winsorize_lower_quantile = traits.Range(
379379
low=0.0, high=1.0, value=0.0, argstr='%s', usedefault=True, desc="The Lower quantile to clip image ranges")
380380

381+
verbose = traits.Bool(argstr='-v', default=False)
382+
381383

382384
class RegistrationOutputSpec(TraitedSpec):
383385
forward_transforms = traits.List(

nipype/interfaces/dcm2nii.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,10 @@ def _parse_stdout(self, stdout):
309309
else:
310310
pass
311311
if out_file:
312-
files.append(out_file + ".nii.gz")
312+
if self.inputs.compress == 'n':
313+
files.append(out_file + ".nii")
314+
else:
315+
files.append(out_file + ".nii.gz")
313316
if self.inputs.bids_format:
314317
bids.append(out_file + ".json")
315318
continue

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: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@
1717
from __future__ import print_function, division, unicode_literals, absolute_import
1818
from builtins import open, object, str
1919

20-
2120
import os
2221

22+
from ... import LooseVersion
2323
from ...utils.filemanip import fname_presuffix
2424
from ..base import (CommandLine, Directory,
2525
CommandLineInputSpec, isdefined,
2626
traits, TraitedSpec, File)
2727

2828
__docformat__ = 'restructuredtext'
2929

30+
3031
class Info(object):
3132
""" Freesurfer subject directory and version information.
3233
@@ -65,6 +66,41 @@ def version():
6566
fid.close()
6667
return version
6768

69+
@classmethod
70+
def looseversion(cls):
71+
""" Return a comparable version object
72+
73+
If no version found, use LooseVersion('0.0.0')
74+
"""
75+
ver = cls.version()
76+
if ver is None:
77+
return LooseVersion('0.0.0')
78+
79+
vinfo = ver.rstrip().split('-')
80+
try:
81+
int(vinfo[-1], 16)
82+
except ValueError:
83+
githash = ''
84+
else:
85+
githash = '.' + vinfo[-1]
86+
87+
# As of FreeSurfer v6.0.0, the final component is a githash
88+
if githash:
89+
if vinfo[3] == 'dev':
90+
# This will need updating when v6.0.1 comes out
91+
vstr = '6.0.0-dev' + githash
92+
elif vinfo[5][0] == 'v':
93+
vstr = vinfo[5][1:]
94+
else:
95+
raise RuntimeError('Unknown version string: ' + ver)
96+
# Retain pre-6.0.0 heuristics
97+
elif 'dev' in ver:
98+
vstr = vinfo[-1] + '-dev'
99+
else:
100+
vstr = ver.rstrip().split('-v')[-1]
101+
102+
return LooseVersion(vstr)
103+
68104
@classmethod
69105
def subjectsdir(cls):
70106
"""Check the global SUBJECTS_DIR
@@ -154,16 +190,49 @@ def _gen_fname(self, basename, fname=None, cwd=None, suffix='_fs',
154190

155191
@property
156192
def version(self):
157-
ver = Info.version()
158-
if ver:
159-
if 'dev' in ver:
160-
return ver.rstrip().split('-')[-1] + '.dev'
161-
else:
162-
return ver.rstrip().split('-v')[-1]
193+
ver = Info.looseversion()
194+
if ver > LooseVersion("0.0.0"):
195+
return ver.vstring
196+
197+
198+
class FSSurfaceCommand(FSCommand):
199+
"""Support for FreeSurfer surface-related functions.
200+
For some functions, if the output file is not specified starting with
201+
'lh.' or 'rh.', FreeSurfer prepends the prefix from the input file to the
202+
output filename. Output out_file must be adjusted to accommodate this.
203+
By including the full path in the filename, we can also avoid this behavior.
204+
"""
205+
def _get_filecopy_info(self):
206+
self._normalize_filenames()
207+
return super(FSSurfaceCommand, self)._get_filecopy_info()
208+
209+
def _normalize_filenames(self):
210+
"""Filename normalization routine to perform only when run in Node
211+
context
212+
"""
213+
pass
214+
215+
@staticmethod
216+
def _associated_file(in_file, out_name):
217+
"""Based on MRIsBuildFileName in freesurfer/utils/mrisurf.c
218+
219+
Use in_file prefix to indicate hemisphere for out_name, rather than
220+
inspecting the surface data structure.
221+
Also, output to in_file's directory if path information not provided
222+
for out_name.
223+
"""
224+
path, base = os.path.split(out_name)
225+
if path == '':
226+
path, in_file = os.path.split(in_file)
227+
hemis = ('lh.', 'rh.')
228+
if in_file[:3] in hemis and base[:3] not in hemis:
229+
base = in_file[:3] + base
230+
return os.path.abspath(os.path.join(path, base))
163231

164232

165233
class FSScriptCommand(FSCommand):
166-
""" Support for Freesurfer script commands with log inputs.terminal_output """
234+
""" Support for Freesurfer script commands with log inputs.terminal_output
235+
"""
167236
_terminal_output = 'file'
168237
_always_run = False
169238

nipype/interfaces/freesurfer/preprocess.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,9 @@
3636
__docformat__ = 'restructuredtext'
3737
iflogger = logging.getLogger('interface')
3838

39-
FSVersion = "0"
40-
_ver = Info.version()
41-
if _ver:
42-
if 'dev' in _ver:
43-
FSVersion = _ver.rstrip().split('-')[-1] + '.dev'
44-
else:
45-
FSVersion = _ver.rstrip().split('-v')[-1]
39+
# Keeping this to avoid breaking external programs that depend on it, but
40+
# this should not be used internally
41+
FSVersion = Info.looseversion()
4642

4743

4844
class ParseDICOMDirInputSpec(FSTraitedSpec):
@@ -724,7 +720,7 @@ class ReconAll(CommandLine):
724720
'mri/brainmask.auto.mgz',
725721
'mri/brainmask.mgz'], []),
726722
]
727-
if LooseVersion(FSVersion) < LooseVersion("6.0.0"):
723+
if Info.looseversion() < LooseVersion("6.0.0"):
728724
_autorecon2_steps = [
729725
('gcareg', ['mri/transforms/talairach.lta'], []),
730726
('canorm', ['mri/norm.mgz'], []),
@@ -1072,7 +1068,7 @@ class BBRegister(FSCommand):
10721068
"""
10731069

10741070
_cmd = 'bbregister'
1075-
if FSVersion and LooseVersion(FSVersion) < LooseVersion("6.0.0"):
1071+
if LooseVersion('0.0.0') < Info.looseversion() < LooseVersion("6.0.0"):
10761072
input_spec = BBRegisterInputSpec
10771073
else:
10781074
input_spec = BBRegisterInputSpec6
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/tests/test_preprocess.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from nipype.testing.fixtures import create_files_in_directory
88

99
from nipype.interfaces import freesurfer
10-
from nipype.interfaces.freesurfer.preprocess import FSVersion
10+
from nipype.interfaces.freesurfer import Info
1111
from nipype import LooseVersion
1212

1313

@@ -138,7 +138,7 @@ def test_bbregister(create_files_in_directory):
138138
bbr.inputs.contrast_type = 't2'
139139

140140
# Check that 'init' is mandatory in FS < 6, but not in 6+
141-
if LooseVersion(FSVersion) < LooseVersion("6.0.0"):
141+
if Info.looseversion() < LooseVersion("6.0.0"):
142142
with pytest.raises(ValueError):
143143
bbr.cmdline
144144
else:

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/interfaces/fsl/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
TractSkeleton, MakeDyadicVectors, BEDPOSTX5, XFibres5)
3030
from .maths import (ChangeDataType, Threshold, MeanImage, ApplyMask,
3131
IsotropicSmooth, TemporalFilter, DilateImage, ErodeImage,
32-
SpatialFilter, UnaryMaths, BinaryMaths, MultiImageMaths)
32+
SpatialFilter, UnaryMaths, BinaryMaths, MultiImageMaths,
33+
MaxnImage, MinImage, MedianImage, PercentileImage,
34+
AR1Image)
3335

3436
from .possum import B0Calc

0 commit comments

Comments
 (0)