Skip to content

Commit 5037c40

Browse files
committed
Merge branch 'master' of https://github.com/kesshijordan/nipype into fix_dtitk_nonlinear_wf
2 parents 73abfda + 6db39fd commit 5037c40

File tree

148 files changed

+1434
-797
lines changed

Some content is hidden

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

148 files changed

+1434
-797
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: 68 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
from scipy.ndimage.measurements import center_of_mass, label
2020

2121
from .. import config, logging
22-
from ..utils.misc import package_check
2322

24-
from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File,
25-
InputMultiPath, BaseInterfaceInputSpec,
26-
isdefined)
27-
from ..utils import NUMPY_MMAP
23+
from ..interfaces.base import (
24+
SimpleInterface, BaseInterface, traits, TraitedSpec, File,
25+
InputMultiPath, BaseInterfaceInputSpec,
26+
isdefined)
27+
from ..interfaces.nipy.base import NipyBaseInterface
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))
478448

449+
ncomp = refdata.shape[-1]
450+
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(
@@ -651,7 +646,7 @@ class SimilarityOutputSpec(TraitedSpec):
651646
traits.Float(desc="Similarity between volume 1 and 2, frame by frame"))
652647

653648

654-
class Similarity(BaseInterface):
649+
class Similarity(NipyBaseInterface):
655650
"""Calculates similarity between two 3D or 4D volumes. Both volumes have to be in
656651
the same coordinate system, same space within that coordinate system and
657652
with the same voxel dimensions.
@@ -674,19 +669,8 @@ class Similarity(BaseInterface):
674669

675670
input_spec = SimilarityInputSpec
676671
output_spec = SimilarityOutputSpec
677-
_have_nipy = True
678-
679-
def __init__(self, **inputs):
680-
try:
681-
package_check('nipy')
682-
except Exception:
683-
self._have_nipy = False
684-
super(Similarity, self).__init__(**inputs)
685672

686673
def _run_interface(self, runtime):
687-
if not self._have_nipy:
688-
raise RuntimeError('nipy is not installed')
689-
690674
from nipy.algorithms.registration.histogram_registration import HistogramRegistration
691675
from nipy.algorithms.registration.affine import Affine
692676

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

0 commit comments

Comments
 (0)