Skip to content

Commit 909187b

Browse files
committed
Merge remote-tracking branch 'upstream/master' into fix/frompy3
* upstream/master: fix: deploy only nipy/nipype master branch fix for identifying if one or multiple durations are employed to describe the condition corrected line of modelgen and added test for variable concatenated events fix test fix: bad copy paste. tst: added more duration checking tests tst: added duration checking fix: use float for computation input fix: update scripts to use single file ants composite_transform output doc: changed description fix: address multimodal and unimodal inputs Update test_auto_MRIConvert.py FIX: add TR, TE and TI flag to FreeSurfer's MRIConvert
2 parents b345ead + 5a24fd2 commit 909187b

File tree

9 files changed

+87
-22
lines changed

9 files changed

+87
-22
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@ deploy:
4545
secure: ZaN6Y4hfybPMi1rW3Qe03irCe/3GU4J4BJsjG+tClVN5Fjc3FCJaYU0k4wNd9q2kMjuRDv1efA9BN/5QasJS32oqs6wHvxzIWA18ucel4BBnAAkuviC1vqi/8Hk/DGB6e1gnmZ1Nv58zVLfn0MwlOKmsGDtvGQWc+HbaV2fS5NA=
4646
on:
4747
tags: true
48+
repo: nipy/nipype
4849
branch: master

examples/fmri_ants_openfmri.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def create_reg_workflow(name='registration'):
236236

237237
merge = pe.Node(niu.Merge(2), iterfield=['in2'], name='mergexfm')
238238
register.connect(convert2itk, 'itk_transform', merge, 'in2')
239-
register.connect(reg, ('composite_transform', pickfirst), merge, 'in1')
239+
register.connect(reg, 'composite_transform', merge, 'in1')
240240

241241
"""
242242
Transform the mean image. First to anatomical and then to target
@@ -429,7 +429,7 @@ def create_fs_reg_workflow(name='registration'):
429429
reg.inputs.use_histogram_matching = [False] * 2 + [True]
430430
reg.inputs.winsorize_lower_quantile = 0.005
431431
reg.inputs.winsorize_upper_quantile = 0.995
432-
reg.inputs.args = '--float'
432+
reg.inputs.float = True
433433
reg.inputs.output_warped_image = 'output_warped_image.nii.gz'
434434
reg.inputs.num_threads = 4
435435
reg.plugin_args = {'qsub_args': '-pe orte 4',
@@ -445,7 +445,7 @@ def create_fs_reg_workflow(name='registration'):
445445

446446
merge = Node(Merge(2), iterfield=['in2'], name='mergexfm')
447447
register.connect(convert2itk, 'itk_transform', merge, 'in2')
448-
register.connect(reg, ('composite_transform', pickfirst), merge, 'in1')
448+
register.connect(reg, 'composite_transform', merge, 'in1')
449449

450450
"""
451451
Transform the mean image. First to anatomical and then to target

examples/rsfmri_vol_surface_preprocessing.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ def create_reg_workflow(name='registration'):
517517
reg.inputs.use_histogram_matching = [False] * 2 + [True]
518518
reg.inputs.winsorize_lower_quantile = 0.005
519519
reg.inputs.winsorize_upper_quantile = 0.995
520-
reg.inputs.args = '--float'
520+
reg.inputs.float = True
521521
reg.inputs.output_warped_image = 'output_warped_image.nii.gz'
522522
reg.inputs.num_threads = 4
523523
reg.plugin_args = {'qsub_args': '-l nodes=1:ppn=4'}
@@ -528,11 +528,9 @@ def create_reg_workflow(name='registration'):
528528
Concatenate the affine and ants transforms into a list
529529
"""
530530

531-
pickfirst = lambda x: x[0]
532-
533531
merge = Node(Merge(2), iterfield=['in2'], name='mergexfm')
534532
register.connect(convert2itk, 'itk_transform', merge, 'in2')
535-
register.connect(reg, ('composite_transform', pickfirst), merge, 'in1')
533+
register.connect(reg, 'composite_transform', merge, 'in1')
536534

537535
"""
538536
Transform the mean image. First to anatomical and then to target

examples/rsfmri_vol_surface_preprocessing_nipy.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def create_reg_workflow(name='registration'):
498498
reg.inputs.use_histogram_matching = [False] * 2 + [True]
499499
reg.inputs.winsorize_lower_quantile = 0.005
500500
reg.inputs.winsorize_upper_quantile = 0.995
501-
reg.inputs.args = '--float'
501+
reg.inputs.float = True
502502
reg.inputs.output_warped_image = 'output_warped_image.nii.gz'
503503
reg.inputs.num_threads = 4
504504
reg.plugin_args = {'sbatch_args': '-c%d' % 4}
@@ -510,11 +510,9 @@ def create_reg_workflow(name='registration'):
510510
Concatenate the affine and ants transforms into a list
511511
"""
512512

513-
pickfirst = lambda x: x[0]
514-
515513
merge = Node(Merge(2), iterfield=['in2'], name='mergexfm')
516514
register.connect(convert2itk, 'itk_transform', merge, 'in2')
517-
register.connect(reg, ('composite_transform', pickfirst), merge, 'in1')
515+
register.connect(reg, 'composite_transform', merge, 'in1')
518516

519517

520518
"""

nipype/algorithms/modelgen.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ def _concatenate_info(self, infolist):
462462
# names, onsets, durations, amplitudes, pmod, tmod, regressor_names,
463463
# regressors
464464
infoout = infolist[0]
465+
for j, val in enumerate(infolist[0].durations):
466+
if len(infolist[0].onsets[j]) > 1 and len(val) == 1:
467+
infoout.durations[j] = (infolist[0].durations[j] *
468+
len(infolist[0].onsets[j]))
465469
for i, info in enumerate(infolist[1:]):
466470
#info.[conditions, tmod] remain the same
467471
if info.onsets:
@@ -476,8 +480,15 @@ def _concatenate_info(self, infolist):
476480
sum(nscans[0:(i + 1)])
477481
infoout.onsets[j].extend(onsets.tolist())
478482
for j, val in enumerate(info.durations):
479-
if len(val) > 1:
483+
if len(info.onsets[j]) > 1 and len(val) == 1:
484+
infoout.durations[j].extend(info.durations[j] *
485+
len(info.onsets[j]))
486+
elif len(info.onsets[j]) == len(val):
480487
infoout.durations[j].extend(info.durations[j])
488+
else:
489+
raise ValueError('Mismatch in number of onsets and \
490+
durations for run {0}, condition \
491+
{1}'.format(i+2, j+1))
481492
if hasattr(info, 'amplitudes') and info.amplitudes:
482493
for j, val in enumerate(info.amplitudes):
483494
infoout.amplitudes[j].extend(info.amplitudes[j])

nipype/algorithms/tests/test_modelgen.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ def test_modelgen1():
3838
yield assert_equal, len(res.outputs.session_info[0]['regress']), 0
3939
yield assert_equal, len(res.outputs.session_info[0]['cond']), 1
4040
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['onset']), np.array([12, 300, 600, 1080])
41+
info = [Bunch(conditions=['cond1'], onsets=[[2]], durations=[[1]]),
42+
Bunch(conditions=['cond1'], onsets=[[3]], durations=[[1]])]
43+
s.inputs.subject_info = deepcopy(info)
44+
res = s.run()
45+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['duration']), np.array([6.])
46+
yield assert_almost_equal, np.array(res.outputs.session_info[1]['cond'][0]['duration']), np.array([6.])
47+
info = [Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2]], durations=[[1, 1], [1]]),
48+
Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2, 4]], durations=[[1, 1], [1, 1]])]
49+
s.inputs.subject_info = deepcopy(info)
50+
s.inputs.input_units = 'scans'
51+
res = s.run()
52+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['duration']), np.array([6., 6.])
53+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][1]['duration']), np.array([6.,])
54+
yield assert_almost_equal, np.array(res.outputs.session_info[1]['cond'][1]['duration']), np.array([6., 6.])
4155
rmtree(tempdir)
4256

4357

@@ -47,6 +61,7 @@ def test_modelgen_spm_concat():
4761
filename2 = os.path.join(tempdir, 'test2.nii')
4862
Nifti1Image(np.random.rand(10, 10, 10, 30), np.eye(4)).to_filename(filename1)
4963
Nifti1Image(np.random.rand(10, 10, 10, 30), np.eye(4)).to_filename(filename2)
64+
# Test case when only one duration is passed, as being the same for all onsets.
5065
s = SpecifySPMModel()
5166
s.inputs.input_units = 'secs'
5267
s.inputs.concatenate_runs = True
@@ -64,16 +79,41 @@ def test_modelgen_spm_concat():
6479
yield assert_equal, np.sum(res.outputs.session_info[0]['regress'][0]['val']), 30
6580
yield assert_equal, len(res.outputs.session_info[0]['cond']), 1
6681
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['onset']), np.array([2.0, 50.0, 100.0, 170.0, 210.0, 220.0, 280.0, 330.0])
82+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['duration']), np.array([1., 1., 1., 1., 1., 1., 1., 1.])
83+
# Test case of scans as output units instead of seconds
6784
setattr(s.inputs, 'output_units', 'scans')
6885
yield assert_equal, s.inputs.output_units, 'scans'
6986
s.inputs.subject_info = deepcopy(info)
7087
res = s.run()
7188
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['onset']), np.array([2.0, 50.0, 100.0, 170.0, 210.0, 220.0, 280.0, 330.0])/6
89+
# Test case for no concatenation with seconds as output units
7290
s.inputs.concatenate_runs = False
7391
s.inputs.subject_info = deepcopy(info)
7492
s.inputs.output_units = 'secs'
7593
res = s.run()
7694
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['onset']), np.array([2.0, 50.0, 100.0, 170.0])
95+
# Test case for variable number of events in separate runs, sometimes unique.
96+
filename3 = os.path.join(tempdir, 'test3.nii')
97+
Nifti1Image(np.random.rand(10, 10, 10, 30), np.eye(4)).to_filename(filename3)
98+
s.inputs.functional_runs = [filename1, filename2, filename3]
99+
info = [Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2]], durations=[[1, 1], [1]]),
100+
Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2, 4]], durations=[[1, 1], [1, 1]]),
101+
Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2]], durations=[[1, 1], [1]])]
102+
s.inputs.subject_info = deepcopy(info)
103+
res = s.run()
104+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['duration']), np.array([1., 1.])
105+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][1]['duration']), np.array([1.,])
106+
yield assert_almost_equal, np.array(res.outputs.session_info[1]['cond'][1]['duration']), np.array([1., 1.])
107+
yield assert_almost_equal, np.array(res.outputs.session_info[2]['cond'][1]['duration']), np.array([1.,])
108+
# Test case for variable number of events in concatenated runs, sometimes unique.
109+
s.inputs.concatenate_runs = True
110+
info = [Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2]], durations=[[1, 1], [1]]),
111+
Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2, 4]], durations=[[1, 1], [1, 1]]),
112+
Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 3], [2]], durations=[[1, 1], [1]])]
113+
s.inputs.subject_info = deepcopy(info)
114+
res = s.run()
115+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][0]['duration']), np.array([1., 1., 1., 1., 1., 1.])
116+
yield assert_almost_equal, np.array(res.outputs.session_info[0]['cond'][1]['duration']), np.array([1., 1., 1., 1.])
77117
rmtree(tempdir)
78118

79119

nipype/interfaces/ants/registration.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
>>> os.chdir(datadir)
88
"""
99

10-
from ..base import (TraitedSpec, File, traits)
10+
from ..base import (TraitedSpec, File, traits, InputMultiPath)
1111
from .base import ANTSCommand, ANTSCommandInputSpec
1212
import os
13-
from nipype.interfaces.base import InputMultiPath
14-
from nipype.interfaces.traits_extension import isdefined
13+
from ..traits_extension import isdefined
1514
import numpy as np
1615

1716

@@ -458,15 +457,22 @@ class Registration(ANTSCommand):
458457
459458
>>> # Test multiple metrics per stage
460459
>>> reg5 = copy.deepcopy(reg)
461-
>>> reg5.inputs.fixed_image = [ 'fixed1.nii', 'fixed2.nii' ]
462-
>>> reg5.inputs.moving_image = [ 'moving1.nii', 'moving2.nii' ]
460+
>>> reg5.inputs.fixed_image = 'fixed1.nii'
461+
>>> reg5.inputs.moving_image = 'moving1.nii'
463462
>>> reg5.inputs.metric = ['Mattes', ['Mattes', 'CC']]
464463
>>> reg5.inputs.metric_weight = [1, [.5,.5]]
465-
>>> reg5.inputs.radius_or_number_of_bins = [32, [32,4] ]
464+
>>> reg5.inputs.radius_or_number_of_bins = [32, [32, 4] ]
466465
>>> reg5.inputs.sampling_strategy = ['Random', None] # use default strategy in second stage
467466
>>> reg5.inputs.sampling_percentage = [0.05, [0.05, 0.10]]
468467
>>> reg5.cmdline
469-
'antsRegistration --collapse-output-transforms 0 --dimensionality 3 --initial-moving-transform [ trans.mat, 1 ] --initialize-transforms-per-stage 0 --interpolation Linear --output [ output_, output_warped_image.nii.gz ] --restore-state trans.mat --save-state trans.mat --transform Affine[ 2.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32, Random, 0.05 ] --convergence [ 1500x200, 1e-08, 20 ] --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] --metric CC[ fixed2.nii, moving2.nii, 0.5, 4, None, 0.1 ] --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1'
468+
'antsRegistration --collapse-output-transforms 0 --dimensionality 3 --initial-moving-transform [ trans.mat, 1 ] --initialize-transforms-per-stage 0 --interpolation Linear --output [ output_, output_warped_image.nii.gz ] --restore-state trans.mat --save-state trans.mat --transform Affine[ 2.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32, Random, 0.05 ] --convergence [ 1500x200, 1e-08, 20 ] --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] --metric CC[ fixed1.nii, moving1.nii, 0.5, 4, None, 0.1 ] --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1'
469+
470+
>>> # Test multiple inputs
471+
>>> reg6 = copy.deepcopy(reg5)
472+
>>> reg6.inputs.fixed_image = ['fixed1.nii', 'fixed2.nii']
473+
>>> reg6.inputs.moving_image = ['moving1.nii', 'moving2.nii']
474+
>>> reg6.cmdline
475+
'antsRegistration --collapse-output-transforms 0 --dimensionality 3 --initial-moving-transform [ trans.mat, 1 ] --initialize-transforms-per-stage 0 --interpolation Linear --output [ output_, output_warped_image.nii.gz ] --restore-state trans.mat --save-state trans.mat --transform Affine[ 2.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32, Random, 0.05 ] --convergence [ 1500x200, 1e-08, 20 ] --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] --metric CC[ fixed2.nii, moving2.nii, 0.5, 4, None, 0.1 ] --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --write-composite-transform 1'
470476
"""
471477
DEF_SAMPLING_STRATEGY = 'None'
472478
"""The default sampling strategy argument."""
@@ -520,12 +526,12 @@ def _formatMetric(self, index):
520526
specs = list()
521527
for i in indexes:
522528
temp = dict([(k, v[i]) for k, v in items])
523-
if i > len( self.inputs.fixed_image ):
529+
if len(self.inputs.fixed_image) == 1:
524530
temp["fixed_image"] = self.inputs.fixed_image[0]
525531
else:
526532
temp["fixed_image"] = self.inputs.fixed_image[i]
527533

528-
if i > len( self.inputs.moving_image ):
534+
if len(self.inputs.moving_image) == 1:
529535
temp["moving_image"] = self.inputs.moving_image[0]
530536
else:
531537
temp["moving_image"] = self.inputs.moving_image[i]

nipype/interfaces/freesurfer/preprocess.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ class MRIConvertInputSpec(FSTraitedSpec):
212212
desc='dont rescale values for COR')
213213
no_change = traits.Bool(argstr='--nochange',
214214
desc="don't change type of input to that of template")
215+
tr = traits.Int(argstr='-tr %d',
216+
desc='TR in msec')
217+
te = traits.Int(argstr='-te %d',
218+
desc='TE in msec')
219+
ti = traits.Int(argstr='-ti %d',
220+
desc='TI in msec (note upper case flag)')
215221
autoalign_matrix = File(exists=True, argstr='--autoalign %s',
216222
desc='text file with autoalign matrix')
217223
unwarp_gradient = traits.Bool(argstr='--unwarp_gradient_nonlinearity',

nipype/interfaces/freesurfer/tests/test_auto_MRIConvert.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,17 @@ def test_MRIConvert_inputs():
162162
subject_name=dict(argstr='--subject_name %s',
163163
),
164164
subjects_dir=dict(),
165+
te=dict(argstr='-te %d',
166+
),
165167
template_info=dict(),
166168
template_type=dict(argstr='--template_type %s',
167169
),
168170
terminal_output=dict(nohash=True,
169171
),
172+
ti=dict(argstr='-ti %d',
173+
),
174+
tr=dict(argstr='-tr %d',
175+
),
170176
unwarp_gradient=dict(argstr='--unwarp_gradient_nonlinearity',
171177
),
172178
vox_size=dict(argstr='-voxsize %f %f %f',
@@ -190,4 +196,3 @@ def test_MRIConvert_outputs():
190196
for key, metadata in output_map.items():
191197
for metakey, value in metadata.items():
192198
yield assert_equal, getattr(outputs.traits()[key], metakey), value
193-

0 commit comments

Comments
 (0)