Skip to content

Commit 6db39fd

Browse files
committed
Merge branch 'master' of https://github.com/nipy/nipype
2 parents ebe82a4 + 9333ff8 commit 6db39fd

File tree

125 files changed

+1162
-618
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+1162
-618
lines changed

nipype/algorithms/confounds.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,12 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
390390
desc='Detrend time series prior to component '
391391
'extraction')
392392
use_regress_poly = traits.Bool(
393-
True,
394393
deprecated='0.15.0',
395394
new_name='pre_filter',
396395
desc=('use polynomial regression '
397396
'pre-component extraction'))
398397
regress_poly_degree = traits.Range(
399-
low=1, default=1, usedefault=True, desc='the degree polynomial to use')
398+
low=1, value=1, usedefault=True, desc='the degree polynomial to use')
400399
header_prefix = traits.Str(
401400
desc=('the desired header for the output tsv '
402401
'file (one column). If undefined, will '

nipype/algorithms/mesh.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ class MeshWarpMathsInputSpec(BaseInterfaceInputSpec):
289289
float_trait,
290290
File(exists=True),
291291
default=1.0,
292+
usedefault=True,
292293
mandatory=True,
293294
desc='image, float or tuple of floats to act as operator')
294295

nipype/algorithms/metrics.py

Lines changed: 66 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020

2121
from .. import config, logging
2222

23-
from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File,
24-
InputMultiPath, BaseInterfaceInputSpec,
25-
isdefined)
23+
from ..interfaces.base import (
24+
SimpleInterface, BaseInterface, traits, TraitedSpec, File,
25+
InputMultiPath, BaseInterfaceInputSpec,
26+
isdefined)
2627
from ..interfaces.nipy.base import NipyBaseInterface
27-
from ..utils import NUMPY_MMAP
2828

2929
iflogger = logging.getLogger('interface')
3030

@@ -383,6 +383,7 @@ class FuzzyOverlapInputSpec(BaseInterfaceInputSpec):
383383
File(exists=True),
384384
mandatory=True,
385385
desc='Test image. Requires the same dimensions as in_ref.')
386+
in_mask = File(exists=True, desc='calculate overlap only within mask')
386387
weighting = traits.Enum(
387388
'none',
388389
'volume',
@@ -403,10 +404,6 @@ class FuzzyOverlapInputSpec(BaseInterfaceInputSpec):
403404
class FuzzyOverlapOutputSpec(TraitedSpec):
404405
jaccard = traits.Float(desc='Fuzzy Jaccard Index (fJI), all the classes')
405406
dice = traits.Float(desc='Fuzzy Dice Index (fDI), all the classes')
406-
diff_file = File(
407-
exists=True,
408-
desc=
409-
'resulting difference-map of all classes, using the chosen weighting')
410407
class_fji = traits.List(
411408
traits.Float(),
412409
desc='Array containing the fJIs of each computed class')
@@ -415,7 +412,7 @@ class FuzzyOverlapOutputSpec(TraitedSpec):
415412
desc='Array containing the fDIs of each computed class')
416413

417414

418-
class FuzzyOverlap(BaseInterface):
415+
class FuzzyOverlap(SimpleInterface):
419416
"""Calculates various overlap measures between two maps, using the fuzzy
420417
definition proposed in: Crum et al., Generalized Overlap Measures for
421418
Evaluation and Validation in Medical Image Analysis, IEEE Trans. Med.
@@ -439,77 +436,75 @@ class FuzzyOverlap(BaseInterface):
439436
output_spec = FuzzyOverlapOutputSpec
440437

441438
def _run_interface(self, runtime):
442-
ncomp = len(self.inputs.in_ref)
443-
assert (ncomp == len(self.inputs.in_tst))
444-
weights = np.ones(shape=ncomp)
445-
446-
img_ref = np.array([
447-
nb.load(fname, mmap=NUMPY_MMAP).get_data()
448-
for fname in self.inputs.in_ref
449-
])
450-
img_tst = np.array([
451-
nb.load(fname, mmap=NUMPY_MMAP).get_data()
452-
for fname in self.inputs.in_tst
453-
])
454-
455-
msk = np.sum(img_ref, axis=0)
456-
msk[msk > 0] = 1.0
457-
tst_msk = np.sum(img_tst, axis=0)
458-
tst_msk[tst_msk > 0] = 1.0
459-
460-
# check that volumes are normalized
461-
# img_ref[:][msk>0] = img_ref[:][msk>0] / (np.sum( img_ref, axis=0 ))[msk>0]
462-
# img_tst[tst_msk>0] = img_tst[tst_msk>0] / np.sum( img_tst, axis=0 )[tst_msk>0]
463-
464-
self._jaccards = []
465-
volumes = []
466-
467-
diff_im = np.zeros(img_ref.shape)
468-
469-
for ref_comp, tst_comp, diff_comp in zip(img_ref, img_tst, diff_im):
470-
num = np.minimum(ref_comp, tst_comp)
471-
ddr = np.maximum(ref_comp, tst_comp)
472-
diff_comp[ddr > 0] += 1.0 - (num[ddr > 0] / ddr[ddr > 0])
473-
self._jaccards.append(np.sum(num) / np.sum(ddr))
474-
volumes.append(np.sum(ref_comp))
475-
476-
self._dices = 2.0 * (np.array(self._jaccards) /
477-
(np.array(self._jaccards) + 1.0))
439+
# Load data
440+
refdata = nb.concat_images(self.inputs.in_ref).get_data()
441+
tstdata = nb.concat_images(self.inputs.in_tst).get_data()
442+
443+
# Data must have same shape
444+
if not refdata.shape == tstdata.shape:
445+
raise RuntimeError(
446+
'Size of "in_tst" %s must match that of "in_ref" %s.' %
447+
(tstdata.shape, refdata.shape))
448+
449+
ncomp = refdata.shape[-1]
478450

451+
# Load mask
452+
mask = np.ones_like(refdata, dtype=bool)
453+
if isdefined(self.inputs.in_mask):
454+
mask = nb.load(self.inputs.in_mask).get_data()
455+
mask = mask > 0
456+
mask = np.repeat(mask[..., np.newaxis], ncomp, -1)
457+
assert mask.shape == refdata.shape
458+
459+
# Drop data outside mask
460+
refdata = refdata[mask]
461+
tstdata = tstdata[mask]
462+
463+
if np.any(refdata < 0.0):
464+
iflogger.warning('Negative values encountered in "in_ref" input, '
465+
'taking absolute values.')
466+
refdata = np.abs(refdata)
467+
468+
if np.any(tstdata < 0.0):
469+
iflogger.warning('Negative values encountered in "in_tst" input, '
470+
'taking absolute values.')
471+
tstdata = np.abs(tstdata)
472+
473+
if np.any(refdata > 1.0):
474+
iflogger.warning('Values greater than 1.0 found in "in_ref" input, '
475+
'scaling values.')
476+
refdata /= refdata.max()
477+
478+
if np.any(tstdata > 1.0):
479+
iflogger.warning('Values greater than 1.0 found in "in_tst" input, '
480+
'scaling values.')
481+
tstdata /= tstdata.max()
482+
483+
numerators = np.atleast_2d(
484+
np.minimum(refdata, tstdata).reshape((-1, ncomp)))
485+
denominators = np.atleast_2d(
486+
np.maximum(refdata, tstdata).reshape((-1, ncomp)))
487+
488+
jaccards = numerators.sum(axis=0) / denominators.sum(axis=0)
489+
490+
# Calculate weights
491+
weights = np.ones_like(jaccards, dtype=float)
479492
if self.inputs.weighting != "none":
480-
weights = 1.0 / np.array(volumes)
493+
volumes = np.sum((refdata + tstdata) > 0, axis=1).reshape((-1, ncomp))
494+
weights = 1.0 / volumes
481495
if self.inputs.weighting == "squared_vol":
482496
weights = weights**2
483497

484498
weights = weights / np.sum(weights)
499+
dices = 2.0 * jaccards / (jaccards + 1.0)
485500

486-
setattr(self, '_jaccard', np.sum(weights * self._jaccards))
487-
setattr(self, '_dice', np.sum(weights * self._dices))
488-
489-
diff = np.zeros(diff_im[0].shape)
490-
491-
for w, ch in zip(weights, diff_im):
492-
ch[msk == 0] = 0
493-
diff += w * ch
494-
495-
nb.save(
496-
nb.Nifti1Image(diff,
497-
nb.load(self.inputs.in_ref[0]).affine,
498-
nb.load(self.inputs.in_ref[0]).header),
499-
self.inputs.out_file)
500-
501+
# Fill-in the results object
502+
self._results['jaccard'] = float(weights.dot(jaccards))
503+
self._results['dice'] = float(weights.dot(dices))
504+
self._results['class_fji'] = [float(v) for v in jaccards]
505+
self._results['class_fdi'] = [float(v) for v in dices]
501506
return runtime
502507

503-
def _list_outputs(self):
504-
outputs = self._outputs().get()
505-
for method in ("dice", "jaccard"):
506-
outputs[method] = getattr(self, '_' + method)
507-
# outputs['volume_difference'] = self._volume
508-
outputs['diff_file'] = os.path.abspath(self.inputs.out_file)
509-
outputs['class_fji'] = np.array(self._jaccards).astype(float).tolist()
510-
outputs['class_fdi'] = self._dices.astype(float).tolist()
511-
return outputs
512-
513508

514509
class ErrorMapInputSpec(BaseInterfaceInputSpec):
515510
in_ref = File(

nipype/algorithms/misc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ..interfaces.base import (
2727
BaseInterface, traits, TraitedSpec, File, InputMultiPath, OutputMultiPath,
2828
BaseInterfaceInputSpec, isdefined, DynamicTraitedSpec, Undefined)
29-
from ..utils.filemanip import fname_presuffix, split_filename, filename_to_list
29+
from ..utils.filemanip import fname_presuffix, split_filename, ensure_list
3030
from ..utils import NUMPY_MMAP
3131

3232
from . import confounds
@@ -1479,7 +1479,7 @@ def _gen_fname(self, suffix, idx=None, ext=None):
14791479
def _run_interface(self, runtime):
14801480
total = None
14811481
self._median_files = []
1482-
for idx, fname in enumerate(filename_to_list(self.inputs.in_files)):
1482+
for idx, fname in enumerate(ensure_list(self.inputs.in_files)):
14831483
img = nb.load(fname, mmap=NUMPY_MMAP)
14841484
data = np.median(img.get_data(), axis=3)
14851485
if self.inputs.median_per_file:

nipype/algorithms/modelgen.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ..interfaces.base import (BaseInterface, TraitedSpec, InputMultiPath,
2727
traits, File, Bunch, BaseInterfaceInputSpec,
2828
isdefined)
29-
from ..utils.filemanip import filename_to_list
29+
from ..utils.filemanip import ensure_list
3030
from ..utils.misc import normalize_mc_params
3131
from .. import config, logging
3232
iflogger = logging.getLogger('interface')
@@ -383,7 +383,7 @@ def _generate_standard_design(self,
383383
if outliers is not None:
384384
for i, out in enumerate(outliers):
385385
numscans = 0
386-
for f in filename_to_list(sessinfo[i]['scans']):
386+
for f in ensure_list(sessinfo[i]['scans']):
387387
shape = load(f, mmap=NUMPY_MMAP).shape
388388
if len(shape) == 3 or shape[3] == 1:
389389
iflogger.warning('You are using 3D instead of 4D '
@@ -580,7 +580,7 @@ def _generate_design(self, infolist=None):
580580
else:
581581
infolist = gen_info(self.inputs.event_files)
582582
concatlist, nscans = self._concatenate_info(infolist)
583-
functional_runs = [filename_to_list(self.inputs.functional_runs)]
583+
functional_runs = [ensure_list(self.inputs.functional_runs)]
584584
realignment_parameters = []
585585
if isdefined(self.inputs.realignment_parameters):
586586
realignment_parameters = []

nipype/algorithms/rapidart.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from ..interfaces.base import (BaseInterface, traits, InputMultiPath,
2929
OutputMultiPath, TraitedSpec, File,
3030
BaseInterfaceInputSpec, isdefined)
31-
from ..utils.filemanip import filename_to_list, save_json, split_filename
31+
from ..utils.filemanip import ensure_list, save_json, split_filename
3232
from ..utils.misc import find_indices, normalize_mc_params
3333
from .. import logging, config
3434
iflogger = logging.getLogger('interface')
@@ -234,8 +234,9 @@ class ArtifactDetectInputSpec(BaseInterfaceInputSpec):
234234
desc=("Mask threshold to be used if mask_type"
235235
" is 'thresh'."))
236236
intersect_mask = traits.Bool(
237-
True, desc=("Intersect the masks when computed from "
238-
"spm_global."))
237+
True, usedefault=True,
238+
desc=("Intersect the masks when computed from "
239+
"spm_global."))
239240
save_plot = traits.Bool(
240241
True, desc="save plots containing outliers", usedefault=True)
241242
plot_type = traits.Enum(
@@ -376,7 +377,7 @@ def _list_outputs(self):
376377
outputs['displacement_files'] = []
377378
if isdefined(self.inputs.save_plot) and self.inputs.save_plot:
378379
outputs['plot_files'] = []
379-
for i, f in enumerate(filename_to_list(self.inputs.realigned_files)):
380+
for i, f in enumerate(ensure_list(self.inputs.realigned_files)):
380381
(outlierfile, intensityfile, statsfile, normfile, plotfile,
381382
displacementfile, maskfile) = \
382383
self._get_output_filenames(f, os.getcwd())
@@ -616,8 +617,8 @@ def _detect_outliers_core(self, imgfile, motionfile, runidx, cwd=None):
616617
def _run_interface(self, runtime):
617618
"""Execute this module.
618619
"""
619-
funcfilelist = filename_to_list(self.inputs.realigned_files)
620-
motparamlist = filename_to_list(self.inputs.realignment_parameters)
620+
funcfilelist = ensure_list(self.inputs.realigned_files)
621+
motparamlist = ensure_list(self.inputs.realignment_parameters)
621622
for i, imgf in enumerate(funcfilelist):
622623
self._detect_outliers_core(
623624
imgf, motparamlist[i], i, cwd=os.getcwd())

nipype/algorithms/stats.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
Managing statistical maps
6+
"""
7+
from __future__ import (print_function, division, unicode_literals,
8+
absolute_import)
9+
import os
10+
import nibabel as nb
11+
import numpy as np
12+
13+
from ..interfaces.base import (
14+
BaseInterfaceInputSpec, TraitedSpec, SimpleInterface,
15+
traits, InputMultiPath, File
16+
)
17+
from ..utils.filemanip import split_filename
18+
19+
20+
class ActivationCountInputSpec(BaseInterfaceInputSpec):
21+
in_files = InputMultiPath(File(exists=True), mandatory=True,
22+
desc='input file, generally a list of z-stat maps')
23+
threshold = traits.Float(
24+
mandatory=True, desc='binarization threshold. E.g. a threshold of 1.65 '
25+
'corresponds to a two-sided Z-test of p<.10')
26+
27+
28+
class ActivationCountOutputSpec(TraitedSpec):
29+
out_file = File(exists=True, desc='output activation count map')
30+
acm_pos = File(exists=True, desc='positive activation count map')
31+
acm_neg = File(exists=True, desc='negative activation count map')
32+
33+
34+
class ActivationCount(SimpleInterface):
35+
"""
36+
Calculate a simple Activation Count Maps
37+
38+
Adapted from: https://github.com/poldracklab/CNP_task_analysis/\
39+
blob/61c27f5992db9d8800884f8ffceb73e6957db8af/CNP_2nd_level_ACM.py
40+
"""
41+
input_spec = ActivationCountInputSpec
42+
output_spec = ActivationCountOutputSpec
43+
44+
def _run_interface(self, runtime):
45+
allmaps = nb.concat_images(self.inputs.in_files).get_data()
46+
acm_pos = np.mean(allmaps > self.inputs.threshold,
47+
axis=3, dtype=np.float32)
48+
acm_neg = np.mean(allmaps < -1.0 * self.inputs.threshold,
49+
axis=3, dtype=np.float32)
50+
acm_diff = acm_pos - acm_neg
51+
52+
template_fname = self.inputs.in_files[0]
53+
ext = split_filename(template_fname)[2]
54+
fname_fmt = os.path.join(runtime.cwd, 'acm_{}' + ext).format
55+
56+
self._results['out_file'] = fname_fmt('diff')
57+
self._results['acm_pos'] = fname_fmt('pos')
58+
self._results['acm_neg'] = fname_fmt('neg')
59+
60+
img = nb.load(template_fname)
61+
img.__class__(acm_diff, img.affine, img.header).to_filename(
62+
self._results['out_file'])
63+
img.__class__(acm_pos, img.affine, img.header).to_filename(
64+
self._results['acm_pos'])
65+
img.__class__(acm_neg, img.affine, img.header).to_filename(
66+
self._results['acm_neg'])
67+
68+
return runtime
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from __future__ import unicode_literals
3+
from ..stats import ActivationCount
4+
5+
6+
def test_ActivationCount_inputs():
7+
input_map = dict(
8+
ignore_exception=dict(
9+
deprecated='1.0.0',
10+
nohash=True,
11+
usedefault=True,
12+
),
13+
in_files=dict(mandatory=True, ),
14+
threshold=dict(mandatory=True, ),
15+
)
16+
inputs = ActivationCount.input_spec()
17+
18+
for key, metadata in list(input_map.items()):
19+
for metakey, value in list(metadata.items()):
20+
assert getattr(inputs.traits()[key], metakey) == value
21+
def test_ActivationCount_outputs():
22+
output_map = dict(
23+
acm_neg=dict(),
24+
acm_pos=dict(),
25+
out_file=dict(),
26+
)
27+
outputs = ActivationCount.output_spec()
28+
29+
for key, metadata in list(output_map.items()):
30+
for metakey, value in list(metadata.items()):
31+
assert getattr(outputs.traits()[key], metakey) == value

0 commit comments

Comments
 (0)