def test_eig_from_lo_tri(): psphere = get_sphere('symmetric362') bvecs = np.concatenate(([[0, 0, 0]], psphere.vertices)) bvals = np.zeros(len(bvecs)) + 1000 bvals[0] = 0 gtab = grad.gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0001], [0.0015, 0.0003, 0.0003])) mevecs = [np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]), np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]])] S = np.array([[single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None), single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None)]]) dm = dti.TensorModel(gtab, 'LS') dmfit = dm.fit(S) lo_tri = lower_triangular(dmfit.quadratic_form) assert_array_almost_equal(dti.eig_from_lo_tri(lo_tri), dmfit.model_params)
def test_wmti_model_multi_voxel(): # DKI fit dkiM = dki_micro.DiffusionKurtosisModel(gtab_2s, fit_method="WLS") dkiF = dkiM.fit(DWIsim) # Axonal Water Fraction sphere = get_sphere() AWF = dki_micro.axonal_water_fraction(dkiF.model_params, sphere, mask=None, gtol=1e-5) assert_almost_equal(AWF, FIE) # Extra-cellular and intra-cellular components edt, idt = dki_micro.diffusion_components(dkiF.model_params, sphere) EDT = eig_from_lo_tri(edt) IDT = eig_from_lo_tri(idt) # check eigenvalues assert_array_almost_equal(EDT[..., 0], ADE, decimal=3) assert_array_almost_equal(EDT[..., 1], RDE, decimal=3) assert_array_almost_equal(EDT[..., 2], RDE, decimal=3) assert_array_almost_equal(IDT[..., 0], ADI, decimal=3) assert_array_almost_equal(IDT[..., 1], RDI, decimal=3) assert_array_almost_equal(IDT[..., 2], RDI, decimal=3) # Test methods performance when a signal with all zeros is present FIEc = FIE.copy() RDIc = RDI.copy() ADIc = ADI.copy() ADEc = ADE.copy() Torc = Tor.copy() RDEc = RDE.copy() DWIsimc = DWIsim.copy() FIEc[0, 0, 0] = 0 RDIc[0, 0, 0] = 0 ADIc[0, 0, 0] = 0 ADEc[0, 0, 0] = 0 Torc[0, 0, 0] = 0 RDEc[0, 0, 0] = 0 DWIsimc[0, 0, 0, :] = 0 mask = np.ones((2, 2, 2)) mask[0, 0, 0] = 0 dkiF = dkiM.fit(DWIsimc) awf = dki_micro.axonal_water_fraction(dkiF.model_params, sphere, gtol=1e-5) assert_almost_equal(awf, FIEc) # Extra-cellular and intra-cellular components edt, idt = dki_micro.diffusion_components(dkiF.model_params, sphere, awf=awf) EDT = eig_from_lo_tri(edt) IDT = eig_from_lo_tri(idt) assert_array_almost_equal(EDT[..., 0], ADEc, decimal=3) assert_array_almost_equal(EDT[..., 1], RDEc, decimal=3) assert_array_almost_equal(EDT[..., 2], RDEc, decimal=3) assert_array_almost_equal(IDT[..., 0], ADIc, decimal=3) assert_array_almost_equal(IDT[..., 1], RDIc, decimal=3) assert_array_almost_equal(IDT[..., 2], RDIc, decimal=3) # Check when mask is given dkiF = dkiM.fit(DWIsim) awf = dki_micro.axonal_water_fraction(dkiF.model_params, sphere, gtol=1e-5, mask=mask) assert_almost_equal(awf, FIEc, decimal=3) # Extra-cellular and intra-cellular components edt, idt = dki_micro.diffusion_components(dkiF.model_params, sphere, awf=awf, mask=mask) EDT = eig_from_lo_tri(edt) IDT = eig_from_lo_tri(idt) assert_array_almost_equal(EDT[..., 0], ADEc, decimal=3) assert_array_almost_equal(EDT[..., 1], RDEc, decimal=3) assert_array_almost_equal(EDT[..., 2], RDEc, decimal=3) assert_array_almost_equal(IDT[..., 0], ADIc, decimal=3) assert_array_almost_equal(IDT[..., 1], RDIc, decimal=3) assert_array_almost_equal(IDT[..., 2], RDIc, decimal=3) # Check class object wmtiM = dki_micro.KurtosisMicrostructureModel(gtab_2s, fit_method="WLS") wmtiF = wmtiM.fit(DWIsim, mask=mask) assert_almost_equal(wmtiF.awf, FIEc, decimal=3) assert_almost_equal(wmtiF.axonal_diffusivity, ADIc, decimal=3) assert_almost_equal(wmtiF.hindered_ad, ADEc, decimal=3) assert_almost_equal(wmtiF.hindered_rd, RDEc, decimal=3) assert_almost_equal(wmtiF.tortuosity, Torc, decimal=3)
def test_single_fiber_model(): # single fiber simulate (which is the assumption of our model) fie = 0.49 ADi = 0.00099 ADe = 0.00226 RDi = 0 RDe = 0.00087 # prepare simulation: theta = random.uniform(0, 180) phi = random.uniform(0, 320) angles = [(theta, phi), (theta, phi)] mevals = np.array([[ADi, RDi, RDi], [ADe, RDe, RDe]]) frac = [fie * 100, (1 - fie) * 100] signal, dt, kt = multi_tensor_dki(gtab_2s, mevals, angles=angles, fractions=frac, snr=None) # DKI fit dkiM = dki_micro.DiffusionKurtosisModel(gtab_2s, fit_method="WLS") dkiF = dkiM.fit(signal) # Axonal Water Fraction sphere = get_sphere('symmetric724') AWF = dki_micro.axonal_water_fraction(dkiF.model_params, sphere, mask=None, gtol=1e-5) assert_almost_equal(AWF, fie) # Extra-cellular and intra-cellular components edt, idt = dki_micro.diffusion_components(dkiF.model_params, sphere) EDT = eig_from_lo_tri(edt) IDT = eig_from_lo_tri(idt) # check eigenvalues assert_array_almost_equal(EDT[0:3], np.array([ADe, RDe, RDe])) assert_array_almost_equal(IDT[0:3], np.array([ADi, RDi, RDi])) # first eigenvalue should be the direction of the fibers fiber_direction = _check_directions([(theta, phi)]) f_norm = abs(np.dot(fiber_direction, np.array((EDT[3], EDT[6], EDT[9])))) assert_almost_equal(f_norm, 1.) f_norm = abs(np.dot(fiber_direction, np.array((IDT[3], IDT[6], IDT[9])))) assert_almost_equal(f_norm, 1.) # Test model and fit objects wmtiM = dki_micro.KurtosisMicrostructureModel(gtab_2s, fit_method="WLS") wmtiF = wmtiM.fit(signal) assert_almost_equal(wmtiF.awf, AWF) assert_array_almost_equal(wmtiF.hindered_evals, np.array([ADe, RDe, RDe])) assert_array_almost_equal(wmtiF.restricted_evals, np.array([ADi, RDi, RDi])) assert_almost_equal(wmtiF.hindered_ad, ADe) assert_almost_equal(wmtiF.hindered_rd, RDe) assert_almost_equal(wmtiF.axonal_diffusivity, ADi) assert_almost_equal(wmtiF.tortuosity, ADe / RDe, decimal=4) # Test diffusion_components when a kurtosis tensors is associated with # negative kurtosis values. E.g of this cases is given below: dkiparams = np.array([ 1.67135726e-03, 5.03651205e-04, 9.35365328e-05, -7.11167583e-01, 6.23186820e-01, -3.25390313e-01, -1.75247376e-02, -4.78415563e-01, -8.77958674e-01, 7.02804064e-01, 6.18673368e-01, -3.51154825e-01, 2.18384153, -2.76378153e-02, 2.22893297, -2.68306546e-01, -1.28411610, -1.56557645e-01, -1.80850619e-01, -8.33152110e-01, -3.62410766e-01, 1.57775442e-01, 8.73775381e-01, 2.77188975e-01, -3.67415502e-02, -1.56330984e-01, -1.62295407e-02 ]) edt, idt = dki_micro.diffusion_components(dkiparams) assert_(np.all(np.isfinite(edt)))
def initial_fit(dwis, bvals, bvecs, mask, wm_roi, csf_roi, MD, csf_percentile=95, wm_percentile=5, lmin=0.1e-3, lmax=2.5e-3, evals_lmin=0.1e-3, evals_lmax=2.5e-3, md_value=0.6e-3, interpolate=True, fixed_MD=False): ''' Produce the initial estimate of the volume fraction and the initial tensor image ''' print(" - Compute baseline image and DW attenuation.") dim_x, dim_y, dim_z = mask.shape indices_dwis = (bvals > 0) nb_dwis = np.count_nonzero(indices_dwis) indices_b0 = (bvals == 0) nb_b0 = np.count_nonzero(indices_b0) # TO DO : address this line for multi-shell dwi b = bvals.max() b0 = dwis[..., indices_b0].mean(-1) # signal attenuation signal = dwis[..., indices_dwis] / b0[..., None] np.clip(signal, 1.0e-6, 1 - 1.0e-6, signal) signal[np.logical_not(mask)] = 0. # tissue b0 references csf_b0 = np.percentile(b0[csf_roi], csf_percentile) print("\t{0:2d}th percentile of b0 signal in CSF: {1}.".format( csf_percentile, csf_b0)) wm_b0 = np.percentile(b0[wm_roi], wm_percentile) print("\t{0:2d}th percentile of b0 signal in WM : {1}.".format( wm_percentile, wm_b0)) print(" - Compute initial volume fraction ...") # Eq. 7 from Pasternak 2009 MRM epsi = 1e-12 # only used to prevent log(0) init_f = 1 - np.log(b0 / wm_b0 + epsi) / np.log(csf_b0 / wm_b0) np.clip(init_f, 0.0, 1.0, init_f) alpha = init_f.copy() # exponent for interpolation print(" - Compute fixed MD VF map") init_f_MD = (np.exp(-b * MD) - np.exp(-b * d)) / (np.exp(-b * md_value) - np.exp(-b * d)) np.clip(init_f_MD, 0.01, 0.99, init_f_MD) print(" - Compute min_f and max_f from lmin, lmax") ### This was following Pasternak 2009 although with an error ### Amin = exp(-b*lmax) and Amax = exp(-b*lmin) in that paper # min_f = (signal.min(-1)-np.exp(-b*d)) / (np.exp(-b*lmin)-np.exp(-b*d)) # max_f = (signal.max(-1)-np.exp(-b*d)) / (np.exp(-b*lmax)-np.exp(-b*d)) ### From Pasternak 2009 method, Amin < At implies that the ### term with signal.min(-1) in numerator is the upper bound of f ### although in that paper the equation 6 has fmin and fmax reversed. ### With lmin, lmax=0.1e-3, 2.5e-3, Amin = 0.08, Awater = 0.04 ### and one can see that max_f here will usually be >> 1 min_f = (signal.max(-1) - np.exp(-b * d)) / (np.exp(-b * lmin) - np.exp(-b * d)) max_f = (signal.min(-1) - np.exp(-b * d)) / (np.exp(-b * lmax) - np.exp(-b * d)) # If MD of a voxel is > 3.0e-3, min_f and max_f can be negative. # These voxels should be initialized as 0 np.clip(min_f, 0.0, 1.0, min_f) np.clip(max_f, 0.0, 1.0, max_f) np.clip(init_f, min_f, max_f, init_f) if interpolate: print(" - Interpolate two estimates of volume fraction") # f = tissue fraction. with init_f high, alpha will be ~1 and init_f_MD will be weighted init_f = (np.power(init_f, (1 - alpha))) * (np.power(init_f_MD, alpha)) elif fixed_MD: print( " - Using fixed MD value of {0} for inital volume fraction".format( md_value)) init_f = init_f_MD else: print(" - Using lmin and lmax for initial volume fraction") np.clip(init_f, 0.05, 0.99, init_f) # want minimum 5% of tissue init_f[np.isnan(init_f)] = 0.5 init_f[np.logical_not(mask)] = 0.5 print(" - Compute initial tissue tensor ...") signal[np.isnan(signal)] = 0 bvecs = bvecs[indices_dwis] bvals = bvals[indices_dwis] signal_free_water = np.exp(-bvals * d) corrected_signal = (signal - (1 - init_f[..., np.newaxis]) \ * signal_free_water[np.newaxis, np.newaxis, np.newaxis, :]) \ / (init_f[..., np.newaxis]) np.clip(corrected_signal, 1.0e-3, 1. - 1.0e-3, corrected_signal) log_signal = np.log(corrected_signal) gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) H = dti.design_matrix(gtab)[:, :6] pseudo_inv = np.dot(np.linalg.inv(np.dot(H.T, H)), H.T) init_tensor = np.dot(log_signal, pseudo_inv.T) dti_params = dti.eig_from_lo_tri(init_tensor).reshape( (dim_x, dim_y, dim_z, 4, 3)) evals = dti_params[..., 0, :] evecs = dti_params[..., 1:, :] if evals_lmin > 0.1e-3: print(" - Fatten tensor to {}".format(evals_lmin)) lower_triangular = clip_tensor_evals(evals, evecs, evals_lmin, evals_lmax) lower_triangular[np.logical_not(mask)] = [ evals_lmin, 0, evals_lmin, 0, 0, evals_lmin ] nan_mask = np.any(np.isnan(lower_triangular), axis=-1) lower_triangular[nan_mask] = [evals_lmin, 0, evals_lmin, 0, 0, evals_lmin] init_tensor = lower_triangular[:, :, :, np.newaxis, :] return init_f, init_tensor
def gradient_descent(dwis, bvals, bvecs, mask, init_f, init_tensor, niters=50): ''' Optimize the volume fraction and the tensor via gradient descent. ''' dim_x, dim_y, dim_z = mask.shape indices_dwis = (bvals > 0) nb_dwis = np.count_nonzero(indices_dwis) indices_b0 = (bvals == 0) nb_b0 = np.count_nonzero(indices_b0) b = bvals.max() b0 = dwis[..., indices_b0].mean(-1) signal = dwis[..., indices_dwis] / b0[..., None] np.clip(signal, 1.0e-6, 1 - 1.0e-6, signal) bvals = bvals[indices_dwis] bvecs = bvecs[indices_dwis] gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) H = dti.design_matrix(gtab)[:, :6] signal[np.logical_not(mask)] = 0. signal = signal[mask] lower_triangular = init_tensor[mask, 0] volume_fraction = init_f[mask] print(" - Begin gradient descent.") mask_nvoxels = np.count_nonzero(mask) step_size = 1.0e-7 weight = 100.0 l_min_loop, l_max_loop = 0.1e-3, 2.5e-3 for i in range(niters): print(" - Iteration {0:d} out of {1:d}.".format(i + 1, niters)) grad1, predicted_signal_tissue, predicted_signal_water = \ grad_data_fit_tensor(lower_triangular, signal, H, bvals, volume_fraction) print("\tgrad1 avg: {0:0.4e}".format(np.mean(np.abs(grad1)))) predicted_signal = volume_fraction[..., None] * predicted_signal_tissue + \ (1-volume_fraction[..., None]) * predicted_signal_water prediction_error = np.sqrt(((predicted_signal - signal)**2).mean(-1)) print("\tpref error avg: {0:0.4e}".format(np.mean(prediction_error))) gradf = (bvals * (predicted_signal - signal) \ * (predicted_signal_tissue - predicted_signal_water)).sum(-1) print("\tgradf avg: {0:0.4e}".format(np.mean(np.abs(gradf)))) volume_fraction -= weight * step_size * gradf grad1[np.isnan(grad1)] = 0 # np.clip(grad1, -1.e5, 1.e5, grad1) np.clip(volume_fraction, 0.01, 0.99, volume_fraction) lower_triangular -= step_size * grad1 lower_triangular[np.isnan(lower_triangular)] = 0 dti_params = dti.eig_from_lo_tri(lower_triangular).reshape( (mask_nvoxels, 4, 3)) evals = dti_params[..., 0, :] evecs = dti_params[..., 1:, :] lower_triangular = clip_tensor_evals(evals, evecs, l_min_loop, l_max_loop) del dti_params, evals, evecs final_tensor = np.zeros((dim_x, dim_y, dim_z, 1, 6), dtype=np.float32) final_tensor[mask, 0] = lower_triangular final_f = np.zeros((dim_x, dim_y, dim_z), dtype=np.float32) final_f[mask] = 1 - volume_fraction return final_f, final_tensor
def test_single_fiber_model(): # single fiber simulate (which is the assumption of our model) fie = 0.49 ADi = 0.00099 ADe = 0.00226 RDi = 0 RDe = 0.00087 # prepare simulation: theta = random.uniform(0, 180) phi = random.uniform(0, 320) angles = [(theta, phi), (theta, phi)] mevals = np.array([[ADi, RDi, RDi], [ADe, RDe, RDe]]) frac = [fie*100, (1 - fie)*100] signal, dt, kt = multi_tensor_dki(gtab_2s, mevals, angles=angles, fractions=frac, snr=None) # DKI fit dkiM = dki_micro.DiffusionKurtosisModel(gtab_2s, fit_method="WLS") dkiF = dkiM.fit(signal) # Axonal Water Fraction sphere = get_sphere('symmetric724') AWF = dki_micro.axonal_water_fraction(dkiF.model_params, sphere, mask=None, gtol=1e-5) assert_almost_equal(AWF, fie) # Extra-cellular and intra-cellular components edt, idt = dki_micro.diffusion_components(dkiF.model_params, sphere) EDT = eig_from_lo_tri(edt) IDT = eig_from_lo_tri(idt) # check eigenvalues assert_array_almost_equal(EDT[0:3], np.array([ADe, RDe, RDe])) assert_array_almost_equal(IDT[0:3], np.array([ADi, RDi, RDi])) # first eigenvalue should be the direction of the fibers fiber_direction = _check_directions([(theta, phi)]) f_norm = abs(np.dot(fiber_direction, np.array((EDT[3], EDT[6], EDT[9])))) assert_almost_equal(f_norm, 1.) f_norm = abs(np.dot(fiber_direction, np.array((IDT[3], IDT[6], IDT[9])))) assert_almost_equal(f_norm, 1.) # Test model and fit objects wmtiM = dki_micro.KurtosisMicrostructureModel(gtab_2s, fit_method="WLS") wmtiF = wmtiM.fit(signal) assert_almost_equal(wmtiF.awf, AWF) assert_array_almost_equal(wmtiF.hindered_evals, np.array([ADe, RDe, RDe])) assert_array_almost_equal(wmtiF.restricted_evals, np.array([ADi, RDi, RDi])) assert_almost_equal(wmtiF.hindered_ad, ADe) assert_almost_equal(wmtiF.hindered_rd, RDe) assert_almost_equal(wmtiF.axonal_diffusivity, ADi) assert_almost_equal(wmtiF.tortuosity, ADe/RDe, decimal=4) # Test diffusion_components when a kurtosis tensors is associated with # negative kurtosis values. E.g of this cases is given below: dkiparams = np.array([1.67135726e-03, 5.03651205e-04, 9.35365328e-05, -7.11167583e-01, 6.23186820e-01, -3.25390313e-01, -1.75247376e-02, -4.78415563e-01, -8.77958674e-01, 7.02804064e-01, 6.18673368e-01, -3.51154825e-01, 2.18384153, -2.76378153e-02, 2.22893297, -2.68306546e-01, -1.28411610, -1.56557645e-01, -1.80850619e-01, -8.33152110e-01, -3.62410766e-01, 1.57775442e-01, 8.73775381e-01, 2.77188975e-01, -3.67415502e-02, -1.56330984e-01, -1.62295407e-02]) edt, idt = dki_micro.diffusion_components(dkiparams) assert_(np.all(np.isfinite(edt)))
def _run_interface(self, runtime): # Load the 4D image files img = nb.load(self.inputs.in_file) data = img.get_data() affine = img.get_affine() if self.inputs.lower_triangular_input: try: dti_params = dti.eig_from_lo_tri(data) except: dti_params = dti.tensor_eig_from_lo_tri(data) else: data = np.asarray(data) data_flat = data.reshape((-1, data.shape[-1])) dti_params = np.empty((len(data_flat), 4, 3)) for ii in range(len(data_flat)): tensor = from_upper_triangular(data_flat[ii]) evals, evecs = dti.decompose_tensor(tensor) dti_params[ii, 0] = evals dti_params[ii, 1:] = evecs dti_params.shape = data.shape[:-1] + (12, ) evals = dti_params[..., :3] evecs = dti_params[..., 3:] evecs = evecs.reshape(np.shape(evecs)[:3] + (3, 3)) # Estimate electrical conductivity evals = abs(self.inputs.eigenvalue_scaling_factor * evals) if self.inputs.volume_normalized_mapping: # Calculate the cube root of the product of the three eigenvalues (for # normalization) denominator = np.power( (evals[..., 0] * evals[..., 1] * evals[..., 2]), (1 / 3)) # Calculate conductivity and normalize the eigenvalues evals = self.inputs.sigma_white_matter * evals / denominator evals[denominator < 0.0001] = self.inputs.sigma_white_matter # Threshold outliers that show unusually high conductivity if self.inputs.use_outlier_correction: evals[evals > 0.4] = 0.4 conductivity_quadratic = np.array(vec_val_vect(evecs, evals)) if self.inputs.lower_triangular_output: conductivity_data = dti.lower_triangular(conductivity_quadratic) else: conductivity_data = upper_triangular(conductivity_quadratic) # Write as a 4D Nifti tensor image with the original affine img = nb.Nifti1Image(conductivity_data, affine=affine) out_file = op.abspath(self._gen_outfilename()) nb.save(img, out_file) IFLOGGER.info( 'Conductivity tensor image saved as {i}'.format(i=out_file)) return runtime