Skip to content

Commit 164a78e

Browse files
committed
fix: deprecation cycle and basic testing
1 parent a9cc329 commit 164a78e

File tree

2 files changed

+90
-45
lines changed

2 files changed

+90
-45
lines changed

nipype/algorithms/confounds.py

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ def _list_outputs(self):
302302
class CompCorInputSpec(BaseInterfaceInputSpec):
303303
realigned_file = File(exists=True, mandatory=True,
304304
desc='already realigned brain image (4D)')
305+
mask_file = InputMultiPath(File(exists=True, deprecated='0.13',
306+
new_name='mask_files',
307+
desc='One or more mask files that determines ROI (3D)'))
305308
mask_files = InputMultiPath(File(exists=True,
306309
desc='One or more mask files that determines ROI (3D)'))
307310
merge_method = traits.Enum('union', 'intersect', 'none', xor=['mask_index'],
@@ -355,19 +358,21 @@ class CompCor(BaseInterface):
355358
'tags': ['method', 'implementation']
356359
}]
357360

358-
def _run_interface(self, runtime):
361+
def _run_interface(self, runtime, tCompCor_mask=False):
362+
359363
imgseries = nb.load(self.inputs.realigned_file,
360364
mmap=NUMPY_MMAP).get_data()
361365
components = None
362366

363-
if isdefined(self.inputs.mask_index) and self.inputs.mask_index:
367+
if isdefined(self.inputs.mask_index):
364368
if self.inputs.mask_index < len(self.inputs.mask_files):
365369
self.inputs.mask_files = [
366370
self.inputs.mask_files[self.inputs.mask_index]]
367371
else:
368-
RuntimeWarning('Mask index exceeded number of masks, using mask'
369-
' {} instead').format(self.inputs.mask_files[0])
370372
self.inputs.mask_files = self.inputs.mask_files[0]
373+
if not tCompCor_mask:
374+
RuntimeWarning('Mask index exceeded number of masks, using '
375+
'mask {} instead'.format(self.inputs.mask_files[0]))
371376

372377
for mask_file in self.inputs.mask_files:
373378
mask = nb.load(mask_file, mmap=NUMPY_MMAP).get_data()
@@ -380,16 +385,16 @@ def _run_interface(self, runtime):
380385
imgseries.shape[:3], mask.shape))
381386

382387
if (isdefined(self.inputs.merge_method) and
383-
self.merge_method != 'none' and
384-
len(self.inputs.mask_files > 1)):
388+
self.inputs.merge_method != 'none' and
389+
len(self.inputs.mask_files) > 1):
385390
if mask_file == self.inputs.mask_files[0]:
386391
new_mask = mask
387392
continue
388-
else:
389-
if self.inputs.merge_method == 'union':
390-
new_mask = np.logical_or(new_mask, mask).astype(int)
391-
elif self.inputs.merge_method == 'intersect':
392-
new_mask = np.logical_and(new_mask, mask).astype(int)
393+
else:
394+
if self.inputs.merge_method == 'union':
395+
new_mask = np.logical_or(new_mask, mask).astype(int)
396+
elif self.inputs.merge_method == 'intersect':
397+
new_mask = np.logical_and(new_mask, mask).astype(int)
393398

394399
if mask_file != self.inputs.mask_files[-1]:
395400
continue
@@ -500,9 +505,12 @@ class TCompCor(CompCor):
500505
output_spec = TCompCorOutputSpec
501506

502507
def _run_interface(self, runtime):
508+
503509
_out_masks = []
504-
imgseries = nb.load(self.inputs.realigned_file,
505-
mmap=NUMPY_MMAP).get_data()
510+
img = nb.load(self.inputs.realigned_file, mmap=NUMPY_MMAP)
511+
imgseries = img.get_data()
512+
aff = img.affine
513+
506514

507515
if imgseries.ndim != 4:
508516
raise ValueError('tCompCor expected a 4-D nifti file. Input {} has '
@@ -511,29 +519,30 @@ def _run_interface(self, runtime):
511519
imgseries.shape))
512520

513521
if isdefined(self.inputs.mask_files):
514-
if isdefined(self.inputs.mask_index) and self.inputs.mask_index:
522+
if isdefined(self.inputs.mask_index):
515523
if self.inputs.mask_index < len(self.inputs.mask_files):
516524
self.inputs.mask_files = [
517525
self.inputs.mask_files[self.inputs.mask_index]]
518526
else:
519527
RuntimeWarning('Mask index exceeded number of masks, using '
520-
'mask {} instead').format(self.inputs.mask_files[0])
528+
'mask {} instead'.format(self.inputs.mask_files[0]))
521529
self.inputs.mask_files = self.inputs.mask_files[0]
522530

523531
for i, mask_file in enumerate(self.inputs.mask_files, 1):
524532
in_mask = nb.load(mask_file, mmap=NUMPY_MMAP).get_data()
525533
if (isdefined(self.inputs.merge_method) and
526-
self.merge_method != 'none' and
527-
len(self.inputs.mask_files > 1)):
534+
self.inputs.merge_method != 'none' and
535+
len(self.inputs.mask_files) > 1):
528536
if mask_file == self.inputs.mask_files[0]:
529537
new_mask = in_mask
530538
continue
531-
else:
532-
if self.inputs.merge_method == 'union':
533-
new_mask = np.logical_or(new_mask, in_mask).astype(int)
534-
elif self.inputs.merge_method == 'intersect':
535-
new_mask = np.logical_and(new_mask, in_mask).astype(int)
536-
539+
else:
540+
if self.inputs.merge_method == 'union':
541+
new_mask = np.logical_or(new_mask,
542+
in_mask).astype(int)
543+
elif self.inputs.merge_method == 'intersect':
544+
new_mask = np.logical_and(new_mask,
545+
in_mask).astype(int)
537546
if mask_file != self.inputs.mask_files[-1]:
538547
continue
539548
else: # merge complete
@@ -559,9 +568,11 @@ def _run_interface(self, runtime):
559568
mask_data = np.zeros_like(in_mask)
560569
mask_data[in_mask != 0] = mask
561570
# save mask
562-
mask_file = os.path.abspath('mask{}.nii'.format(i))
563-
nb.Nifti1Image(mask_data, nb.load(
564-
self.inputs.realigned_file).affine).to_filename(mask_file)
571+
if self.inputs.merge_method == 'none':
572+
mask_file = os.path.abspath('mask{}.nii'.format(i))
573+
else:
574+
mask_file = os.path.abspath('mask.nii')
575+
nb.Nifti1Image(mask_data, aff).to_filename(mask_file)
565576
IFLOG.debug('tCompcor computed and saved mask of shape {} to '
566577
'mask_file {}'.format(mask.shape, mask_file))
567578
_out_masks.append(mask_file)
@@ -577,16 +588,14 @@ def _run_interface(self, runtime):
577588

578589
# save mask
579590
mask_file = os.path.abspath('mask.nii')
580-
nb.Nifti1Image(mask_data,
581-
nb.load(self.inputs.realigned_file).affine).to_filename(mask_file)
582-
IFLOG.debug('tCompcor computed and saved mask of shape {} to mask_file '
583-
'{}'.format(mask.shape, mask_file))
591+
nb.Nifti1Image(mask_data, aff).to_filename(mask_file)
592+
IFLOG.debug('tCompcor computed and saved mask of shape {} to '
593+
'mask_file {}'.format(mask.shape, mask_file))
584594
_out_masks.append(mask_file)
585595
self._set_header('tCompCor')
586596

587597
self.inputs.mask_files = _out_masks
588-
print(self.inputs.mask_files)
589-
super(TCompCor, self)._run_interface(runtime)
598+
super(TCompCor, self)._run_interface(runtime, tCompCor_mask=True)
590599
return runtime
591600

592601
def _list_outputs(self):

nipype/algorithms/tests/test_compcor.py

Lines changed: 49 additions & 13 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_files = 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_files=self.mask_files),
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_files=self.mask_files,
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,16 +75,18 @@ 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_files=self.mask_files,
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]
@@ -91,6 +106,27 @@ 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,
114+
mask_index=Undefined).run()
115+
if method == 'union':
116+
assert np.array_equal(nb.load('mask.nii').get_data(),
117+
([[[0,0],[0,0]],[[0,0],[1,0]]]))
118+
if method == 'intersect':
119+
assert np.array_equal(nb.load('mask.nii').get_data(),
120+
([[[0,0],[0,0]],[[0,1],[0,0]]]))
121+
122+
def test_tcompcor_index_mask(self):
123+
print(len(self.mask_files))
124+
TCompCor(realigned_file=self.realigned_file,
125+
mask_files=self.mask_files,
126+
mask_index=1).run()
127+
assert np.array_equal(nb.load('mask.nii').get_data(),
128+
([[[0,0],[0,0]],[[0,1],[0,0]]]))
129+
94130
def run_cc(self, ccinterface, expected_components, expected_header='CompCor'):
95131
# run
96132
ccresult = ccinterface.run()

0 commit comments

Comments
 (0)