def test__smooth_array(): """Test smoothing of images: _smooth_array()""" # Impulse in 3D data = np.zeros((40, 41, 42)) data[20, 20, 20] = 1 # fwhm divided by any test affine must be odd. Otherwise assertion below # will fail. ( 9 / 0.6 = 15 is fine) fwhm = 9 test_affines = (np.eye(4), np.diag((1, 1, -1, 1)), np.diag((.6, 1, .6, 1))) for affine in test_affines: filtered = image._smooth_array(data, affine, fwhm=fwhm, copy=True) assert_false(np.may_share_memory(filtered, data)) # We are expecting a full-width at half maximum of # fwhm / voxel_size: vmax = filtered.max() above_half_max = filtered > .5 * vmax for axis in (0, 1, 2): proj = np.any(np.any(np.rollaxis(above_half_max, axis=axis), axis=-1), axis=-1) np.testing.assert_equal(proj.sum(), fwhm / np.abs(affine[axis, axis])) # Check that NaNs in the data do not propagate data[10, 10, 10] = np.NaN filtered = image._smooth_array(data, affine, fwhm=fwhm, ensure_finite=True, copy=True) assert_true(np.all(np.isfinite(filtered))) # Check copy=False. for affine in test_affines: data = np.zeros((40, 41, 42)) data[20, 20, 20] = 1 image._smooth_array(data, affine, fwhm=fwhm, copy=False) # We are expecting a full-width at half maximum of # fwhm / voxel_size: vmax = data.max() above_half_max = data > .5 * vmax for axis in (0, 1, 2): proj = np.any(np.any(np.rollaxis(above_half_max, axis=axis), axis=-1), axis=-1) np.testing.assert_equal(proj.sum(), fwhm / np.abs(affine[axis, axis])) # Check fwhm='fast' for affine in test_affines: np.testing.assert_equal(image._smooth_array(data, affine, fwhm='fast'), image._fast_smooth_array(data))
def test__fast_smooth_array(): N = 4 shape = (N, N, N) # hardcoded in _fast_smooth_array neighbor_weight = 0.2 # 6 neighbors in 3D if you are not on an edge nb_neighbors_max = 6 data = np.ones(shape) smooth_data = image._fast_smooth_array(data) # this contains the number of neighbors for each cell in the array nb_neighbors_arr = np.empty(shape) for (i, j, k), __ in np.ndenumerate(nb_neighbors_arr): nb_neighbors_arr[i, j, k] = (3 + (0 < i < N - 1) + (0 < j < N - 1) + (0 < k < N - 1)) expected = ((1 + neighbor_weight * nb_neighbors_arr) / (1 + neighbor_weight * nb_neighbors_max)) np.testing.assert_allclose(smooth_data, expected)
def _smooth_array(arr, affine, fwhm=None, ensure_finite=True, copy=True, **kwargs): """Smooth images by applying a Gaussian filter. Apply a Gaussian filter along the three first dimensions of arr. This is copied and slightly modified from nilearn: https://github.com/nilearn/nilearn/blob/master/nilearn/image/image.py Added the **kwargs argument. Parameters ========== arr: numpy.ndarray 4D array, with image number as last dimension. 3D arrays are also accepted. affine: numpy.ndarray (4, 4) matrix, giving affine transformation for image. (3, 3) matrices are also accepted (only these coefficients are used). If fwhm='fast', the affine is not used and can be None fwhm: scalar, numpy.ndarray, 'fast' or None Smoothing strength, as a full-width at half maximum, in millimeters. If a scalar is given, width is identical on all three directions. A numpy.ndarray must have 3 elements, giving the FWHM along each axis. If fwhm == 'fast', a fast smoothing will be performed with a filter [0.2, 1, 0.2] in each direction and a normalisation to preserve the local average value. If fwhm is None, no filtering is performed (useful when just removal of non-finite values is needed). ensure_finite: bool if True, replace every non-finite values (like NaNs) by zero before filtering. copy: bool if True, input array is not modified. False by default: the filtering is performed in-place. kwargs: keyword-arguments Arguments for the ndimage.gaussian_filter1d function. Returns ======= filtered_arr: numpy.ndarray arr, filtered. Notes ===== This function is most efficient with arr in C order. """ if arr.dtype.kind == 'i': if arr.dtype == np.int64: arr = arr.astype(np.float64) else: # We don't need crazy precision arr = arr.astype(np.float32) if copy: arr = arr.copy() if ensure_finite: # SPM tends to put NaNs in the data outside the brain arr[np.logical_not(np.isfinite(arr))] = 0 if fwhm == 'fast': arr = _fast_smooth_array(arr) elif fwhm is not None: # Keep only the scale part. affine = affine[:3, :3] # Convert from a FWHM to a sigma: fwhm_over_sigma_ratio = np.sqrt(8 * np.log(2)) vox_size = np.sqrt(np.sum(affine ** 2, axis=0)) sigma = fwhm / (fwhm_over_sigma_ratio * vox_size) for n, s in enumerate(sigma): ndimage.gaussian_filter1d(arr, s, output=arr, axis=n, **kwargs) return arr