Skip to content

Commit bdf62ab

Browse files
committed
Merge remote-tracking branch 'upstream/master' into fix/1795
2 parents 4291ffc + 61978ad commit bdf62ab

File tree

9 files changed

+366
-137
lines changed

9 files changed

+366
-137
lines changed

.travis.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@ before_install:
2828
export PATH=/home/travis/anaconda/bin:$PATH &&
2929
conda config --append channels conda-forge &&
3030
conda update --all -y python=$TRAVIS_PYTHON_VERSION &&
31-
conda install -y icu numpy scipy traits &&
32-
if [ "${TRAVIS_PYTHON_VERSION:0:1}" -gt "2" ]; then
33-
conda install -y boto3;
34-
fi }
31+
conda install -y nipype icu==56.1 &&
32+
# Add install of vtk and mayavi to test mesh (disabled): conda install -y vtk mayavi &&
33+
rm -r /home/travis/miniconda/lib/python${TRAVIS_PYTHON_VERSION}/site-packages/nipype*}
3534
- travis_retry bef_inst
3635
install:
37-
# Add install of vtk and mayavi to test mesh (disabled): conda install -y vtk mayavi &&
3836
- travis_retry pip install -e .[$NIPYPE_EXTRAS]
3937
script:
4038
- py.test --doctest-modules nipype

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Upcoming release 0.13
2929
* ENH: Added support for custom job submission check in SLURM (https://github.com/nipy/nipype/pull/1582)
3030
* ENH: Added ANTs interface CreateJacobianDeterminantImage; replaces deprecated JacobianDeterminant
3131
(https://github.com/nipy/nipype/pull/1654)
32+
* ENH: Update ReconAll interface for FreeSurfer v6.0.0 (https://github.com/nipy/nipype/pull/1790)
3233

3334
Release 0.12.1 (August 3, 2016)
3435
===============================

doc/devel/interface_specs.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ CommandLine
353353
be fixed it's recommended to just use ``usedefault``.
354354

355355
``sep``
356-
For List traits the string with witch elements of the list will be joined.
356+
For List traits the string with which elements of the list will be joined.
357357

358358
``name_source``
359359
Indicates the list of input fields from which the value of the current File

nipype/interfaces/freesurfer/preprocess.py

Lines changed: 192 additions & 97 deletions
Large diffs are not rendered by default.

nipype/interfaces/fsl/epi.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,20 @@ class EddyOutputSpec(TraitedSpec):
454454
out_parameter = File(exists=True,
455455
desc=('text file with parameters definining the '
456456
'field and movement for each scan'))
457+
out_rotated_bvecs = File(exists=True,
458+
desc=('File containing rotated b-values for all volumes'))
459+
out_movement_rms = File(exists=True,
460+
desc=('Summary of the "total movement" in each volume'))
461+
out_restricted_movement_rms = File(exists=True,
462+
desc=('Summary of the "total movement" in each volume '
463+
'disregarding translation in the PE direction'))
464+
out_shell_alignment_parameters = File(exists=True,
465+
desc=('File containing rigid body movement parameters '
466+
'between the different shells as estimated by a '
467+
'post-hoc mutual information based registration'))
468+
out_outlier_report = File(exists=True,
469+
desc=('Text-file with a plain language report '
470+
'on what outlier slices eddy has found'))
457471

458472

459473
class Eddy(FSLCommand):
@@ -526,6 +540,30 @@ def _list_outputs(self):
526540
'%s.nii.gz' % self.inputs.out_base)
527541
outputs['out_parameter'] = os.path.abspath(
528542
'%s.eddy_parameters' % self.inputs.out_base)
543+
544+
# File generation might depend on the version of EDDY
545+
out_rotated_bvecs = os.path.abspath(
546+
'%s.eddy_rotated_bvecs' % self.inputs.out_base)
547+
out_movement_rms = os.path.abspath(
548+
'%s.eddy_movement_rms' % self.inputs.out_base)
549+
out_restricted_movement_rms = os.path.abspath(
550+
'%s.eddy_restricted_movement_rms' % self.inputs.out_base)
551+
out_shell_alignment_parameters = os.path.abspath(
552+
'%s.eddy_post_eddy_shell_alignment_parameters' % self.inputs.out_base)
553+
out_outlier_report = os.path.abspath(
554+
'%s.eddy_outlier_report' % self.inputs.out_base)
555+
556+
if os.path.exists(out_rotated_bvecs):
557+
outputs['out_rotated_bvecs'] = out_rotated_bvecs
558+
if os.path.exists(out_movement_rms):
559+
outputs['out_movement_rms'] = out_movement_rms
560+
if os.path.exists(out_restricted_movement_rms):
561+
outputs['out_restricted_movement_rms'] = out_restricted_movement_rms
562+
if os.path.exists(out_shell_alignment_parameters):
563+
outputs['out_shell_alignment_parameters'] = out_shell_alignment_parameters
564+
if os.path.exists(out_outlier_report):
565+
outputs['out_outlier_report'] = out_outlier_report
566+
529567
return outputs
530568

531569

nipype/interfaces/fsl/preprocess.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from ..base import (TraitedSpec, File, InputMultiPath,
2626
OutputMultiPath, Undefined, traits,
2727
isdefined)
28-
from .base import FSLCommand, FSLCommandInputSpec
28+
from .base import FSLCommand, FSLCommandInputSpec, Info
2929

3030

3131
class BETInputSpec(FSLCommandInputSpec):
@@ -750,10 +750,12 @@ class FNIRTInputSpec(FSLCommandInputSpec):
750750
desc='name of file containing affine transform')
751751
inwarp_file = File(exists=True, argstr='--inwarp=%s',
752752
desc='name of file containing initial non-linear warps')
753-
in_intensitymap_file = File(exists=True, argstr='--intin=%s',
754-
desc=('name of file/files containing initial '
755-
'intensity maping usually generated by '
756-
'previous fnirt run'))
753+
in_intensitymap_file = traits.List(File(exists=True), argstr='--intin=%s',
754+
copyfiles=False, minlen=1, maxlen=2,
755+
desc=('name of file/files containing '
756+
'initial intensity mapping '
757+
'usually generated by previous '
758+
'fnirt run'))
757759
fieldcoeff_file = traits.Either(
758760
traits.Bool, File, argstr='--cout=%s',
759761
desc='name of output file with field coefficients or true')
@@ -907,8 +909,9 @@ class FNIRTOutputSpec(TraitedSpec):
907909
field_file = File(desc='file with warp field')
908910
jacobian_file = File(desc='file containing Jacobian of the field')
909911
modulatedref_file = File(desc='file containing intensity modulated --ref')
910-
out_intensitymap_file = File(
911-
desc='file containing info pertaining to intensity mapping')
912+
out_intensitymap_file = traits.List(
913+
File, minlen=2, maxlen=2,
914+
desc='files containing info pertaining to intensity mapping')
912915
log_file = File(desc='Name of log-file')
913916

914917

@@ -975,9 +978,23 @@ def _list_outputs(self):
975978
change_ext=change_ext)
976979
else:
977980
outputs[key] = os.path.abspath(inval)
981+
982+
if key == 'out_intensitymap_file' and isdefined(outputs[key]):
983+
basename = FNIRT.intensitymap_file_basename(outputs[key])
984+
outputs[key] = [
985+
outputs[key],
986+
'%s.txt' % basename,
987+
]
978988
return outputs
979989

980990
def _format_arg(self, name, spec, value):
991+
if name in ('in_intensitymap_file', 'out_intensitymap_file'):
992+
if name == 'out_intensitymap_file':
993+
value = self._list_outputs()[name]
994+
value = [FNIRT.intensitymap_file_basename(v) for v in value]
995+
assert len(set(value)) == 1, (
996+
'Found different basenames for {}: {}'.format(name, value))
997+
return spec.argstr % value[0]
981998
if name in list(self.filemap.keys()):
982999
return spec.argstr % self._list_outputs()[name]
9831000
return super(FNIRT, self)._format_arg(name, spec, value)
@@ -1005,6 +1022,17 @@ def write_config(self, configfile):
10051022
fid.write('%s\n' % (item))
10061023
fid.close()
10071024

1025+
@classmethod
1026+
def intensitymap_file_basename(cls, f):
1027+
"""Removes valid intensitymap extensions from `f`, returning a basename
1028+
that can refer to both intensitymap files.
1029+
"""
1030+
for ext in list(Info.ftypes.values()) + ['.txt']:
1031+
if f.endswith(ext):
1032+
return f[:-len(ext)]
1033+
# TODO consider warning for this case
1034+
return f
1035+
10081036

10091037
class ApplyWarpInputSpec(FSLCommandInputSpec):
10101038
in_file = File(exists=True, argstr='--in=%s',

nipype/interfaces/fsl/tests/test_preprocess.py

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ def test_mcflirt(setup_flirt):
353353
def test_fnirt(setup_flirt):
354354

355355
tmpdir, infile, reffile = setup_flirt
356+
os.chdir(tmpdir)
356357
fnirt = fsl.FNIRT()
357358
assert fnirt.cmd == 'fnirt'
358359

@@ -404,64 +405,81 @@ def test_fnirt(setup_flirt):
404405
fnirt.run()
405406
fnirt.inputs.in_file = infile
406407
fnirt.inputs.ref_file = reffile
408+
intmap_basename = '%s_intmap' % fsl.FNIRT.intensitymap_file_basename(infile)
409+
intmap_image = fsl_name(fnirt, intmap_basename)
410+
intmap_txt = '%s.txt' % intmap_basename
411+
# doing this to create the file to pass tests for file existence
412+
with open(intmap_image, 'w'):
413+
pass
414+
with open(intmap_txt, 'w'):
415+
pass
407416

408417
# test files
409-
opt_map = {
410-
'affine_file': ('--aff='),
411-
'inwarp_file': ('--inwarp='),
412-
'in_intensitymap_file': ('--intin='),
413-
'config_file': ('--config='),
414-
'refmask_file': ('--refmask='),
415-
'inmask_file': ('--inmask='),
416-
'field_file': ('--fout='),
417-
'jacobian_file': ('--jout='),
418-
'modulatedref_file': ('--refout='),
419-
'out_intensitymap_file': ('--intout='),
420-
'log_file': ('--logout=')}
421-
422-
for name, settings in list(opt_map.items()):
418+
opt_map = [
419+
('affine_file', '--aff=%s' % infile, infile),
420+
('inwarp_file', '--inwarp=%s' % infile, infile),
421+
('in_intensitymap_file', '--intin=%s' % intmap_basename, [intmap_image]),
422+
('in_intensitymap_file',
423+
'--intin=%s' % intmap_basename,
424+
[intmap_image, intmap_txt]),
425+
('config_file', '--config=%s' % infile, infile),
426+
('refmask_file', '--refmask=%s' % infile, infile),
427+
('inmask_file', '--inmask=%s' % infile, infile),
428+
('field_file', '--fout=%s' % infile, infile),
429+
('jacobian_file', '--jout=%s' % infile, infile),
430+
('modulatedref_file', '--refout=%s' % infile, infile),
431+
('out_intensitymap_file',
432+
'--intout=%s' % intmap_basename, True),
433+
('out_intensitymap_file', '--intout=%s' % intmap_basename, intmap_image),
434+
('fieldcoeff_file', '--cout=%s' % infile, infile),
435+
('log_file', '--logout=%s' % infile, infile)]
436+
437+
for (name, settings, arg) in opt_map:
423438
fnirt = fsl.FNIRT(in_file=infile,
424439
ref_file=reffile,
425-
**{name: infile})
440+
**{name: arg})
426441

427-
if name in ('config_file', 'affine_file', 'field_file'):
428-
cmd = 'fnirt %s%s --in=%s '\
442+
if name in ('config_file', 'affine_file', 'field_file', 'fieldcoeff_file'):
443+
cmd = 'fnirt %s --in=%s '\
429444
'--logout=%s '\
430-
'--ref=%s --iout=%s' % (settings, infile, infile, log,
445+
'--ref=%s --iout=%s' % (settings, infile, log,
431446
reffile, iout)
432447
elif name in ('refmask_file'):
433448
cmd = 'fnirt --in=%s '\
434449
'--logout=%s --ref=%s '\
435-
'%s%s '\
450+
'%s '\
436451
'--iout=%s' % (infile, log,
437452
reffile,
438-
settings, infile,
453+
settings,
439454
iout)
440455
elif name in ('in_intensitymap_file', 'inwarp_file', 'inmask_file', 'jacobian_file'):
441456
cmd = 'fnirt --in=%s '\
442-
'%s%s '\
457+
'%s '\
443458
'--logout=%s --ref=%s '\
444459
'--iout=%s' % (infile,
445-
settings, infile,
460+
settings,
446461
log,
447462
reffile,
448463
iout)
449464
elif name in ('log_file'):
450465
cmd = 'fnirt --in=%s '\
451-
'%s%s --ref=%s '\
466+
'%s --ref=%s '\
452467
'--iout=%s' % (infile,
453-
settings, infile,
468+
settings,
454469
reffile,
455470
iout)
456471
else:
457472
cmd = 'fnirt --in=%s '\
458-
'--logout=%s %s%s '\
473+
'--logout=%s %s '\
459474
'--ref=%s --iout=%s' % (infile, log,
460-
settings, infile,
475+
settings,
461476
reffile, iout)
462477

463478
assert fnirt.cmdline == cmd
464479

480+
if name == 'out_intensitymap_file':
481+
assert fnirt._list_outputs()['out_intensitymap_file'] == [
482+
intmap_image, intmap_txt]
465483

466484
@pytest.mark.skipif(no_fsl(), reason="fsl is not installed")
467485
def test_applywarp(setup_flirt):

nipype/utils/filemanip.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,19 @@ def list_to_filename(filelist):
447447
else:
448448
return filelist[0]
449449

450+
451+
def check_depends(targets, dependencies):
452+
"""Return true if all targets exist and are newer than all dependencies.
453+
454+
An OSError will be raised if there are missing dependencies.
455+
"""
456+
tgts = filename_to_list(targets)
457+
deps = filename_to_list(dependencies)
458+
return all(map(os.path.exists, tgts)) and \
459+
min(map(os.path.getmtime, tgts)) > \
460+
max(list(map(os.path.getmtime, deps)) + [0])
461+
462+
450463
def save_json(filename, data):
451464
"""Save data to a json file
452465

nipype/utils/tests/test_filemanip.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from builtins import open
66

77
import os
8-
from tempfile import mkstemp
8+
import time
9+
from tempfile import mkstemp, mkdtemp
10+
import shutil
911
import warnings
1012

1113
import pytest
@@ -15,6 +17,7 @@
1517
hash_rename, check_forhash,
1618
copyfile, copyfiles,
1719
filename_to_list, list_to_filename,
20+
check_depends,
1821
split_filename, get_related_files)
1922

2023
import numpy as np
@@ -271,6 +274,41 @@ def test_list_to_filename(list, expected):
271274
assert x == expected
272275

273276

277+
def test_check_depends():
278+
def touch(fname):
279+
with open(fname, 'a'):
280+
os.utime(fname, None)
281+
282+
tmpdir = mkdtemp()
283+
284+
dependencies = [os.path.join(tmpdir, str(i)) for i in range(3)]
285+
targets = [os.path.join(tmpdir, str(i)) for i in range(3, 6)]
286+
287+
# Targets newer than dependencies
288+
for dep in dependencies:
289+
touch(dep)
290+
time.sleep(1)
291+
for tgt in targets:
292+
touch(tgt)
293+
assert check_depends(targets, dependencies)
294+
295+
# Targets older than newest dependency
296+
time.sleep(1)
297+
touch(dependencies[0])
298+
assert not check_depends(targets, dependencies)
299+
300+
# Missing dependency
301+
os.unlink(dependencies[0])
302+
try:
303+
check_depends(targets, dependencies)
304+
except OSError as e:
305+
pass
306+
else:
307+
assert False, "Should raise OSError on missing dependency"
308+
309+
shutil.rmtree(tmpdir)
310+
311+
274312
def test_json():
275313
# Simple roundtrip test of json files, just a sanity check.
276314
adict = dict(a='one', c='three', b='two')

0 commit comments

Comments
 (0)