Skip to content

Commit b823356

Browse files
committed
Merge branch 'master' of github.com:nipy/nipype into enh/qwarp
2 parents 5dee2f0 + 048413e commit b823356

29 files changed

+3123
-97
lines changed

nipype/algorithms/confounds.py

Lines changed: 172 additions & 64 deletions
Large diffs are not rendered by default.

nipype/algorithms/tests/test_compcor.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import pytest
99
from ...testing import utils
1010
from ..confounds import CompCor, TCompCor, ACompCor
11+
from ...interfaces.base import Undefined
1112

1213

1314
class TestCompCor():
1415
''' Note: Tests currently do a poor job of testing functionality '''
1516

1617
filenames = {'functionalnii': 'compcorfunc.nii',
1718
'masknii': 'compcormask.nii',
19+
'masknii2': 'compcormask2.nii',
1820
'components_file': None}
1921

2022
@pytest.fixture(autouse=True)
@@ -28,7 +30,15 @@ def setup_class(self, tmpdir):
2830
mask = np.ones(self.fake_data.shape[:3])
2931
mask[0, 0, 0] = 0
3032
mask[0, 0, 1] = 0
31-
self.mask_file = utils.save_toy_nii(mask, self.filenames['masknii'])
33+
mask1 = utils.save_toy_nii(mask, self.filenames['masknii'])
34+
35+
other_mask = np.ones(self.fake_data.shape[:3])
36+
other_mask[0, 1, 0] = 0
37+
other_mask[1, 1, 0] = 0
38+
mask2 = utils.save_toy_nii(other_mask, self.filenames['masknii2'])
39+
40+
self.mask_files = [mask1, mask2]
41+
3242

3343
def test_compcor(self):
3444
expected_components = [['-0.1989607212', '-0.5753813646'],
@@ -37,16 +47,19 @@ def test_compcor(self):
3747
['0.4206466244', '-0.3361270124'],
3848
['-0.1246655485', '-0.1235705610']]
3949

40-
self.run_cc(CompCor(realigned_file=self.realigned_file, mask_file=self.mask_file),
41-
expected_components)
50+
self.run_cc(CompCor(realigned_file=self.realigned_file,
51+
mask_files=self.mask_files),
52+
expected_components)
4253

43-
self.run_cc(ACompCor(realigned_file=self.realigned_file, mask_file=self.mask_file,
54+
self.run_cc(ACompCor(realigned_file=self.realigned_file,
55+
mask_files=self.mask_files,
4456
components_file='acc_components_file'),
45-
expected_components, 'aCompCor')
57+
expected_components, 'aCompCor')
4658

4759

4860
def test_tcompcor(self):
49-
ccinterface = TCompCor(realigned_file=self.realigned_file, percentile_threshold=0.75)
61+
ccinterface = TCompCor(realigned_file=self.realigned_file,
62+
percentile_threshold=0.75)
5063
self.run_cc(ccinterface, [['-0.1114536190', '-0.4632908609'],
5164
['0.4566907310', '0.6983205193'],
5265
['-0.7132557407', '0.1340170559'],
@@ -62,27 +75,29 @@ def test_tcompcor_no_percentile(self):
6275
assert num_nonmasked_voxels == 1
6376

6477
def test_compcor_no_regress_poly(self):
65-
self.run_cc(CompCor(realigned_file=self.realigned_file, mask_file=self.mask_file,
66-
use_regress_poly=False), [['0.4451946442', '-0.7683311482'],
67-
['-0.4285129505', '-0.0926034137'],
68-
['0.5721540256', '0.5608764842'],
69-
['-0.5367548139', '0.0059943226'],
70-
['-0.0520809054', '0.2940637551']])
78+
self.run_cc(CompCor(realigned_file=self.realigned_file,
79+
mask_files=self.mask_files,
80+
use_regress_poly=False), [['0.4451946442', '-0.7683311482'],
81+
['-0.4285129505', '-0.0926034137'],
82+
['0.5721540256', '0.5608764842'],
83+
['-0.5367548139', '0.0059943226'],
84+
['-0.0520809054', '0.2940637551']])
7185

7286
def test_tcompcor_asymmetric_dim(self):
7387
asymmetric_shape = (2, 3, 4, 5)
74-
asymmetric_data = utils.save_toy_nii(np.zeros(asymmetric_shape), 'asymmetric.nii')
88+
asymmetric_data = utils.save_toy_nii(np.zeros(asymmetric_shape),
89+
'asymmetric.nii')
7590

7691
TCompCor(realigned_file=asymmetric_data).run()
7792
assert nb.load('mask.nii').get_data().shape == asymmetric_shape[:3]
7893

7994
def test_compcor_bad_input_shapes(self):
80-
shape_less_than = (1, 2, 2, 5) # dim 0 is < dim 0 of self.mask_file (2)
81-
shape_more_than = (3, 3, 3, 5) # dim 0 is > dim 0 of self.mask_file (2)
95+
shape_less_than = (1, 2, 2, 5) # dim 0 is < dim 0 of self.mask_files (2)
96+
shape_more_than = (3, 3, 3, 5) # dim 0 is > dim 0 of self.mask_files (2)
8297

8398
for data_shape in (shape_less_than, shape_more_than):
8499
data_file = utils.save_toy_nii(np.zeros(data_shape), 'temp.nii')
85-
interface = CompCor(realigned_file=data_file, mask_file=self.mask_file)
100+
interface = CompCor(realigned_file=data_file, mask_files=self.mask_files)
86101
with pytest.raises(ValueError, message="Dimension mismatch"): interface.run()
87102

88103
def test_tcompcor_bad_input_dim(self):
@@ -91,6 +106,25 @@ def test_tcompcor_bad_input_dim(self):
91106
interface = TCompCor(realigned_file=data_file)
92107
with pytest.raises(ValueError, message='Not a 4D file'): interface.run()
93108

109+
def test_tcompcor_merge_intersect_masks(self):
110+
for method in ['union', 'intersect']:
111+
TCompCor(realigned_file=self.realigned_file,
112+
mask_files=self.mask_files,
113+
merge_method=method).run()
114+
if method == 'union':
115+
assert np.array_equal(nb.load('mask.nii').get_data(),
116+
([[[0,0],[0,0]],[[0,0],[1,0]]]))
117+
if method == 'intersect':
118+
assert np.array_equal(nb.load('mask.nii').get_data(),
119+
([[[0,0],[0,0]],[[0,1],[0,0]]]))
120+
121+
def test_tcompcor_index_mask(self):
122+
TCompCor(realigned_file=self.realigned_file,
123+
mask_files=self.mask_files,
124+
mask_index=1).run()
125+
assert np.array_equal(nb.load('mask.nii').get_data(),
126+
([[[0,0],[0,0]],[[0,1],[0,0]]]))
127+
94128
def run_cc(self, ccinterface, expected_components, expected_header='CompCor'):
95129
# run
96130
ccresult = ccinterface.run()

nipype/interfaces/freesurfer/preprocess.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ def _prep_expert_file(self):
10281028

10291029
contents = ''.join(lines)
10301030
if not isdefined(self.inputs.xopts) and \
1031-
self.get_expert_file() == contents:
1031+
self._get_expert_file() == contents:
10321032
return ' -xopts-use'
10331033

10341034
expert_fname = os.path.abspath('expert.opts')

nipype/interfaces/fsl/tests/test_preprocess.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ def test_mcflirt(setup_flirt):
324324
realcmd = 'mcflirt -in ' + infile + ' -out ' + outfile2
325325
assert frt.cmdline == realcmd
326326

327+
328+
@pytest.mark.skipif(no_fsl(), reason="fsl is not installed")
329+
def test_mcflirt_opt(setup_flirt):
330+
tmpdir, infile, reffile = setup_flirt
331+
_, nme = os.path.split(infile)
332+
327333
opt_map = {
328334
'cost': ('-cost mutualinfo', 'mutualinfo'),
329335
'bins': ('-bins 256', 256),
@@ -344,6 +350,9 @@ def test_mcflirt(setup_flirt):
344350

345351
for name, settings in list(opt_map.items()):
346352
fnt = fsl.MCFLIRT(in_file=infile, **{name: settings[1]})
353+
outfile = os.path.join(os.getcwd(), nme)
354+
outfile = fnt._gen_fname(outfile, suffix='_mcf')
355+
347356
instr = '-in %s' % (infile)
348357
outstr = '-out %s' % (outfile)
349358
if name in ('init', 'cost', 'dof', 'mean_vol', 'bins'):
@@ -357,10 +366,14 @@ def test_mcflirt(setup_flirt):
357366
outstr,
358367
settings[0]])
359368

369+
370+
@pytest.mark.skipif(no_fsl(), reason="fsl is not installed")
371+
def test_mcflirt_noinput():
360372
# Test error is raised when missing required args
361373
fnt = fsl.MCFLIRT()
362-
with pytest.raises(ValueError):
374+
with pytest.raises(ValueError) as excinfo:
363375
fnt.run()
376+
assert str(excinfo.value).startswith("MCFLIRT requires a value for input 'in_file'")
364377

365378
# test fnirt
366379

nipype/interfaces/mrtrix/convert.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
>>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
88
>>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
99
>>> os.chdir(datadir)
10-
1110
"""
1211
from __future__ import print_function, division, unicode_literals, absolute_import
13-
from builtins import open
12+
from io import open
1413

1514
import os.path as op
1615
import nibabel as nb
@@ -55,10 +54,11 @@ def read_mrtrix_tracks(in_file, as_generator=True):
5554

5655

5756
def read_mrtrix_header(in_file):
58-
fileobj = open(in_file, 'r')
57+
fileobj = open(in_file, 'rb')
5958
header = {}
6059
iflogger.info('Reading header data...')
6160
for line in fileobj:
61+
line = line.decode()
6262
if line == 'END\n':
6363
iflogger.info('Reached the end of the header!')
6464
break
@@ -78,7 +78,7 @@ def read_mrtrix_header(in_file):
7878
def read_mrtrix_streamlines(in_file, header, as_generator=True):
7979
offset = header['offset']
8080
stream_count = header['count']
81-
fileobj = open(in_file, 'r')
81+
fileobj = open(in_file, 'rb')
8282
fileobj.seek(offset)
8383
endianness = native_code
8484
f4dt = np.dtype(endianness + 'f4')
@@ -138,9 +138,14 @@ def track_gen(track_points):
138138
if n_streams == stream_count:
139139
iflogger.info('100% : {n} tracks read'.format(n=n_streams))
140140
raise StopIteration
141-
if n_streams % int(stream_count / 100) == 0:
142-
percent = int(float(n_streams) / float(stream_count) * 100)
143-
iflogger.info('{p}% : {n} tracks read'.format(p=percent, n=n_streams))
141+
try:
142+
if n_streams % int(stream_count / 100) == 0:
143+
percent = int(float(n_streams) / float(stream_count) * 100)
144+
iflogger.info('{p}% : {n} tracks read'.format(p=percent,
145+
n=n_streams))
146+
except ZeroDivisionError:
147+
iflogger.info('{} stream read out of {}'.format(n_streams,
148+
stream_count))
144149
track_points, nonfinite_list = points_per_track(offset)
145150
fileobj.seek(offset)
146151
streamlines = track_gen(track_points)
@@ -166,10 +171,8 @@ class MRTrix2TrackVis(BaseInterface):
166171
"""
167172
Converts MRtrix (.tck) tract files into TrackVis (.trk) format
168173
using functions from dipy
169-
170174
Example
171175
-------
172-
173176
>>> import nipype.interfaces.mrtrix as mrt
174177
>>> tck2trk = mrt.MRTrix2TrackVis()
175178
>>> tck2trk.inputs.in_file = 'dwi_CSD_tracked.tck'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
5+
"""
6+
The niftyreg module provides classes for interfacing with the `NiftyReg
7+
<http://sourceforge.net/projects/niftyreg/>`_ command line tools.
8+
9+
Top-level namespace for niftyreg.
10+
"""
11+
12+
from .base import no_niftyreg, get_custom_path
13+
from .reg import RegAladin, RegF3D
14+
from .regutils import (RegResample, RegJacobian, RegAverage, RegTools,
15+
RegTransform, RegMeasure)

nipype/interfaces/niftyreg/base.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
5+
"""
6+
The niftyreg module provides classes for interfacing with `niftyreg
7+
<http://sourceforge.net/projects/niftyreg/>`_ command line tools.
8+
9+
These are the base tools for working with niftyreg.
10+
11+
Registration tools are found in niftyreg/reg.py
12+
Every other tool is found in niftyreg/regutils.py
13+
14+
Examples
15+
--------
16+
See the docstrings of the individual classes for examples.
17+
18+
"""
19+
20+
from __future__ import (print_function, division, unicode_literals,
21+
absolute_import)
22+
from builtins import property, super
23+
from distutils.version import StrictVersion
24+
import os
25+
import shutil
26+
import subprocess
27+
from warnings import warn
28+
29+
from ..base import CommandLine, isdefined, CommandLineInputSpec, traits
30+
from ...utils.filemanip import split_filename
31+
32+
33+
def get_custom_path(command):
34+
return os.path.join(os.getenv('NIFTYREGDIR', ''), command)
35+
36+
37+
def no_niftyreg(cmd='reg_f3d'):
38+
try:
39+
return shutil.which(cmd) is None
40+
except AttributeError: # Python < 3.3
41+
return not any(
42+
[os.path.isfile(os.path.join(path, cmd)) and
43+
os.access(os.path.join(path, cmd), os.X_OK)
44+
for path in os.environ["PATH"].split(os.pathsep)])
45+
46+
47+
class NiftyRegCommandInputSpec(CommandLineInputSpec):
48+
"""Input Spec for niftyreg interfaces."""
49+
# Set the number of omp thread to use
50+
omp_core_val = traits.Int(desc='Number of openmp thread to use',
51+
argstr='-omp %i')
52+
53+
54+
class NiftyRegCommand(CommandLine):
55+
"""
56+
Base support interface for NiftyReg commands.
57+
"""
58+
_suffix = '_nr'
59+
_min_version = '1.5.30'
60+
61+
def __init__(self, required_version=None, **inputs):
62+
super(NiftyRegCommand, self).__init__(**inputs)
63+
self.required_version = required_version
64+
_version = self.get_version()
65+
if _version:
66+
_version = _version.decode("utf-8")
67+
if StrictVersion(_version) < StrictVersion(self._min_version):
68+
msg = 'A later version of Niftyreg is required (%s < %s)'
69+
warn(msg % (_version, self._min_version))
70+
if required_version is not None:
71+
if StrictVersion(_version) != StrictVersion(required_version):
72+
msg = 'The version of NiftyReg differs from the required'
73+
msg += '(%s != %s)'
74+
warn(msg % (_version, self.required_version))
75+
76+
def check_version(self):
77+
_version = self.get_version()
78+
if not _version:
79+
raise Exception('Niftyreg not found')
80+
# Decoding to string:
81+
_version = _version.decode("utf-8")
82+
if StrictVersion(_version) < StrictVersion(self._min_version):
83+
err = 'A later version of Niftyreg is required (%s < %s)'
84+
raise ValueError(err % (_version, self._min_version))
85+
if self.required_version:
86+
if StrictVersion(_version) != StrictVersion(self.required_version):
87+
err = 'The version of NiftyReg differs from the required'
88+
err += '(%s != %s)'
89+
raise ValueError(err % (_version, self.required_version))
90+
91+
def get_version(self):
92+
if no_niftyreg(cmd=self.cmd):
93+
return None
94+
exec_cmd = ''.join((self.cmd, ' -v'))
95+
return subprocess.check_output(exec_cmd, shell=True).strip()
96+
97+
@property
98+
def version(self):
99+
return self.get_version()
100+
101+
def exists(self):
102+
return self.get_version() is not None
103+
104+
def _run_interface(self, runtime):
105+
# Update num threads estimate from OMP_NUM_THREADS env var
106+
# Default to 1 if not set
107+
if not isdefined(self.inputs.environ['OMP_NUM_THREADS']):
108+
self.inputs.environ['OMP_NUM_THREADS'] = self.num_threads
109+
return super(NiftyRegCommand, self)._run_interface(runtime)
110+
111+
def _format_arg(self, name, spec, value):
112+
if name == 'omp_core_val':
113+
self.numthreads = value
114+
return super(NiftyRegCommand, self)._format_arg(name, spec, value)
115+
116+
def _gen_fname(self, basename, out_dir=None, suffix=None, ext=None):
117+
if basename == '':
118+
msg = 'Unable to generate filename for command %s. ' % self.cmd
119+
msg += 'basename is not set!'
120+
raise ValueError(msg)
121+
_, final_bn, final_ext = split_filename(basename)
122+
if out_dir is None:
123+
out_dir = os.getcwd()
124+
if ext is not None:
125+
final_ext = ext
126+
if suffix is not None:
127+
final_bn = ''.join((final_bn, suffix))
128+
return os.path.abspath(os.path.join(out_dir, final_bn + final_ext))

0 commit comments

Comments
 (0)