@@ -325,33 +325,32 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
325
325
components_file = traits .Str ('components_file.txt' , usedefault = True ,
326
326
desc = 'Filename to store physiological components' )
327
327
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' ,
329
333
desc = ('use polynomial regression '
330
334
'pre-component extraction' ))
331
335
regress_poly_degree = traits .Range (low = 1 , default = 1 , usedefault = True ,
332
336
desc = 'the degree polynomial to use' )
333
337
header_prefix = traits .Str (desc = ('the desired header for the output tsv '
334
338
'file (one column). If undefined, will '
335
339
'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' )
340
340
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' )
343
343
repetition_time = traits .Float (
344
344
desc = 'Repetition time (TR) of series - derived from image header if '
345
345
'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' )
349
348
350
349
351
350
class CompCorOutputSpec (TraitedSpec ):
352
351
components_file = File (exists = True ,
353
352
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' )
355
354
356
355
357
356
class CompCor (BaseInterface ):
@@ -397,11 +396,14 @@ def _run_interface(self, runtime):
397
396
self .inputs .merge_method ,
398
397
self .inputs .mask_index )
399
398
399
+ if self .inputs .use_regress_poly :
400
+ self .inputs .pre_filter = 'polynomial'
401
+
402
+ # Degree 0 == remove mean; see compute_noise_components
400
403
degree = (self .inputs .regress_poly_degree if
401
- self .inputs .use_regress_poly else 0 )
404
+ self .inputs .pre_filter == 'polynomial' else 0 )
402
405
403
- imgseries = nb .load (self .inputs .realigned_file ,
404
- mmap = NUMPY_MMAP )
406
+ imgseries = nb .load (self .inputs .realigned_file , mmap = NUMPY_MMAP )
405
407
406
408
if len (imgseries .shape ) != 4 :
407
409
raise ValueError ('{} expected a 4-D nifti file. Input {} has '
@@ -435,20 +437,22 @@ def _run_interface(self, runtime):
435
437
'{} cannot detect repetition time from image - '
436
438
'Set the repetition_time input' .format (self ._header ))
437
439
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 )
442
443
443
444
components_file = os .path .join (os .getcwd (), self .inputs .components_file )
444
445
np .savetxt (components_file , components , fmt = b"%.10f" , delimiter = '\t ' ,
445
446
header = self ._make_headers (components .shape [1 ]), comments = '' )
446
447
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 = '' )
452
456
453
457
return runtime
454
458
@@ -518,7 +522,7 @@ class TCompCor(CompCor):
518
522
>>> ccinterface.inputs.realigned_file = 'functional.nii'
519
523
>>> ccinterface.inputs.mask_files = 'mask.nii'
520
524
>>> ccinterface.inputs.num_components = 1
521
- >>> ccinterface.inputs.use_regress_poly = True
525
+ >>> ccinterface.inputs.pre_filter = 'polynomial'
522
526
>>> ccinterface.inputs.regress_poly_degree = 2
523
527
>>> ccinterface.inputs.percentile_threshold = .03
524
528
@@ -883,6 +887,8 @@ def regress_poly(degree, data, remove_mean=True, axis=-1):
883
887
value_array = np .linspace (- 1 , 1 , timepoints )
884
888
X = np .hstack ((X , polynomial_func (value_array )[:, np .newaxis ]))
885
889
890
+ non_constant_regressors = X [:, :- 1 ] if X .shape [1 ] > 1 else np .array ([])
891
+
886
892
# Calculate coefficients
887
893
betas = np .linalg .pinv (X ).dot (data .T )
888
894
@@ -894,7 +900,7 @@ def regress_poly(degree, data, remove_mean=True, axis=-1):
894
900
regressed_data = data - datahat
895
901
896
902
# Back to original shape
897
- return regressed_data .reshape (datashape )
903
+ return regressed_data .reshape (datashape ), non_constant_regressors
898
904
899
905
900
906
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):
952
958
return [img ]
953
959
954
960
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 ):
958
964
"""Compute the noise components from the imgseries for each mask
959
965
960
966
imgseries: a nibabel img
@@ -972,7 +978,7 @@ def compute_noise_components(imgseries, mask_images, degree, num_components,
972
978
973
979
"""
974
980
components = None
975
- hpf_basis = None
981
+ basis = None
976
982
for img in mask_images :
977
983
mask = img .get_data ().astype (np .bool )
978
984
if imgseries .shape [:3 ] != mask .shape :
@@ -986,15 +992,16 @@ def compute_noise_components(imgseries, mask_images, degree, num_components,
986
992
# Zero-out any bad values
987
993
voxel_timecourses [np .isnan (np .sum (voxel_timecourses , axis = 1 )), :] = 0
988
994
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 (
992
999
voxel_timecourses , repetition_time , period_cut )
993
- else :
1000
+ elif filter_type in ( 'polynomial' , False ) :
994
1001
# from paper:
995
1002
# "The constant and linear trends of the columns in the matrix M were
996
1003
# removed [prior to ...]"
997
- voxel_timecourses = regress_poly (degree , voxel_timecourses )
1004
+ voxel_timecourses , basis = regress_poly (degree , voxel_timecourses )
998
1005
999
1006
# "Voxel time series from the noise ROI (either anatomical or tSTD) were
1000
1007
# 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,
1014
1021
u [:, :num_components ]))
1015
1022
if components is None and num_components > 0 :
1016
1023
raise ValueError ('No components found' )
1017
- return components , hpf_basis
1024
+ return components , basis
1018
1025
1019
1026
1020
1027
def _compute_tSTD (M , x , axis = 0 ):
0 commit comments