3232class ComputeDVARSInputSpec (BaseInterfaceInputSpec ):
3333 in_file = File (exists = True , mandatory = True , desc = 'functional data, after HMC' )
3434 in_mask = File (exists = True , mandatory = True , desc = 'a brain mask' )
35- remove_zerovariance = traits .Bool (False , usedefault = True ,
35+ remove_zerovariance = traits .Bool (True , usedefault = True ,
3636 desc = 'remove voxels with zero variance' )
3737 save_std = traits .Bool (True , usedefault = True ,
3838 desc = 'save standardized DVARS' )
@@ -255,7 +255,7 @@ def _run_interface(self, runtime):
255255 'out_file' : op .abspath (self .inputs .out_file ),
256256 'fd_average' : float (fd_res .mean ())
257257 }
258- np .savetxt (self .inputs .out_file , fd_res )
258+ np .savetxt (self .inputs .out_file , fd_res , header = 'framewise_displacement' )
259259
260260 if self .inputs .save_plot :
261261 tr = None
@@ -291,6 +291,8 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
291291 'pre-component extraction' )
292292 regress_poly_degree = traits .Range (low = 1 , default = 1 , usedefault = True ,
293293 desc = 'the degree polynomial to use' )
294+ header = traits .Str (desc = 'the desired header for the output tsv file (one column).'
295+ 'If undefined, will default to "CompCor"' )
294296
295297class CompCorOutputSpec (TraitedSpec ):
296298 components_file = File (exists = True ,
@@ -329,6 +331,13 @@ class CompCor(BaseInterface):
329331 def _run_interface (self , runtime ):
330332 imgseries = nb .load (self .inputs .realigned_file ).get_data ()
331333 mask = nb .load (self .inputs .mask_file ).get_data ()
334+
335+ if imgseries .shape [:3 ] != mask .shape :
336+ raise ValueError ('Inputs for CompCor, func {} and mask {}, do not have matching '
337+ 'spatial dimensions ({} and {}, respectively)'
338+ .format (self .inputs .realigned_file , self .inputs .mask_file ,
339+ imgseries .shape [:3 ], mask .shape ))
340+
332341 voxel_timecourses = imgseries [mask > 0 ]
333342 # Zero-out any bad values
334343 voxel_timecourses [np .isnan (np .sum (voxel_timecourses , axis = 1 )), :] = 0
@@ -352,7 +361,10 @@ def _run_interface(self, runtime):
352361 u , _ , _ = linalg .svd (M , full_matrices = False )
353362 components = u [:, :self .inputs .num_components ]
354363 components_file = os .path .join (os .getcwd (), self .inputs .components_file )
355- np .savetxt (components_file , components , fmt = b"%.10f" )
364+
365+ self ._set_header ()
366+ np .savetxt (components_file , components , fmt = b"%.10f" , delimiter = '\t ' ,
367+ header = self ._make_headers (components .shape [1 ]))
356368 return runtime
357369
358370 def _list_outputs (self ):
@@ -367,6 +379,26 @@ def _compute_tSTD(self, M, x):
367379 stdM [np .isnan (stdM )] = x
368380 return stdM
369381
382+ def _set_header (self , header = 'CompCor' ):
383+ self .inputs .header = self .inputs .header if isdefined (self .inputs .header ) else header
384+
385+ def _make_headers (self , num_col ):
386+ headers = []
387+ for i in range (num_col ):
388+ headers .append (self .inputs .header + str (i ))
389+ return '\t ' .join (headers )
390+
391+
392+ class ACompCor (CompCor ):
393+ ''' Anatomical compcor; for input/output, see CompCor.
394+ If the mask provided is an anatomical mask, CompCor == ACompCor '''
395+
396+ def __init__ (self , * args , ** kwargs ):
397+ ''' exactly the same as compcor except the header '''
398+ super (ACompCor , self ).__init__ (* args , ** kwargs )
399+ self ._set_header ('aCompCor' )
400+
401+
370402class TCompCorInputSpec (CompCorInputSpec ):
371403 # and all the fields in CompCorInputSpec
372404 percentile_threshold = traits .Range (low = 0. , high = 1. , value = .02 ,
@@ -401,6 +433,11 @@ class TCompCor(CompCor):
401433 def _run_interface (self , runtime ):
402434 imgseries = nb .load (self .inputs .realigned_file ).get_data ()
403435
436+ if imgseries .ndim != 4 :
437+ raise ValueError ('tCompCor expected a 4-D nifti file. Input {} has {} dimensions '
438+ '(shape {})'
439+ .format (self .inputs .realigned_file , imgseries .ndim , imgseries .shape ))
440+
404441 # From the paper:
405442 # "For each voxel time series, the temporal standard deviation is
406443 # defined as the standard deviation of the time series after the removal
@@ -419,18 +456,19 @@ def _run_interface(self, runtime):
419456 threshold_index = int (num_voxels * (1. - self .inputs .percentile_threshold ))
420457 threshold_std = sortSTD [threshold_index ]
421458 mask = tSTD >= threshold_std
422- mask = mask .astype (int )
459+ mask = mask .astype (int ). T
423460
424461 # save mask
425- mask_file = 'mask.nii'
462+ mask_file = os . path . abspath ( 'mask.nii' )
426463 nb .nifti1 .save (nb .Nifti1Image (mask , np .eye (4 )), mask_file )
464+ IFLOG .debug ('tCompcor computed and saved mask of shape {} to mask_file {}'
465+ .format (mask .shape , mask_file ))
427466 self .inputs .mask_file = mask_file
467+ self ._set_header ('tCompCor' )
428468
429469 super (TCompCor , self )._run_interface (runtime )
430470 return runtime
431471
432- ACompCor = CompCor
433-
434472class TSNRInputSpec (BaseInterfaceInputSpec ):
435473 in_file = InputMultiPath (File (exists = True ), mandatory = True ,
436474 desc = 'realigned 4D file or a list of 3D files' )
@@ -512,6 +550,8 @@ def regress_poly(degree, data, remove_mean=True, axis=-1):
512550 If remove_mean is True (default), the data is demeaned (i.e. degree 0).
513551 If remove_mean is false, the data is not.
514552 '''
553+ IFLOG .debug ('Performing polynomial regression on data of shape ' + str (data .shape ))
554+
515555 datashape = data .shape
516556 timepoints = datashape [axis ]
517557
@@ -570,6 +610,7 @@ def compute_dvars(in_file, in_mask, remove_zerovariance=False):
570610 import numpy as np
571611 import nibabel as nb
572612 from nitime .algorithms import AR_est_YW
613+ import warnings
573614
574615 func = nb .load (in_file ).get_data ().astype (np .float32 )
575616 mask = nb .load (in_mask ).get_data ().astype (np .uint8 )
@@ -585,7 +626,7 @@ def compute_dvars(in_file, in_mask, remove_zerovariance=False):
585626
586627 if remove_zerovariance :
587628 # Remove zero-variance voxels across time axis
588- mask = zero_variance ( func , mask )
629+ mask = zero_remove ( func_sd , mask )
589630
590631 idx = np .where (mask > 0 )
591632 mfunc = func [idx [0 ], idx [1 ], idx [2 ], :]
@@ -609,31 +650,28 @@ def compute_dvars(in_file, in_mask, remove_zerovariance=False):
609650 # standardization
610651 dvars_stdz = dvars_nstd / diff_sd_mean
611652
612- # voxelwise standardization
613- diff_vx_stdz = func_diff / np .array ([diff_sdhat ] * func_diff .shape [- 1 ]).T
614- dvars_vx_stdz = diff_vx_stdz .std (axis = 0 , ddof = 1 )
653+ with warnings .catch_warnings (): # catch, e.g., divide by zero errors
654+ warnings .filterwarnings ('error' )
655+
656+ # voxelwise standardization
657+ diff_vx_stdz = func_diff / np .array ([diff_sdhat ] * func_diff .shape [- 1 ]).T
658+ dvars_vx_stdz = diff_vx_stdz .std (axis = 0 , ddof = 1 )
615659
616660 return (dvars_stdz , dvars_nstd , dvars_vx_stdz )
617661
618- def zero_variance ( func , mask ):
662+ def zero_remove ( data , mask ):
619663 """
620- Mask out voxels with zero variance across t-axis
664+ Modify inputted mask to also mask out zero values
621665
622- :param numpy.ndarray func: input fMRI dataset, after motion correction
623- :param numpy.ndarray mask: 3D brain mask
624- :return: the 3D mask of voxels with nonzero variance across :math:`t`.
666+ :param numpy.ndarray data: e.g. voxelwise stddev of fMRI dataset, after motion correction
667+ :param numpy.ndarray mask: brain mask (same dimensions as data)
668+ :return: the mask with any additional zero voxels removed (same dimensions as inputs)
625669 :rtype: numpy.ndarray
626670
627671 """
628- idx = np .where (mask > 0 )
629- func = func [idx [0 ], idx [1 ], idx [2 ], :]
630- tvariance = func .var (axis = 1 )
631- tv_mask = np .zeros_like (tvariance , dtype = np .uint8 )
632- tv_mask [tvariance > 0 ] = 1
633-
634- newmask = np .zeros_like (mask , dtype = np .uint8 )
635- newmask [idx ] = tv_mask
636- return newmask
672+ new_mask = mask .copy ()
673+ new_mask [data == 0 ] = 0
674+ return new_mask
637675
638676def plot_confound (tseries , figsize , name , units = None ,
639677 series_tr = None , normalize = False ):
0 commit comments