def test_mppca_returned_sigma(): DWIgt = rfiw_phantom(gtab, snr=None) std_gt = 0.02 noise = std_gt * np.random.standard_normal(DWIgt.shape) DWInoise = DWIgt + noise # Case that sigma is estimated using mpPCA DWIden0, sigma = mppca(DWInoise, patch_radius=2, return_sigma=True) msigma = np.mean(sigma) std_error = abs(msigma - std_gt) / std_gt * 100 assert_(std_error < 5) # Case that sigma is inputed (sigma outputed should be the same as the one # inputed) DWIden1, rsigma = genpca(DWInoise, sigma=sigma, tau_factor=None, patch_radius=2, return_sigma=True) assert_array_almost_equal(rsigma, sigma) # DWIden1 should be very similar to DWIden0 rmse_den = np.sum(np.abs(DWIden1 - DWIden0)) / np.sum(np.abs(DWIden0)) rmse_ref = np.sum(np.abs(DWIden1 - DWIgt)) / np.sum(np.abs(DWIgt)) assert_(rmse_den < rmse_ref)
def run(self, input_files, patch_radius=2, pca_method='eig', return_sigma=False, out_dir='', out_denoised='dwi_mppca.nii.gz', out_sigma='dwi_sigma.nii.gz'): r"""Workflow wrapping Marcenko-Pastur PCA denoising method. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. patch_radius : variable int, optional The radius of the local patch to be taken around each voxel (in voxels) For example, for a patch radius with value 2, and assuming the input image is a 3D image, the denoising will take place in blocks of 5x5x5 voxels. pca_method : string, optional Use either eigenvalue decomposition ('eig') or singular value decomposition ('svd') for principal component analysis. The default method is 'eig' which is faster. However, occasionally 'svd' might be more accurate. return_sigma : bool, optional If true, a noise standard deviation estimate based on the Marcenko-Pastur distribution is returned [2]_. out_dir : string, optional Output directory. (default current directory) out_denoised : string, optional Name of the resulting denoised volume. out_sigma : string, optional Name of the resulting sigma volume. References ---------- .. [1] Veraart J, Novikov DS, Christiaens D, Ades-aron B, Sijbers, Fieremans E, 2016. Denoising of Diffusion MRI using random matrix theory. Neuroimage 142:394-406. doi: 10.1016/j.neuroimage.2016.08.016 .. [2] Veraart J, Fieremans E, Novikov DS. 2016. Diffusion MRI noise mapping using random matrix theory. Magnetic Resonance in Medicine. doi: 10.1002/mrm.26059. """ io_it = self.get_io_iterator() for dwi, odenoised, osigma in io_it: logging.info('Denoising %s', dwi) data, affine, image = load_nifti(dwi, return_img=True) denoised_data, sigma = mppca(data, patch_radius=patch_radius, pca_method=pca_method, return_sigma=True) save_nifti(odenoised, denoised_data, affine, image.header) logging.info('Denoised volume saved as %s', odenoised) if return_sigma: save_nifti(osigma, sigma, affine, image.header) logging.info('Sigma volume saved as %s', osigma)
def test_mppca_in_phantom(): DWIgt = rfiw_phantom(gtab, snr=None) std_gt = 0.02 noise = std_gt * np.random.standard_normal(DWIgt.shape) DWInoise = DWIgt + noise DWIden = mppca(DWInoise, patch_radius=2) # Test if denoised data is closer to ground truth than noisy data rmse_den = np.sum(np.abs(DWIgt - DWIden)) / np.sum(np.abs(DWIgt)) rmse_noisy = np.sum(np.abs(DWIgt - DWInoise)) / np.sum(np.abs(DWIgt)) assert_(rmse_den < rmse_noisy)
gtab = gradient_table(bvals[sel_b], bvecs[sel_b]) print(data.shape) """ As one can see from its shape, the selected data contains a total of 67 volumes of images corresponding to all the diffusion gradient directions of the selected b-values. The PCA denoising using the Marcenko-Pastur distribution can be performed by calling the function ``mppca``: """ t = time() denoised_arr = mppca(data, patch_radius=2) print("Time taken for local MP-PCA ", -t + time()) """ Internally, the ``mppca`` algorithm denoises the diffusion-weighted data using a 3D sliding window which is defined by the variable patch_radius. In total, this window should comprise a larger number of voxels than the number of diffusion-weighted volumes. Since our data has a total of 67 volumes, the patch_radius is set to 2 which corresponds to a 5x5x5 sliding window comprising a total of 125 voxels. To assess the performance of the Marcenko-Pastur PCA denoising algorithm, an axial slice of the original data, denoised data, and residuals are plotted below: """
gtab = gradient_table(bvals[sel_b], bvecs[sel_b]) """ We make use of the ``median_otsu`` method to generate the mask for the data as follows: """ b0_mask, mask = median_otsu(data, median_radius=2, numpass=1, vol_idx=[0, 1]) print(data.shape) """ As one can see from its shape, the selected data contains a total of 67 volumes of images corresponding to all the diffusion gradient directions of the selected b-values and call the ``mppca`` as follows: """ denoised_arr = mppca(data, mask=mask, patch_radius=2) """ Now we will use the denoised array (``denoised_arr``) obtained from ``mppca`` in the rest of the steps in the tutorial. As for the next step, we generate the anisotropic powermap introduced by [DellAcqua2014]_. To do so, we make use of the Q-ball Model as follows: """ qball_model = shm.QballModel(gtab, 8) """ We generate the peaks from the ``qball_model`` as follows: """ peaks = dp.peaks_from_model(model=qball_model, data=denoised_arr,
autocrop=False, dilate=1) """ Since the diffusion kurtosis models involves the estimation of a large number of parameters [TaxCMW2015]_ and since the non-Gaussian components of the diffusion signal are more sensitive to artefacts [NetoHe2012]_, it might be favorable to suppress the effects of noise and artefacts before diffusion kurtosis fitting. In this example the effects of noise are suppressed using the Marcenko-Pastur PCA denoising algorithm (:ref:`example-denoise-mppca`) and the Gibbs artefact suppression algorithm (:ref:`example-denoise-gibbs`). Due to the dataset's large number of diffusion-weighted volumes, these algorithm might take some hours to process. """ print("Denoising") den, sig = mppca(data, patch_radius=4, return_sigma=True) print("Removing Gibbs artifacts") deng = gibbs_removal(den, slice_axis=2) # img = nib.load('denoised_mppca.nii.gz') # img = nib.load('data_mppca_gibbs.nii.gz') # deng = img.get_fdata() """ Before carrying on with dki processing, I will just visually inspect the quality of data and preprocessing step. In the figure below, I am ploting a slice of the raw and denoised + gibbs suppressed data (upper panels). Lower left panel show the difference between raw and denoised data - this difference map does not present anatomicaly information, indicating that PCA denoising suppression noise with minimal low of anatomical information. Lower right map shows the noise std estimate. Lower noise std values are underestimated in background since noise in background appraoches a Rayleigh distribution.