Skip to content

Commit 98ed0f3

Browse files
committed
Move to pre_filter approach
1 parent 76ea740 commit 98ed0f3

File tree

1 file changed

+42
-35
lines changed

1 file changed

+42
-35
lines changed

nipype/algorithms/confounds.py

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -325,33 +325,32 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
325325
components_file = traits.Str('components_file.txt', usedefault=True,
326326
desc='Filename to store physiological components')
327327
num_components = traits.Int(6, usedefault=True) # 6 for BOLD, 4 for ASL
328-
use_regress_poly = traits.Bool(True, usedefault=True, xor=['high_pass_filter'],
328+
pre_filter = traits.Enum('polynomial', 'cosine', False, usedefault=True,
329+
desc='Detrend time series prior to component '
330+
'extraction')
331+
use_regress_poly = traits.Bool(True,
332+
deprecated='0.15.0', new_name='pre_filter',
329333
desc=('use polynomial regression '
330334
'pre-component extraction'))
331335
regress_poly_degree = traits.Range(low=1, default=1, usedefault=True,
332336
desc='the degree polynomial to use')
333337
header_prefix = traits.Str(desc=('the desired header for the output tsv '
334338
'file (one column). If undefined, will '
335339
'default to "CompCor"'))
336-
high_pass_filter = traits.Bool(
337-
False, xor=['use_regress_poly'],
338-
desc='Use cosine basis to remove low-frequency trends pre-component '
339-
'extraction')
340340
high_pass_cutoff = traits.Float(
341-
128, usedefault=True, requires=['high_pass_filter'],
342-
desc='Cutoff (in seconds) for high-pass filter')
341+
128, usedefault=True,
342+
desc='Cutoff (in seconds) for "cosine" pre-filter')
343343
repetition_time = traits.Float(
344344
desc='Repetition time (TR) of series - derived from image header if '
345345
'unspecified')
346-
save_hpf_basis = traits.Either(
347-
traits.Bool, File, requires=['high_pass_filter'],
348-
desc='Save high pass filter basis as text file')
346+
save_pre_filter = traits.Either(
347+
traits.Bool, File, desc='Save pre-filter basis as text file')
349348

350349

351350
class CompCorOutputSpec(TraitedSpec):
352351
components_file = File(exists=True,
353352
desc='text file containing the noise components')
354-
hpf_basis_file = File(desc='text file containing high-pass filter basis')
353+
pre_filter_file = File(desc='text file containing high-pass filter basis')
355354

356355

357356
class CompCor(BaseInterface):
@@ -397,11 +396,14 @@ def _run_interface(self, runtime):
397396
self.inputs.merge_method,
398397
self.inputs.mask_index)
399398

399+
if self.inputs.use_regress_poly:
400+
self.inputs.pre_filter = 'polynomial'
401+
402+
# Degree 0 == remove mean; see compute_noise_components
400403
degree = (self.inputs.regress_poly_degree if
401-
self.inputs.use_regress_poly else 0)
404+
self.inputs.pre_filter == 'polynomial' else 0)
402405

403-
imgseries = nb.load(self.inputs.realigned_file,
404-
mmap=NUMPY_MMAP)
406+
imgseries = nb.load(self.inputs.realigned_file, mmap=NUMPY_MMAP)
405407

406408
if len(imgseries.shape) != 4:
407409
raise ValueError('{} expected a 4-D nifti file. Input {} has '
@@ -435,20 +437,22 @@ def _run_interface(self, runtime):
435437
'{} cannot detect repetition time from image - '
436438
'Set the repetition_time input'.format(self._header))
437439

438-
components, hpf_basis = compute_noise_components(
439-
imgseries.get_data(), mask_images, degree,
440-
self.inputs.num_components, self.inputs.high_pass_filter,
441-
self.inputs.high_pass_cutoff, TR)
440+
components, filter_basis = compute_noise_components(
441+
imgseries.get_data(), mask_images, self.inputs.num_components,
442+
self.inputs.pre_filter, degree, self.inputs.high_pass_cutoff, TR)
442443

443444
components_file = os.path.join(os.getcwd(), self.inputs.components_file)
444445
np.savetxt(components_file, components, fmt=b"%.10f", delimiter='\t',
445446
header=self._make_headers(components.shape[1]), comments='')
446447

447-
if self.inputs.save_hpf_basis:
448-
hpf_basis_file = self._list_outputs()['hpf_basis_file']
449-
header = ['cos{:02d}'.format(i) for i in range(hpf_basis.shape[1])]
450-
np.savetxt(hpf_basis_file, hpf_basis, fmt=b'%.10f', delimiter='\t',
451-
header='\t'.join(header), comments='')
448+
if self.inputs.pre_filter and self.inputs.save_pre_filter:
449+
pre_filter_file = self._list_outputs()['pre_filter_file']
450+
ftype = {'polynomial': 'poly',
451+
'cosine': 'cos'}[self.inputs.pre_filter]
452+
header = ['{}{:02d}'.format(ftype, i)
453+
for i in range(filter_basis.shape[1])]
454+
np.savetxt(pre_filter_file, filter_basis, fmt=b'%.10f',
455+
delimiter='\t', header='\t'.join(header), comments='')
452456

453457
return runtime
454458

@@ -518,7 +522,7 @@ class TCompCor(CompCor):
518522
>>> ccinterface.inputs.realigned_file = 'functional.nii'
519523
>>> ccinterface.inputs.mask_files = 'mask.nii'
520524
>>> ccinterface.inputs.num_components = 1
521-
>>> ccinterface.inputs.use_regress_poly = True
525+
>>> ccinterface.inputs.pre_filter = 'polynomial'
522526
>>> ccinterface.inputs.regress_poly_degree = 2
523527
>>> ccinterface.inputs.percentile_threshold = .03
524528
@@ -883,6 +887,8 @@ def regress_poly(degree, data, remove_mean=True, axis=-1):
883887
value_array = np.linspace(-1, 1, timepoints)
884888
X = np.hstack((X, polynomial_func(value_array)[:, np.newaxis]))
885889

890+
non_constant_regressors = X[:, :-1] if X.shape[1] > 1 else np.array([])
891+
886892
# Calculate coefficients
887893
betas = np.linalg.pinv(X).dot(data.T)
888894

@@ -894,7 +900,7 @@ def regress_poly(degree, data, remove_mean=True, axis=-1):
894900
regressed_data = data - datahat
895901

896902
# Back to original shape
897-
return regressed_data.reshape(datashape)
903+
return regressed_data.reshape(datashape), non_constant_regressors
898904

899905

900906
def combine_mask_files(mask_files, mask_method=None, mask_index=None):
@@ -952,9 +958,9 @@ def combine_mask_files(mask_files, mask_method=None, mask_index=None):
952958
return [img]
953959

954960

955-
def compute_noise_components(imgseries, mask_images, degree, num_components,
956-
high_pass_filter=False, period_cut=128,
957-
repetition_time=0):
961+
def compute_noise_components(imgseries, mask_images, num_components,
962+
filter_type, degree, period_cut,
963+
repetition_time):
958964
"""Compute the noise components from the imgseries for each mask
959965
960966
imgseries: a nibabel img
@@ -972,7 +978,7 @@ def compute_noise_components(imgseries, mask_images, degree, num_components,
972978
973979
"""
974980
components = None
975-
hpf_basis = None
981+
basis = None
976982
for img in mask_images:
977983
mask = img.get_data().astype(np.bool)
978984
if imgseries.shape[:3] != mask.shape:
@@ -986,15 +992,16 @@ def compute_noise_components(imgseries, mask_images, degree, num_components,
986992
# Zero-out any bad values
987993
voxel_timecourses[np.isnan(np.sum(voxel_timecourses, axis=1)), :] = 0
988994

989-
# Use either cosine or Legendre-polynomial detrending
990-
if high_pass_filter:
991-
voxel_timecourses, hpf_basis = cosine_filter(
995+
# Currently support Legendre-polynomial or cosine or detrending
996+
# With no filter, the mean is nonetheless removed (poly w/ degree 0)
997+
if filter_type == 'cosine':
998+
voxel_timecourses, basis = cosine_filter(
992999
voxel_timecourses, repetition_time, period_cut)
993-
else:
1000+
elif filter_type in ('polynomial', False):
9941001
# from paper:
9951002
# "The constant and linear trends of the columns in the matrix M were
9961003
# removed [prior to ...]"
997-
voxel_timecourses = regress_poly(degree, voxel_timecourses)
1004+
voxel_timecourses, basis = regress_poly(degree, voxel_timecourses)
9981005

9991006
# "Voxel time series from the noise ROI (either anatomical or tSTD) were
10001007
# placed in a matrix M of size Nxm, with time along the row dimension
@@ -1014,7 +1021,7 @@ def compute_noise_components(imgseries, mask_images, degree, num_components,
10141021
u[:, :num_components]))
10151022
if components is None and num_components > 0:
10161023
raise ValueError('No components found')
1017-
return components, hpf_basis
1024+
return components, basis
10181025

10191026

10201027
def _compute_tSTD(M, x, axis=0):

0 commit comments

Comments
 (0)