From 64e176bb6067111367f0d7bac22c7ed096dd4c31 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 11 Jul 2022 20:08:00 -0400 Subject: [PATCH 1/4] FIX: Add variance tolerance parameter to ComputeDVARS --- nipype/algorithms/confounds.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/nipype/algorithms/confounds.py b/nipype/algorithms/confounds.py index 82566c07d4..4e87a859c9 100644 --- a/nipype/algorithms/confounds.py +++ b/nipype/algorithms/confounds.py @@ -50,6 +50,11 @@ class ComputeDVARSInputSpec(BaseInterfaceInputSpec): remove_zerovariance = traits.Bool( True, usedefault=True, desc="remove voxels with zero variance" ) + variance_tol = traits.Float( + 1e-7, + usedefault=True, + desc="maximum variance to consider \"close to\" zero for the purposes of removal", + ) save_std = traits.Bool(True, usedefault=True, desc="save standardized DVARS") save_nstd = traits.Bool(False, usedefault=True, desc="save non-standardized DVARS") save_vxstd = traits.Bool( @@ -167,6 +172,7 @@ def _run_interface(self, runtime): self.inputs.in_file, self.inputs.in_mask, remove_zerovariance=self.inputs.remove_zerovariance, + variance_tol=self.inputs.variance_tol, intensity_normalization=self.inputs.intensity_normalization, ) @@ -995,7 +1001,11 @@ def _list_outputs(self): def compute_dvars( - in_file, in_mask, remove_zerovariance=False, intensity_normalization=1000 + in_file, + in_mask, + remove_zerovariance=False, + variance_tol=0.0, + intensity_normalization=1000, ): """ Compute the :abbr:`DVARS (D referring to temporal @@ -1050,8 +1060,9 @@ def compute_dvars( ) / 1.349 if remove_zerovariance: - mfunc = mfunc[func_sd != 0, :] - func_sd = func_sd[func_sd != 0] + zero_variance_voxels = func_sd > self.inputs.variance_tol + mfunc = mfunc[zero_variance_voxels, :] + func_sd = func_sd[zero_variance_voxels] # Compute (non-robust) estimate of lag-1 autocorrelation ar1 = np.apply_along_axis( From 75a78e069bb7305e05786124971509418ccfed08 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 11 Jul 2022 20:24:33 -0400 Subject: [PATCH 2/4] RF: Modernize compute_dvars Directly coerce image dataobj attributes, and mask idiomatically Replace interpolation with method in np.percentile (interpolation deprecated) Call AR_est_YW indirectly to avoid coercion of tuple(array, float) --- nipype/algorithms/confounds.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/nipype/algorithms/confounds.py b/nipype/algorithms/confounds.py index 4e87a859c9..51ac2c5a5e 100644 --- a/nipype/algorithms/confounds.py +++ b/nipype/algorithms/confounds.py @@ -1000,6 +1000,13 @@ def _list_outputs(self): return self._results +def _AR_est_YW(x, order, rxx=None): + """Retrieve AR coefficients while dropping the sig_sq return value""" + from nitime.algorithms import AR_est_YW + + return AR_est_YW(x, order, rxx=rxx)[0] + + def compute_dvars( in_file, in_mask, @@ -1037,17 +1044,15 @@ def compute_dvars( """ import numpy as np import nibabel as nb - from nitime.algorithms import AR_est_YW import warnings - func = nb.load(in_file).get_fdata(dtype=np.float32) - mask = np.asanyarray(nb.load(in_mask).dataobj).astype(np.uint8) + func = np.float32(nb.load(in_file).dataobj) + mask = np.bool_(nb.load(in_mask).dataobj) if len(func.shape) != 4: raise RuntimeError("Input fMRI dataset should be 4-dimensional") - idx = np.where(mask > 0) - mfunc = func[idx[0], idx[1], idx[2], :] + mfunc = func[mask] if intensity_normalization != 0: mfunc = (mfunc / np.median(mfunc)) * intensity_normalization @@ -1055,19 +1060,19 @@ def compute_dvars( # Robust standard deviation (we are using "lower" interpolation # because this is what FSL is doing func_sd = ( - np.percentile(mfunc, 75, axis=1, interpolation="lower") - - np.percentile(mfunc, 25, axis=1, interpolation="lower") + np.percentile(mfunc, 75, axis=1, method="lower") + - np.percentile(mfunc, 25, axis=1, method="lower") ) / 1.349 if remove_zerovariance: - zero_variance_voxels = func_sd > self.inputs.variance_tol + zero_variance_voxels = func_sd > variance_tol mfunc = mfunc[zero_variance_voxels, :] func_sd = func_sd[zero_variance_voxels] # Compute (non-robust) estimate of lag-1 autocorrelation ar1 = np.apply_along_axis( - AR_est_YW, 1, regress_poly(0, mfunc, remove_mean=True)[0].astype(np.float32), 1 - )[:, 0] + _AR_est_YW, 1, regress_poly(0, mfunc, remove_mean=True)[0].astype(np.float32), 1 + ) # Compute (predicted) standard deviation of temporal difference time series diff_sdhat = np.squeeze(np.sqrt(((1 - ar1) * 2).tolist())) * func_sd From 53afcd48498eb79055b8a757860a638df8bb07b2 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 11 Jul 2022 20:35:20 -0400 Subject: [PATCH 3/4] TEST: make specs --- nipype/algorithms/tests/test_auto_ComputeDVARS.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nipype/algorithms/tests/test_auto_ComputeDVARS.py b/nipype/algorithms/tests/test_auto_ComputeDVARS.py index 5fe2d241b9..c5e1118341 100644 --- a/nipype/algorithms/tests/test_auto_ComputeDVARS.py +++ b/nipype/algorithms/tests/test_auto_ComputeDVARS.py @@ -43,6 +43,9 @@ def test_ComputeDVARS_inputs(): usedefault=True, ), series_tr=dict(), + variance_tol=dict( + usedefault=True, + ), ) inputs = ComputeDVARS.input_spec() From 693bff76620fbdfc2e1e41b416470730267a87e6 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 12 Jul 2022 08:30:22 -0400 Subject: [PATCH 4/4] Update nipype/algorithms/confounds.py --- nipype/algorithms/confounds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/algorithms/confounds.py b/nipype/algorithms/confounds.py index 51ac2c5a5e..63dc3def2a 100644 --- a/nipype/algorithms/confounds.py +++ b/nipype/algorithms/confounds.py @@ -1011,8 +1011,8 @@ def compute_dvars( in_file, in_mask, remove_zerovariance=False, - variance_tol=0.0, intensity_normalization=1000, + variance_tol=0.0, ): """ Compute the :abbr:`DVARS (D referring to temporal