Skip to content

Commit 9b0686d

Browse files
authored
Merge pull request #2122 from effigies/enh/compcor_exclude
ENH: Allow CompCor to skip initial volumes
2 parents 3b15d39 + 0927cab commit 9b0686d

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

nipype/algorithms/confounds.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,9 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
345345
'unspecified')
346346
save_pre_filter = traits.Either(
347347
traits.Bool, File, desc='Save pre-filter basis as text file')
348+
ignore_initial_volumes = traits.Range(
349+
low=0, usedefault=True,
350+
desc='Number of volumes at start of series to ignore')
348351

349352

350353
class CompCorOutputSpec(TraitedSpec):
@@ -357,6 +360,26 @@ class CompCor(BaseInterface):
357360
"""
358361
Interface with core CompCor computation, used in aCompCor and tCompCor
359362
363+
CompCor provides three pre-filter options, all of which include per-voxel
364+
mean removal:
365+
- polynomial: Legendre polynomial basis
366+
- cosine: Discrete cosine basis
367+
- False: mean-removal only
368+
369+
In the case of ``polynomial`` and ``cosine`` filters, a pre-filter file may
370+
be saved with a row for each volume/timepoint, and a column for each
371+
non-constant regressor.
372+
If no non-constant (mean-removal) columns are used, this file may be empty.
373+
374+
If ``ignore_initial_volumes`` is set, then the specified number of initial
375+
volumes are excluded both from pre-filtering and CompCor component
376+
extraction.
377+
Each column in the components and pre-filter files are prefixe with zeros
378+
for each excluded volume so that the number of rows continues to match the
379+
number of volumes in the input file.
380+
In addition, for each excluded volume, a column is added to the pre-filter
381+
file with a 1 in the corresponding row.
382+
360383
Example
361384
-------
362385
@@ -417,6 +440,12 @@ def _run_interface(self, runtime):
417440
header=imgseries.header)
418441
mask_images = [img]
419442

443+
skip_vols = self.inputs.ignore_initial_volumes
444+
if skip_vols:
445+
imgseries = imgseries.__class__(
446+
imgseries.get_data()[..., skip_vols:], imgseries.affine,
447+
imgseries.header)
448+
420449
mask_images = self._process_masks(mask_images, imgseries.get_data())
421450

422451
TR = 0
@@ -441,6 +470,13 @@ def _run_interface(self, runtime):
441470
imgseries.get_data(), mask_images, self.inputs.num_components,
442471
self.inputs.pre_filter, degree, self.inputs.high_pass_cutoff, TR)
443472

473+
if skip_vols:
474+
old_comp = components
475+
nrows = skip_vols + components.shape[0]
476+
components = np.zeros((nrows, components.shape[1]),
477+
dtype=components.dtype)
478+
components[skip_vols:] = old_comp
479+
444480
components_file = os.path.join(os.getcwd(), self.inputs.components_file)
445481
np.savetxt(components_file, components, fmt=b"%.10f", delimiter='\t',
446482
header=self._make_headers(components.shape[1]), comments='')
@@ -451,6 +487,15 @@ def _run_interface(self, runtime):
451487
'cosine': 'cos'}[self.inputs.pre_filter]
452488
ncols = filter_basis.shape[1] if filter_basis.size > 0 else 0
453489
header = ['{}{:02d}'.format(ftype, i) for i in range(ncols)]
490+
if skip_vols:
491+
old_basis = filter_basis
492+
nrows = filter_basis.shape[0] if filter_basis.size > 0 else 0
493+
filter_basis = np.zeros((nrows + skip_vols, ncols + skip_vols),
494+
dtype=filter_basis.dtype)
495+
filter_basis[skip_vols:, :ncols] = old_basis
496+
filter_basis[:skip_vols, -skip_vols:] = np.eye(skip_vols)
497+
header.extend(['SteadyState{:02d}'.format(i)
498+
for i in range(skip_vols)])
454499
np.savetxt(pre_filter_file, filter_basis, fmt=b'%.10f',
455500
delimiter='\t', header='\t'.join(header), comments='')
456501

nipype/algorithms/tests/test_auto_ACompCor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ def test_ACompCor_inputs():
1212
ignore_exception=dict(nohash=True,
1313
usedefault=True,
1414
),
15+
ignore_initial_volumes=dict(usedefault=True,
16+
),
1517
mask_files=dict(),
1618
mask_index=dict(requires=['mask_files'],
1719
xor=['merge_method'],

nipype/algorithms/tests/test_auto_TCompCor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ def test_TCompCor_inputs():
1212
ignore_exception=dict(nohash=True,
1313
usedefault=True,
1414
),
15+
ignore_initial_volumes=dict(usedefault=True,
16+
),
1517
mask_files=dict(),
1618
mask_index=dict(requires=['mask_files'],
1719
xor=['merge_method'],

0 commit comments

Comments
 (0)