def test_lpca_boundary_behaviour(): # check is first slice is getting denoised or not ? S0 = 100 * np.ones((20, 20, 20, 20), dtype='f8') S0[:, :, 0, :] = S0[:, :, 0, :] + 2 * \ np.random.standard_normal((20, 20, 20)) S0_first = S0[:, :, 0, :] S0ns = localpca(S0, sigma=np.std(S0)) S0ns_first = S0ns[:, :, 0, :] rmses = np.sum(np.abs(S0ns_first - S0_first)) / \ (100.0 * 20.0 * 20.0 * 20.0) # shows that S0n_first is not very close to S0_first assert_(rmses > 0.0001) assert_equal(np.round(S0ns_first.mean()), 100) # Use a volume of sigma, instead of a scalar: sigma_vol = np.ones(S0.shape[:-1]) * np.std(S0) S0ns = localpca(S0, sigma=sigma_vol) rmses = np.sum(np.abs(S0ns_first - S0_first)) / \ (100.0 * 20.0 * 20.0 * 20.0) # shows that S0n_first is not very close to S0_first assert_(rmses > 0.0001) assert_equal(np.round(S0ns_first.mean()), 100)
def test_lpca_boundary_behaviour(): # check is first slice is getting denoised or not ? S0 = 100 * np.ones((20, 20, 20, 20), dtype='f8') S0[:, :, 0, :] = S0[:, :, 0, :] + 2 * \ np.random.standard_normal((20, 20, 20)) S0_first = S0[:, :, 0, :] S0ns = localpca(S0, sigma=np.std(S0)) S0ns_first = S0ns[:, :, 0, :] rmses = np.sum(np.abs(S0ns_first - S0_first)) / \ (100.0 * 20.0 * 20.0 * 20.0) # shows that S0n_first is not very close to S0_first assert_(rmses > 0.0001) assert_equal(np.round(S0ns_first.mean()), 100) # Use a volume of sigma, instead of a scalar: sigma_vol = np.ones(S0.shape[:-1]) * np.std(S0) S0ns = localpca(S0, sigma=sigma_vol) rmses = np.sum(np.abs(S0ns_first - S0_first)) / \ (100.0 * 20.0 * 20.0 * 20.0) # shows that S0n_first is not very close to S0_first assert_(rmses > 0.0001) assert_equal(np.round(S0ns_first.mean()), 100)
def test_phantom(): gtab = gen_gtab() DWI_clean = rfiw_phantom(gtab, snr=None) DWI, sigma = rfiw_phantom(gtab, snr=30) # To test without rician correction temp = (DWI_clean / sigma)**2 DWI_clean_wrc = (sigma * np.sqrt(np.pi / 2) * np.exp(-0.5 * temp) * ((1 + 0.5 * temp) * sps.iv(0, 0.25 * temp) + 0.5 * temp * sps.iv(1, 0.25 * temp))**2) DWI_den = localpca(DWI, sigma, patch_radius=3) rmse_den = np.sum(np.abs(DWI_clean - DWI_den)) / np.sum(np.abs(DWI_clean)) rmse_noisy = np.sum(np.abs(DWI_clean - DWI)) / np.sum(np.abs(DWI_clean)) rmse_den_wrc = np.sum(np.abs(DWI_clean_wrc - DWI_den)) / np.sum( np.abs(DWI_clean_wrc)) rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc - DWI)) / \ np.sum(np.abs(DWI_clean_wrc)) assert_(np.max(DWI_clean) / sigma < np.max(DWI_den) / sigma) assert_(np.max(DWI_den) / sigma < np.max(DWI) / sigma) assert_(rmse_den < rmse_noisy) assert_(rmse_den_wrc < rmse_noisy_wrc) # Check if the results of different PCA methods (eig, svd) are similar DWI_den_svd = localpca(DWI, sigma, pca_method='svd', patch_radius=3) assert_array_almost_equal(DWI_den, DWI_den_svd) assert_raises(ValueError, localpca, DWI, sigma, pca_method='empty') # Try this with a sigma volume, instead of a scalar sigma_vol = sigma * np.ones(DWI.shape[:-1]) mask = np.zeros_like(DWI, dtype=bool)[..., 0] mask[2:-2, 2:-2, 2:-2] = True DWI_den = localpca(DWI, sigma_vol, mask, patch_radius=3) DWI_clean_masked = DWI_clean.copy() DWI_clean_masked[~mask] = 0 DWI_masked = DWI.copy() DWI_masked[~mask] = 0 rmse_den = np.sum(np.abs(DWI_clean_masked - DWI_den)) / np.sum( np.abs(DWI_clean_masked)) rmse_noisy = np.sum(np.abs(DWI_clean_masked - DWI_masked)) / np.sum( np.abs(DWI_clean_masked)) DWI_clean_wrc_masked = DWI_clean_wrc.copy() DWI_clean_wrc_masked[~mask] = 0 rmse_den_wrc = np.sum(np.abs(DWI_clean_wrc_masked - DWI_den)) / np.sum( np.abs(DWI_clean_wrc_masked)) rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc_masked - DWI_masked)) / \ np.sum(np.abs(DWI_clean_wrc_masked)) assert_(np.max(DWI_clean) / sigma < np.max(DWI_den) / sigma) assert_(np.max(DWI_den) / sigma < np.max(DWI) / sigma) assert_(rmse_den < rmse_noisy) assert_(rmse_den_wrc < rmse_noisy_wrc)
def test_phantom(): gtab = gen_gtab() DWI_clean = rfiw_phantom(gtab, snr=None) DWI, sigma = rfiw_phantom(gtab, snr=30) # To test without rician correction temp = (DWI_clean / sigma)**2 DWI_clean_wrc = (sigma * np.sqrt(np.pi / 2) * np.exp(-0.5 * temp) * ((1 + 0.5 * temp) * sps.iv(0, 0.25 * temp) + 0.5 * temp * sps.iv(1, 0.25 * temp))**2) DWI_den = localpca(DWI, sigma, patch_radius=3) rmse_den = np.sum(np.abs(DWI_clean - DWI_den)) / np.sum(np.abs(DWI_clean)) rmse_noisy = np.sum(np.abs(DWI_clean - DWI)) / np.sum(np.abs(DWI_clean)) rmse_den_wrc = np.sum(np.abs(DWI_clean_wrc - DWI_den) ) / np.sum(np.abs(DWI_clean_wrc)) rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc - DWI)) / \ np.sum(np.abs(DWI_clean_wrc)) assert_(np.max(DWI_clean) / sigma < np.max(DWI_den) / sigma) assert_(np.max(DWI_den) / sigma < np.max(DWI) / sigma) assert_(rmse_den < rmse_noisy) assert_(rmse_den_wrc < rmse_noisy_wrc) # Check if the results of different PCA methods (eig, svd) are similar DWI_den_svd = localpca(DWI, sigma, pca_method='svd', patch_radius=3) assert_array_almost_equal(DWI_den, DWI_den_svd) assert_raises(ValueError, localpca, DWI, sigma, pca_method='empty') # Try this with a sigma volume, instead of a scalar sigma_vol = sigma * np.ones(DWI.shape[:-1]) mask = np.zeros_like(DWI, dtype=bool)[..., 0] mask[2:-2, 2:-2, 2:-2] = True DWI_den = localpca(DWI, sigma_vol, mask, patch_radius=3) DWI_clean_masked = DWI_clean.copy() DWI_clean_masked[~mask] = 0 DWI_masked = DWI.copy() DWI_masked[~mask] = 0 rmse_den = np.sum(np.abs(DWI_clean_masked - DWI_den)) / np.sum(np.abs( DWI_clean_masked)) rmse_noisy = np.sum(np.abs(DWI_clean_masked - DWI_masked)) / np.sum(np.abs( DWI_clean_masked)) DWI_clean_wrc_masked = DWI_clean_wrc.copy() DWI_clean_wrc_masked[~mask] = 0 rmse_den_wrc = np.sum(np.abs(DWI_clean_wrc_masked - DWI_den) ) / np.sum(np.abs(DWI_clean_wrc_masked)) rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc_masked - DWI_masked)) / \ np.sum(np.abs(DWI_clean_wrc_masked)) assert_(np.max(DWI_clean) / sigma < np.max(DWI_den) / sigma) assert_(np.max(DWI_den) / sigma < np.max(DWI) / sigma) assert_(rmse_den < rmse_noisy) assert_(rmse_den_wrc < rmse_noisy_wrc)
def _preprocess(self, data_container: DataContainer) -> DataContainer: dc = super()._preprocess(data_container) sigma = pca_noise_estimate(dc.dwi, dc.gtab, correct_bias=True, smooth=self.smooth) dwi = localpca(dc.dwi, sigma=sigma, patch_radius=self.patch_radius) return DataContainer(dc.bvals, dc.bvecs, dc.gtab, dc.t1, dwi, dc.aff, dc.binary_mask, dc.b0, dc.fa)
def test_lpca_random_noise(): S0 = 100 + 2 * np.random.standard_normal((22, 23, 30, 20)) S0ns = localpca(S0, sigma=np.std(S0)) assert_(S0ns.min() > S0.min()) assert_(S0ns.max() < S0.max()) assert_equal(np.round(S0ns.mean()), 100)
def test_lpca_rmse(): S0_w_noise = 100 + 2 * np.random.standard_normal((22, 23, 30, 20)) rmse_w_noise = np.sqrt(np.mean((S0_w_noise - 100)**2)) S0_denoised = localpca(S0_w_noise, sigma=np.std(S0_w_noise)) rmse_denoised = np.sqrt(np.mean((S0_denoised - 100)**2)) # Denoising should always improve the RMSE: assert_(rmse_denoised < rmse_w_noise)
def test_lpca_rmse(): S0_w_noise = 100 + 2 * np.random.standard_normal((22, 23, 30, 20)) rmse_w_noise = np.sqrt(np.mean((S0_w_noise - 100) ** 2)) S0_denoised = localpca(S0_w_noise, sigma=np.std(S0_w_noise)) rmse_denoised = np.sqrt(np.mean((S0_denoised - 100) ** 2)) # Denoising should always improve the RMSE: assert_(rmse_denoised < rmse_w_noise)
def test_lpca_random_noise(): S0 = 100 + 2 * np.random.standard_normal((22, 23, 30, 20)) S0ns = localpca(S0, sigma=np.std(S0)) assert_(S0ns.min() > S0.min()) assert_(S0ns.max() < S0.max()) assert_equal(np.round(S0ns.mean()), 100)
def denoise(diff, gtab, affine_diff): # PCA-based denoising algorithms are effective denoising methods because they explore the redundancy of the multi-dimensional information of diffusion-weighted datasets. sigma = dipy.denoise.pca_noise_estimate.pca_noise_estimate( diff, gtab, correct_bias=True, smooth=3) den = localpca(diff, sigma, tau_factor=2.3, patch_radius=2) nib.save(nib.Nifti1Image(den, affine_diff), 'denoised.nii.gz') return den
def _retrieve_data(self, file_names, denoise=False, b0_threshold=10): """ Reads data from specific files and returns them as object. This functions reads the filenames of the DWI image and loads/parses them accordingly. Also, it denoises them, if specified and generates a b0 image. The `file_names` param should be a dict with the following keys: `['bvals', 'bvecs', 'img', 't1', 'mask']` Parameters ---------- file_names : dict The filenames, or relative paths from `self.path`. denoise : bool, optional A boolean indicating wether the given data should be denoised, by default False b0_threshold : float, optional A single value indicating the b0 threshold used for b0 calculation, by default 10.0 Returns ------- RawData An object holding all data as attributes, usable for further processing. Raises ------ DataContainerNotLoadableError This error is thrown if one or multiple files cannot be found. """ data = RawData() try: data.bvals, data.bvecs = read_bvals_bvecs(os.path.join(self.path, file_names['bvals']), os.path.join(self.path, file_names['bvecs'])) data.img = nb.load(os.path.join(self.path, file_names['img'])) data.t1 = nb.load(os.path.join(self.path, file_names['t1'])).get_data() except FileNotFoundError as error: raise DataContainerNotLoadableError(self.path, error.filename) from None data.gtab = gradient_table(bvals=data.bvals, bvecs=data.bvecs) data.dwi = data.img.get_data().astype("float32") data.aff = data.img.affine data.fa = None if denoise: sigma = pca_noise_estimate(data.dwi, data.gtab, correct_bias=True, smooth=Config.get_config().getint("denoise", "smooth", fallback="3")) data.dwi = localpca(data.dwi, sigma=sigma, patch_radius=Config.get_config().getint("denoise", "pathRadius", fallback="2")) if 'mask' in file_names: data.binarymask = nb.load(os.path.join(self.path, file_names['mask'])).get_data() else: _, data.binarymask = median_otsu(data.dwi[..., 0], 2, 1) data.b0 = data.dwi[..., data.bvals < b0_threshold].mean(axis=-1) return data
def test_lpca_sharpness(): S0 = np.ones((30, 30, 30, 20), dtype=np.float64) * 100 S0[10:20, 10:20, 10:20, :] = 50 S0[20:30, 20:30, 20:30, :] = 0 S0 = S0 + 20 * np.random.standard_normal((30, 30, 30, 20)) S0ns = localpca(S0, sigma=20.0) # check the edge gradient edgs = np.abs(np.mean(S0ns[8, 10:20, 10:20] - S0ns[12, 10:20, 10:20]) - 50) assert_(edgs < 2)
def test_lpca_sharpness(): S0 = np.ones((30, 30, 30, 20), dtype=np.float64) * 100 S0[10:20, 10:20, 10:20, :] = 50 S0[20:30, 20:30, 20:30, :] = 0 S0 = S0 + 20 * np.random.standard_normal((30, 30, 30, 20)) S0ns = localpca(S0, sigma=20.0) # check the edge gradient edgs = np.abs(np.mean(S0ns[8, 10:20, 10:20] - S0ns[12, 10:20, 10:20]) - 50) assert_(edgs < 2)
def test_phantom(): gtab = gen_gtab() DWI_clean = rfiw_phantom(gtab, snr=None) DWI, sigma = rfiw_phantom(gtab, snr=30) # To test without rician correction temp = (DWI_clean / sigma)**2 DWI_clean_wrc = (sigma * np.sqrt(np.pi / 2) * np.exp(-0.5 * temp) * ((1 + 0.5 * temp) * sps.iv(0, 0.25 * temp) + 0.5 * temp * sps.iv(1, 0.25 * temp))**2) DWI_den = localpca(DWI, sigma, patch_radius=3) rmse_den = np.sum(np.abs(DWI_clean - DWI_den)) / np.sum(np.abs(DWI_clean)) rmse_noisy = np.sum(np.abs(DWI_clean - DWI)) / np.sum(np.abs(DWI_clean)) rmse_den_wrc = np.sum(np.abs(DWI_clean_wrc - DWI_den)) / np.sum( np.abs(DWI_clean_wrc)) rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc - DWI)) / \ np.sum(np.abs(DWI_clean_wrc)) assert_(np.max(DWI_clean) / sigma < np.max(DWI_den) / sigma) assert_(np.max(DWI_den) / sigma < np.max(DWI) / sigma) assert_(rmse_den < rmse_noisy) assert_(rmse_den_wrc < rmse_noisy_wrc) # Try this with a sigma volume, instead of a scalar sigma_vol = sigma * np.ones(DWI.shape[:-1]) DWI_den = localpca(DWI, sigma_vol, patch_radius=3) rmse_den = np.sum(np.abs(DWI_clean - DWI_den)) / np.sum(np.abs(DWI_clean)) rmse_noisy = np.sum(np.abs(DWI_clean - DWI)) / np.sum(np.abs(DWI_clean)) rmse_den_wrc = np.sum(np.abs(DWI_clean_wrc - DWI_den)) / np.sum( np.abs(DWI_clean_wrc)) rmse_noisy_wrc = np.sum(np.abs(DWI_clean_wrc - DWI)) / \ np.sum(np.abs(DWI_clean_wrc)) assert_(np.max(DWI_clean) / sigma < np.max(DWI_den) / sigma) assert_(np.max(DWI_den) / sigma < np.max(DWI) / sigma) assert_(rmse_den < rmse_noisy) assert_(rmse_den_wrc < rmse_noisy_wrc)
def test_lpca_dtype(): # If out_dtype is not specified, we retain the original precision: S0 = 200 * np.ones((20, 20, 20, 3), dtype=np.float64) S0ns = localpca(S0, sigma=1) assert_equal(S0.dtype, S0ns.dtype) S0 = 200 * np.ones((20, 20, 20, 20), dtype=np.uint16) S0ns = localpca(S0, sigma=np.ones((20, 20, 20))) assert_equal(S0.dtype, S0ns.dtype) # If we set out_dtype, we get what we asked for: S0 = 200 * np.ones((20, 20, 20, 20), dtype=np.uint16) S0ns = localpca(S0, sigma=np.ones((20, 20, 20)), out_dtype=np.float32) assert_equal(np.float32, S0ns.dtype) # If we set a few entries to zero, this induces negative entries in the # Resulting denoised array: S0[5:8, 5:8, 5:8] = 0 # But if we should always get all non-negative results: S0ns = localpca(S0, sigma=np.ones((20, 20, 20)), out_dtype=np.uint16) assert_(np.all(S0ns >= 0)) # And no wrap-around to crazy high values: assert_(np.all(S0ns <= 200))
def test_lpca_dtype(): # If out_dtype is not specified, we retain the original precision: S0 = 200 * np.ones((20, 20, 20, 3), dtype=np.float64) S0ns = localpca(S0, sigma=1) assert_equal(S0.dtype, S0ns.dtype) S0 = 200 * np.ones((20, 20, 20, 20), dtype=np.uint16) S0ns = localpca(S0, sigma=np.ones((20, 20, 20))) assert_equal(S0.dtype, S0ns.dtype) # If we set out_dtype, we get what we asked for: S0 = 200 * np.ones((20, 20, 20, 20), dtype=np.uint16) S0ns = localpca(S0, sigma=np.ones((20, 20, 20)), out_dtype=np.float32) assert_equal(np.float32, S0ns.dtype) # If we set a few entries to zero, this induces negative entries in the # Resulting denoised array: S0[5:8, 5:8, 5:8] = 0 # But if we should always get all non-negative results: S0ns = localpca(S0, sigma=np.ones((20, 20, 20)), out_dtype=np.uint16) assert_(np.all(S0ns >= 0)) # And no wrap-around to crazy high values: assert_(np.all(S0ns <= 200))
def execution(self, context): data_vol = aims.read(self.dwi_data.fullPath()) header = data_vol.header() data = vol_to_array(data_vol) sigma_vol = aims.read(self.sigma.fullPath()) sigma = vol_to_array(sigma_vol) if self.brain_mask is not None: brain_mask_vol = aims.read(self.brain_mask.fullPath()) brain_mask = vol_to_array(brain_mask_vol) else: brain_mask = None denoised_data = localpca(data, sigma, mask=brain_mask, pca_method=self.method, patch_radius=self.patch_radius, tau_factor=self.tau_factor) denoised_data_vol = array_to_vol(denoised_data, header=header) aims.write(denoised_data_vol, self.denoised_dwi_data.fullPath())
def dti_preprocessing(diff_dir, output_dir, denoise=True, ref_slice=0, median_radius=3, numpass=1, autocrop=False, dilate=2, selem=10, output_type="NIFTI"): """Function to realign, eddy current correct and eventually denoise diffusion data Parameters ---------- diff_dir : absolute path pointing at directory sub-id_visit-id/dwi where dwi acquisition (as a single 4D nii or nii file), bvec and bval files are stored output_dir: absolute path of the directories were the indexes will be written denoise : boolean, should denoise using Marcenko-Pastur PCA algorithm be performed fill : boolean, sometimes oshu method create hole in the mask, especially if you are working with populations that may have very low values at B0 in certain structures. Fill use erosion based reconstruction methods to fill in-mask holes. output_type: ["NIFTI", "NIFTI_GZ"], the type of nifti output desired, ... : other parameters related to the nipype and dipy functions used """ fdwi = glob("{}/*nii*".format(diff_dir))[0] output_base_name = get_base_name_all_type(fdwi) if output_type == "NIFTI": ext = ".nii" elif output_type == "NIFTI_GZ": ext = ".nii.gz" else: raise ValueError( "Output file type {} not recognized".format(output_type)) ec_out_file = "{}/{}_ec{}".format(output_dir, output_base_name, ext) if not (isfile(ec_out_file)): ec = EddyCorrect(in_file=fdwi, out_file=ec_out_file) ec.inputs.output_type = output_type print("Eddy Correction") ec.run() else: print("Skipping EC") mask_name, masked_name = custom_brain_extraction(output_base_name, ec_out_file, output_dir, output_type, median_radius, numpass, autocrop, dilate, selem) if denoise: denoised_out_file = "{}/{}_denoised{}".format(output_dir, output_base_name, ext) if not (isfile(denoised_out_file)): print("Denoising") fbval = glob("{}/*bval".format(diff_dir))[0] fbvec = glob("{}/*bvec".format(diff_dir))[0] bvals, bvecs = read_bvals_bvecs(fbval, fbvec) gtab = gradient_table(bvals, bvecs) img = nb.load(masked_name) mask = nb.load(mask_name) x_ix, y_ix, z_ix, mask_crop, maskdata_crop = crop_and_indexes( mask, img) sigma = pca_noise_estimate(maskdata_crop, gtab, correct_bias=True, smooth=3) denoised_arr = localpca(maskdata_crop, sigma, tau_factor=2.3, patch_radius=2) opt = np.zeros(img.shape) opt[x_ix, y_ix, z_ix, :] = denoised_arr den_img = nb.Nifti1Image(opt.astype(np.float32), img.affine) nb.save(den_img, denoised_out_file)
def run(self, input_files, bvalues_files, bvectors_files, sigma=0, b0_threshold=50, bvecs_tol=0.01, patch_radius=2, pca_method='eig', tau_factor=2.3, out_dir='', out_denoised='dwi_lpca.nii.gz'): r"""Workflow wrapping LPCA denoising method. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors_files : string Path to the bvectors files. This path may contain wildcards to use multiple bvectors files at once. sigma : float, optional Standard deviation of the noise estimated from the data. Default 0: it means sigma value estimation with the Manjon2013 algorithm [3]_. b0_threshold : float, optional Threshold used to find b0 volumes. bvecs_tol : float, optional Threshold used to check that norm(bvec) = 1 +/- bvecs_tol b-vectors are unit vectors. patch_radius : 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. tau_factor : float, optional Thresholding of PCA eigenvalues is done by nulling out eigenvalues that are smaller than: .. math :: \tau = (\tau_{factor} \sigma)^2 \tau_{factor} can be change to adjust the relationship between the noise standard deviation and the threshold \tau. If \tau_{factor} is set to None, it will be automatically calculated using the Marcenko-Pastur distribution [2]_. out_dir : string, optional Output directory. (default current directory) out_denoised : string, optional Name of the resulting denoised 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. .. [3] Manjon JV, Coupe P, Concha L, Buades A, Collins DL (2013) Diffusion Weighted Image Denoising Using Overcomplete Local PCA. PLoS ONE 8(9): e73021. https://doi.org/10.1371/journal.pone.0073021 """ io_it = self.get_io_iterator() for dwi, bval, bvec, odenoised in io_it: logging.info('Denoising %s', dwi) data, affine, image = load_nifti(dwi, return_img=True) if not sigma: logging.info('Estimating sigma') bvals, bvecs = read_bvals_bvecs(bval, bvec) gtab = gradient_table(bvals, bvecs, b0_threshold=b0_threshold, atol=bvecs_tol) sigma = pca_noise_estimate(data, gtab, correct_bias=True, smooth=3) logging.debug('Found sigma %s', sigma) denoised_data = localpca(data, sigma=sigma, patch_radius=patch_radius, pca_method=pca_method, tau_factor=tau_factor) save_nifti(odenoised, denoised_data, affine, image.header) logging.info('Denoised volume saved as %s', odenoised)
def test_lpca_static(): S0 = 100 * np.ones((20, 20, 20, 20), dtype='f8') S0ns = localpca(S0, sigma=np.ones((20, 20, 20), dtype=np.float64)) assert_array_almost_equal(S0, S0ns)
def test_lpca_static(): S0 = 100 * np.ones((20, 20, 20, 20), dtype='f8') S0ns = localpca(S0, sigma=np.ones((20, 20, 20), dtype=np.float64)) assert_array_almost_equal(S0, S0ns)
from dipy.reconst.dti import TensorModel duration1 = time() - t1 # print('BIAC006'+' DTI duration %.3f' % (duration1,)) lpca_path = outpath + '/LPCA_' + id + '_nii4D.nii.gz' if path.exists(lpca_path): print('File already exists; Skipping LPCA denoising (path: ' + lpca_path + ')') else: print('Beginning LPCA denoising for: ' + id + '. (Expected result: ' + lpca_path + ')') t = time() print(data.shape) data2 = data sigma1 = pca_noise_estimate(data2, gtab, correct_bias=True, smooth=1) print("Sigma estimation time", time() - t) #lpca t = time() denoised_arr = localpca(data2, sigma=sigma1, patch_radius=2, pca_method='svd', tau_factor=2.3) save_nifti(lpca_path, denoised_arr, affine) print("Time taken for local PCA denoising", -t + time())
t = time() sigma = pca_noise_estimate(data, gtab, correct_bias=True, smooth=3) print("Sigma estimation time", time() - t) """ Perform the localPCA using the function localpca. The localpca algorithm takes into account for the directional information in the diffusion MR data. It performs PCA on local 4D patch and then thresholds it using the local variance estimate done by noise estimation function, then performing PCA reconstruction on it gives us the deniosed estimate. """ t = time() denoised_arr = localpca(data, sigma=sigma, patch_radius=2) print("Time taken for local PCA (slow)", -t + time()) """ Let us plot the axial slice of the original and denoised data. We visualize all the slices (22 in total) """ sli = data.shape[2] // 2 gra = data.shape[3] // 2 orig = data[:, :, sli, gra] den = denoised_arr[:, :, sli, gra] rms_diff = np.sqrt((orig - den)**2) fig, ax = plt.subplots(1, 3) ax[0].imshow(orig, cmap='gray', origin='lower', interpolation='none')
## Perform the localPCA using the function localpca. The localpca algorithm takes into account the multi-dimensional information of the diffusion MR data. It performs PCA on local 4D patch and then removes the noise components by thresholding the lowest eigenvalues. The eigenvalue threshold will be computed from the local variance estimate performed by the ``pca_noise_estimate`` function, if this is inputted in the main ``localpca`` function. The relationship between the noise variance estimate and the eigenvalue threshold can be adjusted using the input parameter ``tau_factor``. According to Manjon et al. [Manjon2013]_, this parameter is set to 2.3. """ t = time() denoised_arr = localpca(data, sigma, tau_factor=2.3, patch_radius=2) print("Time taken for local PCA (slow)", -t + time()) """ The ``localpca`` function returns the denoised data which is plotted below (middle panel) together with the original version of the data (left panel) and the algorithm residual (right panel) . """ sli = data.shape[2] // 2 gra = data.shape[3] // 2 orig = data[:, :, sli, gra] den = denoised_arr[:, :, sli, gra] rms_diff = np.sqrt((orig - den)**2) fig, ax = plt.subplots(1, 3)
def main(dwiFname, bvalFname, bvecFname, oFname, plot): """Main.""" """ Load one of the datasets. These data were acquired with 63 gradients and 1 non-diffusion (b=0) image. """ data, affine = load_nifti(dwiFname) bvals, bvecs = read_bvals_bvecs(bvalFname, bvecFname) gtab = gradient_table(bvals, bvecs) print("Input Volume", data.shape) """ Estimate the noise standard deviation ===================================== We use the ``pca_noise_estimate`` method to estimate the value of sigma to be used in local PCA algorithm proposed by Manjon et al. [Manjon2013]_. It takes both data and the gradient table object as input and returns an estimate of local noise standard deviation as a 3D array. We return a smoothed version, where a Gaussian filter with radius 3 voxels has been applied to the estimate of the noise before returning it. We correct for the bias due to Rician noise, based on an equation developed by Koay and Basser [Koay2006]_. """ t = time() sigma = pca_noise_estimate(data, gtab, correct_bias=True, smooth=3) print("Sigma estimation time", time() - t) """ Perform the localPCA using the function `localpca` ================================================== The localpca algorithm takes into account the multi-dimensional information of the diffusion MR data. It performs PCA on local 4D patch and then removes the noise components by thresholding the lowest eigenvalues. The eigenvalue threshold will be computed from the local variance estimate performed by the ``pca_noise_estimate`` function, if this is inputted in the main ``localpca`` function. The relationship between the noise variance estimate and the eigenvalue threshold can be adjusted using the input parameter ``tau_factor``. According to Manjon et al. [Manjon2013]_, this parameter is set to 2.3. """ t = time() denoised_arr = localpca(data, sigma, tau_factor=2.3, patch_radius=3) print("Time taken for local PCA (slow)", -t + time()) """ The ``localpca`` function returns the denoised data which is plotted below (middle panel) together with the original version of the data (left panel) and the algorithm residual (right panel) . """ if plot: sli = data.shape[2] // 2 gra = data.shape[3] // 2 orig = data[:, :, sli, gra] den = denoised_arr[:, :, sli, gra] rms_diff = np.sqrt((orig - den)**2) fig, ax = plt.subplots(1, 3) ax[0].imshow(orig, cmap='gray', origin='lower', interpolation='none') ax[0].set_title('Original') ax[0].set_axis_off() ax[1].imshow(den, cmap='gray', origin='lower', interpolation='none') ax[1].set_title('Denoised Output') ax[1].set_axis_off() ax[2].imshow(rms_diff, cmap='gray', origin='lower', interpolation='none') ax[2].set_title('Residual') ax[2].set_axis_off() plt.savefig(oFname + '.png', bbox_inches='tight') print("Result figure saved to: " + oFname + ".png") """ .. figure:: denoised_localpca.png :align: center Below we show how the denoised data can be saved. """ nib.save(nib.Nifti1Image(denoised_arr, affine), oFname + '.nii.gz') print("Denoised dataset saved to: " + oFname + ".nii.gz") """
sigma = pca_noise_estimate(data, gtab, correct_bias=True, smooth=3) print("Sigma estimation time", time() - t) """ Perform the localPCA using the function localpca. The localpca algorithm takes into account for the directional information in the diffusion MR data. It performs PCA on local 4D patch and then thresholds it using the local variance estimate done by noise estimation function, then performing PCA reconstruction on it gives us the deniosed estimate. """ t = time() denoised_arr = localpca(data, sigma=sigma, patch_radius=2) print("Time taken for local PCA (slow)", -t + time()) """ Let us plot the axial slice of the original and denoised data. We visualize all the slices (22 in total) """ sli = data.shape[2] // 2 gra = data.shape[3] // 2 orig = data[:, :, sli, gra] den = denoised_arr[:, :, sli, gra] rms_diff = np.sqrt((orig - den) ** 2) fig, ax = plt.subplots(1, 3)