def test_msdki_predict(): dkiM = msdki.MeanDiffusionKurtosisModel(gtab_3s) # single voxel pred = dkiM.predict(params_single, S0=100) assert_array_almost_equal(pred, signal_sph) # multi-voxel pred = dkiM.predict(params_multi, S0=100) assert_array_almost_equal(pred[:, :, 0, :], DWI[:, :, 0, :]) # check the function predict of the DiffusionKurtosisFit object dkiF = dkiM.fit(signal_sph) pred_single = dkiF.predict(gtab_3s, S0=100) assert_array_almost_equal(pred_single, signal_sph) dkiF = dkiM.fit(DWI) pred_multi = dkiF.predict(gtab_3s, S0=100) assert_array_almost_equal(pred_multi[:, :, 0, :], DWI[:, :, 0, :]) # No S0 dkiF = dkiM.fit(signal_sph) pred_single = dkiF.predict(gtab_3s) assert_array_almost_equal(100 * pred_single, signal_sph) dkiF = dkiM.fit(DWI) pred_multi = dkiF.predict(gtab_3s) assert_array_almost_equal(100 * pred_multi[:, :, 0, :], DWI[:, :, 0, :]) # SO volume dkiF = dkiM.fit(DWI) pred_multi = dkiF.predict(gtab_3s, 100 * np.ones(DWI.shape[:-1])) assert_array_almost_equal(pred_multi[:, :, 0, :], DWI[:, :, 0, :])
def test_errors(): # first error raises if MeanDiffusionKurtosisModel is called for # data will only one non-zero b-value assert_raises(ValueError, msdki.MeanDiffusionKurtosisModel, gtab) # second error raises if negative signal is given to MeanDiffusionKurtosis # model assert_raises(ValueError, msdki.MeanDiffusionKurtosisModel, gtab_3s, min_signal=-1) # third error raises if wrong mask is given to fit mask_wrong = np.ones((2, 3, 1)) msdki_model = msdki.MeanDiffusionKurtosisModel(gtab_3s) assert_raises(ValueError, msdki_model.fit, DWI, mask=mask_wrong) # fourth error raises if an given index point to more dimensions that data # does not contain # define auxiliary function for the assert raises def aux_test_fun(ob, ind): met = ob[ind].msk return met mdkiF = msdki_model.fit(DWI) assert_raises(IndexError, aux_test_fun, mdkiF, (0, 0, 0, 0)) # checking if aux_test_fun runs fine met = aux_test_fun(mdkiF, (0, 0, 0)) assert_array_almost_equal(MKgt_multi[0, 0, 0], met)
def test_smt2_specific_cases(): mdkiM = msdki.MeanDiffusionKurtosisModel(gtab_3s) # Check smt2 is sepecific cases with knowm g.t: # 1) Intrisic diffusion is equal MSD for single Gaussian isotropic # diffusion (i.e. awf=0) sig_gaussian = single_tensor(gtab_3s, evals=np.array([2e-3, 2e-3, 2e-3])) mdkiF = mdkiM.fit(sig_gaussian) assert_almost_equal(mdkiF.msk, 0.0) assert_almost_equal(mdkiF.msd, 2.0e-3) assert_almost_equal(mdkiF.smt2f, 0) assert_almost_equal(mdkiF.smt2di, 2.0e-3) # 2) Intrisic diffusion is equal to MSD/3 for single powder-averaged stick # compartment Da = 2.0e-3 mevals = np.zeros((64, 3)) mevals[:, 0] = Da fracs = np.ones(64) * 100 / 64 signal_pa, dt_sph, kt_sph = multi_tensor_dki(gtab_3s, mevals, angles=bvecs[1:, :], fractions=fracs, snr=None) mdkiF = mdkiM.fit(signal_pa) # decimal is set to 1 because of finite number of directions for powder # average calculation assert_almost_equal(mdkiF.msk, 2.4, decimal=1) assert_almost_equal(mdkiF.msd * 1000, Da / 3 * 1000, decimal=1) assert_almost_equal(mdkiF.smt2f, 1, decimal=1) assert_almost_equal(mdkiF.smt2di, mdkiF.msd * 3, decimal=1)
def test_msdki_statistics(): # tests if MD and MK are equal to expected values of a spherical # tensors # Multi-tensors ub = unique_bvals(bvals_3s) design_matrix = msdki.design_matrix(ub) msignal, ng = msdki.mean_signal_bvalue(DWI, gtab_3s, bmag=None) params = msdki.wls_fit_msdki(design_matrix, msignal, ng) assert_array_almost_equal(params[..., 1], MKgt_multi) assert_array_almost_equal(params[..., 0], MDgt_multi) mdkiM = msdki.MeanDiffusionKurtosisModel(gtab_3s) mdkiF = mdkiM.fit(DWI) mk = mdkiF.msk md = mdkiF.msd assert_array_almost_equal(MKgt_multi, mk) assert_array_almost_equal(MDgt_multi, md) # Single-tensors mdkiF = mdkiM.fit(signal_sph) mk = mdkiF.msk md = mdkiF.msd assert_array_almost_equal(MKgt, mk) assert_array_almost_equal(MDgt, md) # Test with given mask mask = np.ones(DWI.shape[:-1]) v = (0, 0, 0) mask[1, 1, 1] = 0 mdkiF = mdkiM.fit(DWI, mask=mask) mk = mdkiF.msk md = mdkiF.msd assert_array_almost_equal(MKgt_multi, mk) assert_array_almost_equal(MDgt_multi, md) assert_array_almost_equal(MKgt_multi[v], mdkiF[v].msk) # tuple case assert_array_almost_equal(MDgt_multi[v], mdkiF[v].msd) # tuple case assert_array_almost_equal(MKgt_multi[0], mdkiF[0].msk) # not tuple case assert_array_almost_equal(MDgt_multi[0], mdkiF[0].msd) # not tuple case # Test returned S0 mdkiM = msdki.MeanDiffusionKurtosisModel(gtab_3s, return_S0_hat=True) mdkiF = mdkiM.fit(DWI) assert_array_almost_equal(S0gt_multi, mdkiF.S0_hat) assert_array_almost_equal(MKgt_multi[v], mdkiF[v].msk)
def test_smt2_metrics(): # Just checking if parameters can be retrived from MSDKI's fit class obj # Based on the multi-voxel simulations above (computes gt for SMT2 params) AWFgt = awf_from_msk(MKgt_multi) DIgt = 3 * MDgt_multi / (1 + 2 * (1 - AWFgt)**2) # General microscopic anisotropy estimation (Eq 8 Henriques et al MRM 2019) RDe = DIgt * (1 - AWFgt) # tortuosity assumption VarD = 2 / 9 * (AWFgt * DIgt**2 + (1 - AWFgt) * (DIgt - RDe)**2) MD = (AWFgt * DIgt + (1 - AWFgt) * (DIgt + 2 * RDe)) / 3 uFAgt = np.sqrt(3 / 2 * VarD[MD > 0] / (VarD[MD > 0] + MD[MD > 0]**2)) mdkiM = msdki.MeanDiffusionKurtosisModel(gtab_3s) mdkiF = mdkiM.fit(DWI) assert_array_almost_equal(mdkiF.smt2f, AWFgt) assert_array_almost_equal(mdkiF.smt2di, DIgt) assert_array_almost_equal(mdkiF.smt2uFA[MD > 0], uFAgt) # Check if awf_from_msk when mask is given mask = MKgt_multi > 0 AWF = awf_from_msk(MKgt_multi, mask) assert_array_almost_equal(AWF, AWFgt)
To avoid unnecessary calculations on the background of the image, we also compute a brain mask. """ # Create a brain mask from dipy.segment.mask import median_otsu maskdata, mask = median_otsu(data_slices, vol_idx=range(10, 50), median_radius=3, numpass=1, autocrop=False, dilate=1) # Define mean signal diffusion kurtosis model import dipy.reconst.msdki as msdki dki_model = msdki.MeanDiffusionKurtosisModel(gtab) # Fit the uncorrected data dki_fit = dki_model.fit(data_slices, mask=mask) MSKini = dki_fit.msk # Fit the corrected data dki_fit = dki_model.fit(data_corrected, mask=mask) MSKgib = dki_fit.msk """ Let's plot the results """ fig3, ax = plt.subplots(1, 3, figsize=(12, 12), subplot_kw={'xticks': [], 'yticks': []})
def main(): logger = logging.getLogger("Compute_DKI_Metrics") logger.setLevel(logging.INFO) parser = _build_args_parser() args = parser.parse_args() if not args.not_all: args.dki_fa = args.dki_fa or 'dki_fa.nii.gz' args.dki_md = args.dki_md or 'dki_md.nii.gz' args.dki_ad = args.dki_ad or 'dki_ad.nii.gz' args.dki_rd = args.dki_rd or 'dki_rd.nii.gz' args.mk = args.mk or 'mk.nii.gz' args.rk = args.rk or 'rk.nii.gz' args.ak = args.ak or 'ak.nii.gz' args.dki_residual = args.dki_residual or 'dki_residual.nii.gz' args.msk = args.msk or 'msk.nii.gz' args.msd = args.msd or 'msd.nii.gz' outputs = [args.dki_fa, args.dki_md, args.dki_ad, args.dki_rd, args.mk, args.rk, args.ak, args.dki_residual, args.msk, args.msd] if args.not_all and not any(outputs): parser.error('When using --not_all, you need to specify at least ' + 'one metric to output.') assert_inputs_exist( parser, [args.input, args.bvals, args.bvecs], args.mask) assert_outputs_exist(parser, args, outputs) img = nib.load(args.input) data = img.get_fdata() affine = img.affine if args.mask is None: mask = None else: mask = nib.load(args.mask).get_fdata().astype(np.bool) # Validate bvals and bvecs bvals, bvecs = read_bvals_bvecs(args.bvals, args.bvecs) if not is_normalized_bvecs(bvecs): logging.warning('Your b-vectors do not seem normalized...') bvecs = normalize_bvecs(bvecs) # Find the volume indices that correspond to the shells to extract. tol = args.tolerance shells, _ = identify_shells(bvals, tol) if not len(shells) >= 3: parser.error('Data is not multi-shell. You need at least 2 non-zero' + ' b-values') if (shells > 2500).any(): logging.warning('You seem to be using b > 2500 s/mm2 DWI data. ' + 'In theory, this is beyond the optimal range for DKI') check_b0_threshold(args, bvals.min()) gtab = gradient_table(bvals, bvecs, b0_threshold=bvals.min()) fwhm = args.smooth if fwhm > 0: # converting fwhm to Gaussian std gauss_std = fwhm / np.sqrt(8 * np.log(2)) data_smooth = np.zeros(data.shape) for v in range(data.shape[-1]): data_smooth[..., v] = gaussian_filter(data[..., v], sigma=gauss_std) data = data_smooth # Compute DKI dkimodel = dki.DiffusionKurtosisModel(gtab) dkifit = dkimodel.fit(data, mask=mask) min_k = args.min_k max_k = args.max_k if args.dki_fa: FA = dkifit.fa FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, args.dki_fa) if args.dki_md: MD = dkifit.md md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, args.dki_md) if args.dki_ad: AD = dkifit.ad ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, args.dki_ad) if args.dki_rd: RD = dkifit.rd rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, args.dki_rd) if args.mk: MK = dkifit.mk(min_k, max_k) mk_img = nib.Nifti1Image(MK.astype(np.float32), affine) nib.save(mk_img, args.mk) if args.ak: AK = dkifit.ak(min_k, max_k) ak_img = nib.Nifti1Image(AK.astype(np.float32), affine) nib.save(ak_img, args.ak) if args.rk: RK = dkifit.rk(min_k, max_k) rk_img = nib.Nifti1Image(RK.astype(np.float32), affine) nib.save(rk_img, args.rk) if args.msk or args.msd: # Compute MSDKI msdki_model = msdki.MeanDiffusionKurtosisModel(gtab) msdki_fit = msdki_model.fit(data, mask=mask) if args.msk: MSK = msdki_fit.msk MSK[np.isnan(MSK)] = 0 MSK = np.clip(MSK, min_k, max_k) msk_img = nib.Nifti1Image(MSK.astype(np.float32), affine) nib.save(msk_img, args.msk) if args.msd: MSD = msdki_fit.msd msd_img = nib.Nifti1Image(MSD.astype(np.float32), affine) nib.save(msd_img, args.msd) if args.dki_residual: S0 = np.mean(data[..., gtab.b0s_mask], axis=-1) data_p = dkifit.predict(gtab, S0) R = np.mean(np.abs(data_p[..., ~gtab.b0s_mask] - data[..., ~gtab.b0s_mask]), axis=-1) norm = np.linalg.norm(R) if norm != 0: R = R / norm if args.mask is not None: R *= mask R_img = nib.Nifti1Image(R.astype(np.float32), affine) nib.save(R_img, args.dki_residual)