@@ -52,6 +52,16 @@ class ComputeDVARSInputSpec(BaseInterfaceInputSpec):
5252 desc = 'output figure size' )
5353 figformat = traits .Enum ('png' , 'pdf' , 'svg' , usedefault = True ,
5454 desc = 'output format for figures' )
55+ intensity_normalization = traits .Float (1000.0 , usedefault = True ,
56+ desc = 'Divide value in each voxel at each timepoint '
57+ 'by the median calculated across all voxels'
58+ 'and timepoints within the mask (if specified)'
59+ 'and then multiply by the value specified by'
60+ 'this parameter. By using the default (1000)' \
61+ 'output DVARS will be expressed in ' \
62+ 'x10 % BOLD units compatible with Power et al.' \
63+ '2012. Set this to 0 to disable intensity' \
64+ 'normalization altogether.' )
5565
5666
5767
@@ -128,7 +138,8 @@ def _gen_fname(self, suffix, ext=None):
128138
129139 def _run_interface (self , runtime ):
130140 dvars = compute_dvars (self .inputs .in_file , self .inputs .in_mask ,
131- remove_zerovariance = self .inputs .remove_zerovariance )
141+ remove_zerovariance = self .inputs .remove_zerovariance ,
142+ intensity_normalization = self .inputs .intensity_normalization )
132143
133144 (self ._results ['avg_std' ],
134145 self ._results ['avg_nstd' ],
@@ -595,7 +606,8 @@ def regress_poly(degree, data, remove_mean=True, axis=-1):
595606 # Back to original shape
596607 return regressed_data .reshape (datashape )
597608
598- def compute_dvars (in_file , in_mask , remove_zerovariance = False ):
609+ def compute_dvars (in_file , in_mask , remove_zerovariance = False ,
610+ intensity_normalization = 1000 ):
599611 """
600612 Compute the :abbr:`DVARS (D referring to temporal
601613 derivative of timecourses, VARS referring to RMS variance over voxels)`
@@ -636,59 +648,49 @@ def compute_dvars(in_file, in_mask, remove_zerovariance=False):
636648 raise RuntimeError (
637649 "Input fMRI dataset should be 4-dimensional" )
638650
639- # Robust standard deviation
640- func_sd = (np .percentile (func , 75 , axis = 3 ) -
641- np .percentile (func , 25 , axis = 3 )) / 1.349
642- func_sd [mask <= 0 ] = 0
643-
644- if remove_zerovariance :
645- # Remove zero-variance voxels across time axis
646- mask = zero_remove (func_sd , mask )
647-
648651 idx = np .where (mask > 0 )
649652 mfunc = func [idx [0 ], idx [1 ], idx [2 ], :]
650653
651- # Demean
652- mfunc = regress_poly (0 , mfunc , remove_mean = True ).astype (np .float32 )
654+ if intensity_normalization != 0 :
655+ mfunc = (mfunc / np .median (mfunc )) * intensity_normalization
656+
657+ # Robust standard deviation (we are using "lower" interpolation
658+ # because this is what FSL is doing
659+ func_sd = (np .percentile (mfunc , 75 , axis = 1 , interpolation = "lower" ) -
660+ np .percentile (mfunc , 25 , axis = 1 , interpolation = "lower" )) / 1.349
661+
662+ if remove_zerovariance :
663+ mfunc = mfunc [func_sd != 0 , :]
664+ func_sd = func_sd [func_sd != 0 ]
653665
654666 # Compute (non-robust) estimate of lag-1 autocorrelation
655- ar1 = np .apply_along_axis (AR_est_YW , 1 , mfunc , 1 )[:, 0 ]
667+ ar1 = np .apply_along_axis (AR_est_YW , 1 ,
668+ regress_poly (0 , mfunc , remove_mean = True ).astype (
669+ np .float32 ), 1 )[:, 0 ]
656670
657671 # Compute (predicted) standard deviation of temporal difference time series
658- diff_sdhat = np .squeeze (np .sqrt (((1 - ar1 ) * 2 ).tolist ())) * func_sd [ mask > 0 ]. reshape ( - 1 )
672+ diff_sdhat = np .squeeze (np .sqrt (((1 - ar1 ) * 2 ).tolist ())) * func_sd
659673 diff_sd_mean = diff_sdhat .mean ()
660674
661675 # Compute temporal difference time series
662676 func_diff = np .diff (mfunc , axis = 1 )
663677
664678 # DVARS (no standardization)
665- dvars_nstd = func_diff . std (axis = 0 )
679+ dvars_nstd = np . sqrt ( np . square ( func_diff ). mean (axis = 0 ) )
666680
667681 # standardization
668682 dvars_stdz = dvars_nstd / diff_sd_mean
669683
670- with warnings .catch_warnings (): # catch, e.g., divide by zero errors
684+ with warnings .catch_warnings (): # catch, e.g., divide by zero errors
671685 warnings .filterwarnings ('error' )
672686
673687 # voxelwise standardization
674- diff_vx_stdz = func_diff / np .array ([diff_sdhat ] * func_diff .shape [- 1 ]).T
675- dvars_vx_stdz = diff_vx_stdz .std (axis = 0 , ddof = 1 )
688+ diff_vx_stdz = np .square (
689+ func_diff / np .array ([diff_sdhat ] * func_diff .shape [- 1 ]).T )
690+ dvars_vx_stdz = np .sqrt (diff_vx_stdz .mean (axis = 0 ))
676691
677692 return (dvars_stdz , dvars_nstd , dvars_vx_stdz )
678693
679- def zero_remove (data , mask ):
680- """
681- Modify inputted mask to also mask out zero values
682-
683- :param numpy.ndarray data: e.g. voxelwise stddev of fMRI dataset, after motion correction
684- :param numpy.ndarray mask: brain mask (same dimensions as data)
685- :return: the mask with any additional zero voxels removed (same dimensions as inputs)
686- :rtype: numpy.ndarray
687-
688- """
689- new_mask = mask .copy ()
690- new_mask [data == 0 ] = 0
691- return new_mask
692694
693695def plot_confound (tseries , figsize , name , units = None ,
694696 series_tr = None , normalize = False ):
0 commit comments