def test_peak_finding(): vertices, faces=get_sphere('symmetric724') odf=np.zeros(len(vertices)) odf = np.abs(vertices.sum(-1)) odf[1] = 10. odf[505] = 505. odf[143] = 143. peaks, inds=peak_finding(odf.astype('f8'), faces.astype('uint16')) print peaks, inds edges = unique_edges(faces) peaks, inds = local_maxima(odf, edges) print peaks, inds vertices_half, edges_half, faces_half = reduce_antipodal(vertices, faces) n = len(vertices_half) peaks, inds = local_maxima(odf[:n], edges_half) print peaks, inds mevals=np.array(([0.0015,0.0003,0.0003], [0.0015,0.0003,0.0003])) e0=np.array([1,0,0.]) e1=np.array([0.,1,0]) mevecs=[all_tensor_evecs(e0),all_tensor_evecs(e1)] odf = multi_tensor_odf(vertices, [0.5,0.5], mevals, mevecs) peaks, inds=peak_finding(odf, faces) print peaks, inds peaks2, inds2 = local_maxima(odf[:n], edges_half) print peaks2, inds2 assert_equal(len(peaks), 2) assert_equal(len(peaks2), 2)
def test_r2_term_odf_sharp(): SNR = None S0 = 1 angle = 75 _, fbvals, fbvecs = get_data('small_64D') #get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) sphere = get_sphere('symmetric724') gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) S, sticks = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (angle, 0)], fractions=[50, 50], snr=SNR) mevecs = [all_tensor_evecs(sticks[0]).T, all_tensor_evecs(sticks[1]).T] odf_gt = multi_tensor_odf(sphere.vertices, [0.5, 0.5], mevals, mevecs) odfs_sh = sf_to_sh(odf_gt, sphere, sh_order=8, basis_type=None) fodf_sh = odf_sh_to_sharp(odfs_sh, sphere, basis=None, ratio=3 / 15., sh_order=8, lambda_=1., tau=0.1, r2_term=True) fodf = sh_to_sf(fodf_sh, sphere, sh_order=8, basis_type=None) directions_gt, _, _ = peak_directions(odf_gt, sphere) directions, _, _ = peak_directions(fodf, sphere) ang_sim = angular_similarity(directions_gt, directions) assert_equal(ang_sim > 1.9, True) assert_equal(directions.shape[0], 2)
def test_multi_tensor(): sphere = get_sphere('symmetric724') vertices = sphere.vertices mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) e0 = np.array([np.sqrt(2) / 2., np.sqrt(2) / 2., 0]) e1 = np.array([0, np.sqrt(2) / 2., np.sqrt(2) / 2.]) mevecs = [all_tensor_evecs(e0), all_tensor_evecs(e1)] # odf = multi_tensor_odf(vertices, [0.5, 0.5], mevals, mevecs) # assert_(odf.shape == (len(vertices),)) # assert_(np.all(odf <= 1) & np.all(odf >= 0)) fimg, fbvals, fbvecs = get_data('small_101D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) s1 = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None) s2 = single_tensor(gtab, 100, mevals[1], mevecs[1], snr=None) Ssingle = 0.5*s1 + 0.5*s2 S, sticks = MultiTensor(gtab, mevals, S0=100, angles=[(90, 45), (45, 90)], fractions=[50, 50], snr=None) assert_array_almost_equal(S, Ssingle)
def test_multi_tensor(): sphere = get_sphere('symmetric724') # vertices = sphere.vertices mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) e0 = np.array([np.sqrt(2) / 2., np.sqrt(2) / 2., 0]) e1 = np.array([0, np.sqrt(2) / 2., np.sqrt(2) / 2.]) mevecs = [all_tensor_evecs(e0), all_tensor_evecs(e1)] # odf = multi_tensor_odf(vertices, [0.5, 0.5], mevals, mevecs) # assert_(odf.shape == (len(vertices),)) # assert_(np.all(odf <= 1) & np.all(odf >= 0)) fimg, fbvals, fbvecs = get_fnames('small_101D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) s1 = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None) s2 = single_tensor(gtab, 100, mevals[1], mevecs[1], snr=None) Ssingle = 0.5 * s1 + 0.5 * s2 S, _ = multi_tensor(gtab, mevals, S0=100, angles=[(90, 45), (45, 90)], fractions=[50, 50], snr=None) assert_array_almost_equal(S, Ssingle)
def test_multi_tensor(): vertices, faces = get_sphere('symmetric724') mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) e0 = np.array([1, 0, 0.]) e1 = np.array([0., 1, 0]) mevecs = [all_tensor_evecs(e0), all_tensor_evecs(e1)] odf = multi_tensor_odf(vertices, [0.5, 0.5], mevals, mevecs) assert odf.shape == (len(vertices), ) assert np.all(odf <= 1) & np.all(odf >= 0)
def test_multi_tensor(): vertices, faces = get_sphere('symmetric724') mevals=np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) e0 = np.array([1, 0, 0.]) e1 = np.array([0., 1, 0]) mevecs=[all_tensor_evecs(e0), all_tensor_evecs(e1)] odf = multi_tensor_odf(vertices, [0.5,0.5], mevals, mevecs) assert odf.shape == (len(vertices),) assert np.all(odf <= 1) & np.all(odf >= 0)
def test_odfdeconv(): SNR = 100 S0 = 1 _, fbvals, fbvecs = get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) S, sticks = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (90, 0)], fractions=[50, 50], snr=SNR) sphere = get_sphere('symmetric724') mevecs = [all_tensor_evecs(sticks[0]).T, all_tensor_evecs(sticks[1]).T] odf_gt = multi_tensor_odf(sphere.vertices, [0.5, 0.5], mevals, mevecs) e1 = 15.0 e2 = 3.0 ratio = e2 / e1 csd = ConstrainedSDTModel(gtab, ratio, None) csd_fit = csd.fit(S) fodf = csd_fit.odf(sphere) directions, _, _ = peak_directions(odf_gt, sphere) directions2, _, _ = peak_directions(fodf, sphere) ang_sim = angular_similarity(directions, directions2) assert_equal(ang_sim > 1.9, True) assert_equal(directions.shape[0], 2) assert_equal(directions2.shape[0], 2) with warnings.catch_warnings(record=True) as w: ConstrainedSDTModel(gtab, ratio, sh_order=10) assert_equal(len(w) > 0, True) with warnings.catch_warnings(record=True) as w: ConstrainedSDTModel(gtab, ratio, sh_order=8) assert_equal(len(w) > 0, False)
def sim_tensor_2x(gtab, angle=90, sphere=None, S0=1., snr=None): mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) data, sticks = multi_tensor(gtab, mevals, S0, angles=[(90 , 0), (90, angle)], fractions=[50, 50], snr=snr) mevecs = [all_tensor_evecs(sticks[0]).T, all_tensor_evecs(sticks[1]).T] odf_gt = multi_tensor_odf(sphere.vertices, [0.5, 0.5], mevals, mevecs) return data, sticks, odf_gt
def test_all_tensor_evecs(): e0 = np.array([1 / np.sqrt(2), 1 / np.sqrt(2), 0]) desired = np.array([[1 / np.sqrt(2), 1 / np.sqrt(2), 0], [-1 / np.sqrt(2), 1 / np.sqrt(2), 0], [0, 0, 1]]) assert_array_almost_equal(all_tensor_evecs(e0), desired)
def test_fwdti_predictions(): # single voxel case gtf = 0.50 # ground truth volume fraction angles = [(90, 0), (90, 0)] mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]]) S_conta, peaks = multi_tensor(gtab_2s, mevals, S0=100, angles=angles, fractions=[(1 - gtf) * 100, gtf * 100], snr=None) R = all_tensor_evecs(peaks[0]) R = R.reshape((9)) model_params = np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0) S_pred1 = fwdti_prediction(model_params, gtab_2s, S0=100) assert_array_almost_equal(S_pred1, S_conta) # Testing in model class fwdm = fwdti.FreeWaterTensorModel(gtab_2s) S_pred2 = fwdm.predict(model_params, S0=100) assert_array_almost_equal(S_pred2, S_conta) # Testing in fit class fwefit = fwdm.fit(S_conta) S_pred3 = fwefit.predict(gtab_2s, S0=100) assert_array_almost_equal(S_pred3, S_conta, decimal=5) # Multi voxel simulation S_pred1 = fwdti_prediction(model_params_mv, gtab_2s, S0=100) # function assert_array_almost_equal(S_pred1, DWI) S_pred2 = fwdm.predict(model_params_mv, S0=100) # Model class assert_array_almost_equal(S_pred2, DWI) fwefit = fwdm.fit(DWI) # Fit class S_pred3 = fwefit.predict(gtab_2s, S0=100) assert_array_almost_equal(S_pred3, DWI)
def create_tensor_image(mask_img, direction, prefix): """set intent as NIFTI_INTENT_SYMMATRIX (1005), [dxx, dxy, dyy, dxz, dyz, dzz] are the components info from here https://github.com/ANTsX/ANTs/wiki/Importing-diffusion-tensor-data-from-other-software """ out_fname = prefix + "_tensor.nii" evecs = all_tensor_evecs(direction) evals = np.diag([1.0, 0.5, 0.05]) tensor = np.linalg.multi_dot([evecs, evals, evecs.T]) temp_components = [] for direction in ['xx', 'xy', 'xz', 'yy', 'yz', 'zz']: this_component = prefix + '_temp_dtiComp_%s.nii.gz' % direction LOGGER.info("writing %s", this_component) nb.Nifti1Image(mask_img.get_data() * tensor[tensor_index[direction]], mask_img.affine, mask_img.header).to_filename(this_component) temp_components.append(this_component) compose_cmd = 'ImageMath 3 %s ComponentTo3DTensor %s' % ( out_fname, prefix + '_temp_dtiComp_') LOGGER.info(compose_cmd) os.system(compose_cmd) for temp_component in temp_components: os.remove(temp_component) return out_fname
def test_fwdti_predictions(): # single voxel case gtf = 0.50 # ground truth volume fraction angles = [(90, 0), (90, 0)] mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]]) S_conta, peaks = multi_tensor(gtab_2s, mevals, S0=100, angles=angles, fractions=[(1-gtf) * 100, gtf*100], snr=None) R = all_tensor_evecs(peaks[0]) R = R.reshape((9)) model_params = np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0) S_pred1 = fwdti_prediction(model_params, gtab_2s, S0=100) assert_array_almost_equal(S_pred1, S_conta) # Testing in model class fwdm = fwdti.FreeWaterTensorModel(gtab_2s) S_pred2 = fwdm.predict(model_params, S0=100) assert_array_almost_equal(S_pred2, S_conta) # Testing in fit class fwefit = fwdm.fit(S_conta) S_pred3 = fwefit.predict(gtab_2s, S0=100) assert_array_almost_equal(S_pred3, S_conta, decimal=5) # Multi voxel simulation S_pred1 = fwdti_prediction(model_params_mv, gtab_2s, S0=100) # function assert_array_almost_equal(S_pred1, DWI) S_pred2 = fwdm.predict(model_params_mv, S0=100) # Model class assert_array_almost_equal(S_pred2, DWI) fwefit = fwdm.fit(DWI) # Fit class S_pred3 = fwefit.predict(gtab_2s, S0=100) assert_array_almost_equal(S_pred3, DWI)
def test_all_tensor_evecs(): e0 = np.array([1 / np.sqrt(2), 1 / np.sqrt(2), 0]) # Vectors are returned column-wise! desired = np.array([[1 / np.sqrt(2), 1 / np.sqrt(2), 0], [-1 / np.sqrt(2), 1 / np.sqrt(2), 0], [0, 0, 1]]).T assert_array_almost_equal(all_tensor_evecs(e0), desired)
def test_all_tensor_evecs(): e0 = np.array([1/np.sqrt(2), 1/np.sqrt(2), 0]) desired = np.array([[1/np.sqrt(2), 1/np.sqrt(2), 0], [-1/np.sqrt(2), 1/np.sqrt(2), 0], [0, 0, 1]]).T assert_array_almost_equal(all_tensor_evecs(e0), desired)
def test_all_tensor_evecs(): e0 = np.array([1/np.sqrt(2), 1/np.sqrt(2), 0]) # Vectors are returned column-wise! desired = np.array([[1/np.sqrt(2), 1/np.sqrt(2), 0], [-1/np.sqrt(2), 1/np.sqrt(2), 0], [0, 0, 1]]).T assert_array_almost_equal(all_tensor_evecs(e0), desired)
def setup_module(): """Module-level setup""" global gtab, gtab_2s, mevals, model_params_mv global DWI, FAref, GTF, MDref, FAdti, MDdti _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) # FW model requires multishell data bvals_2s = np.concatenate((bvals, bvals * 1.5), axis=0) bvecs_2s = np.concatenate((bvecs, bvecs), axis=0) gtab_2s = gradient_table(bvals_2s, bvecs_2s) # Simulation a typical DT and DW signal for no water contamination S0 = np.array(100) dt = np.array([0.0017, 0, 0.0003, 0, 0, 0.0003]) evals, evecs = decompose_tensor(from_lower_triangular(dt)) S_tissue = single_tensor(gtab_2s, S0=100, evals=evals, evecs=evecs, snr=None) dm = dti.TensorModel(gtab_2s, 'WLS') dtifit = dm.fit(S_tissue) FAdti = dtifit.fa MDdti = dtifit.md dtiparams = dtifit.model_params # Simulation of 8 voxels tested DWI = np.zeros((2, 2, 2, len(gtab_2s.bvals))) FAref = np.zeros((2, 2, 2)) MDref = np.zeros((2, 2, 2)) # Diffusion of tissue and water compartments are constant for all voxel mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]]) # volume fractions GTF = np.array([[[0.06, 0.71], [0.33, 0.91]], [[0., 0.], [0., 0.]]]) # S0 multivoxel S0m = 100 * np.ones((2, 2, 2)) # model_params ground truth (to be fill) model_params_mv = np.zeros((2, 2, 2, 13)) for i in range(2): for j in range(2): gtf = GTF[0, i, j] S, p = multi_tensor(gtab_2s, mevals, S0=100, angles=[(90, 0), (90, 0)], fractions=[(1 - gtf) * 100, gtf * 100], snr=None) DWI[0, i, j] = S FAref[0, i, j] = FAdti MDref[0, i, j] = MDdti R = all_tensor_evecs(p[0]) R = R.reshape((9)) model_params_mv[0, i, j] = \ np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0)
def setup_module(): """Module-level setup""" global gtab, gtab_2s, mevals, model_params_mv global DWI, FAref, GTF, MDref, FAdti, MDdti _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) # FW model requires multishell data bvals_2s = np.concatenate((bvals, bvals * 1.5), axis=0) bvecs_2s = np.concatenate((bvecs, bvecs), axis=0) gtab_2s = gradient_table(bvals_2s, bvecs_2s) # Simulation a typical DT and DW signal for no water contamination S0 = np.array(100) dt = np.array([0.0017, 0, 0.0003, 0, 0, 0.0003]) evals, evecs = decompose_tensor(from_lower_triangular(dt)) S_tissue = single_tensor(gtab_2s, S0=100, evals=evals, evecs=evecs, snr=None) dm = dti.TensorModel(gtab_2s, 'WLS') dtifit = dm.fit(S_tissue) FAdti = dtifit.fa MDdti = dtifit.md dtiparams = dtifit.model_params # Simulation of 8 voxels tested DWI = np.zeros((2, 2, 2, len(gtab_2s.bvals))) FAref = np.zeros((2, 2, 2)) MDref = np.zeros((2, 2, 2)) # Diffusion of tissue and water compartments are constant for all voxel mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]]) # volume fractions GTF = np.array([[[0.06, 0.71], [0.33, 0.91]], [[0., 0.], [0., 0.]]]) # S0 multivoxel S0m = 100 * np.ones((2, 2, 2)) # model_params ground truth (to be fill) model_params_mv = np.zeros((2, 2, 2, 13)) for i in range(2): for j in range(2): gtf = GTF[0, i, j] S, p = multi_tensor(gtab_2s, mevals, S0=100, angles=[(90, 0), (90, 0)], fractions=[(1-gtf) * 100, gtf*100], snr=None) DWI[0, i, j] = S FAref[0, i, j] = FAdti MDref[0, i, j] = MDdti R = all_tensor_evecs(p[0]) R = R.reshape((9)) model_params_mv[0, i, j] = \ np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0)
def test_multi_tensor_btens(): """ Testing multi tensor simulations when a btensor is given """ mevals = np.array(([0.003, 0.0002, 0.0002], [0.0015, 0.0003, 0.0003])) e0 = np.array([np.sqrt(2) / 2., np.sqrt(2) / 2., 0]) e1 = np.array([0, np.sqrt(2) / 2., np.sqrt(2) / 2.]) mevecs = [all_tensor_evecs(e0), all_tensor_evecs(e1)] gtab_ste = gradient_table(gtab.bvals, gtab.bvecs, btens='STE') s1 = single_tensor(gtab_ste, 100, mevals[0], mevecs[0], snr=None) s2 = single_tensor(gtab_ste, 100, mevals[1], mevecs[1], snr=None) Ssingle = 0.5 * s1 + 0.5 * s2 S, _ = multi_tensor(gtab_ste, mevals, S0=100, angles=[(90, 45), (45, 90)], fractions=[50, 50], snr=None) assert_array_almost_equal(S, Ssingle)
def test_shore_metrics(): fetch_taiwan_ntu_dsi() img, gtab = read_taiwan_ntu_dsi() mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) angl = [(0, 0), (60, 0)] S, sticks = MultiTensor(gtab, mevals, S0=100, angles=angl, fractions=[50, 50], snr=None) S = S / S[0, None].astype(np.float) radial_order = 8 zeta = 800 lambdaN = 1e-12 lambdaL = 1e-12 asm = ShoreModel(gtab, radial_order=radial_order, zeta=zeta, lambdaN=lambdaN, lambdaL=lambdaL) asmfit = asm.fit(S) c_shore= asmfit.shore_coeff cmat = SHOREmatrix(radial_order, zeta, gtab) S_reconst = np.dot(cmat, c_shore) nmse_signal = np.sqrt(np.sum((S - S_reconst) ** 2)) / (S.sum()) assert_almost_equal(nmse_signal, 0.0, 4) mevecs2 = np.zeros((2, 3, 3)) angl = np.array(angl) for i in range(2): mevecs2[i] = all_tensor_evecs(sticks[i]).T sphere = get_sphere('symmetric724') v = sphere.vertices radius = 10e-3 pdf_shore = asmfit.pdf(v * radius) pdf_mt = multi_tensor_pdf(v * radius, [.5, .5], mevals=mevals, mevecs=mevecs2) nmse_pdf = np.sqrt(np.sum((pdf_mt - pdf_shore) ** 2)) / (pdf_mt.sum()) assert_almost_equal(nmse_pdf, 0.0, 2) rtop_shore_signal = asmfit.rtop_signal() rtop_shore_pdf = asmfit.rtop_pdf() assert_almost_equal(rtop_shore_signal, rtop_shore_pdf, 9) rtop_mt = multi_tensor_rtop([.5, .5], mevals=mevals) assert_equal(rtop_mt/rtop_shore_signal < 1.12 and rtop_mt/rtop_shore_signal > 0.9 , True) msd_mt = multi_tensor_msd([.5, .5], mevals=mevals) msd_shore = asmfit.msd() assert_equal(msd_mt/msd_shore < 1.05 and msd_mt/msd_shore > 0.95 , True)
def sfm_design_matrix(gtab, sphere, response, mode='signal'): """ Construct the SFM design matrix Parameters ---------- gtab : GradientTable or Sphere Sets the rows of the matrix, if the mode is 'signal', this should be a GradientTable. If mode is 'odf' this should be a Sphere sphere : Sphere Sets the columns of the matrix response : list of 3 elements The eigenvalues of a tensor which will serve as a kernel function. mode : str {'signal' | 'odf'}, optional Choose the (default) 'signal' for a design matrix containing predicted signal in the measurements defined by the gradient table for putative fascicles oriented along the vertices of the sphere. Otherwise, choose 'odf' for an odf convolution matrix, with values of the odf calculated from a tensor with the provided response eigenvalues, evaluated at the b-vectors in the gradient table, for the tensors with prinicipal diffusion directions along the vertices of the sphere. Returns ------- mat : ndarray A design matrix that can be used for one of the following operations: when the 'signal' mode is used, each column contains the putative signal in each of the bvectors of the `gtab` if a fascicle is oriented in the direction encoded by the sphere vertex corresponding to this column. This is used for deconvolution with a measured DWI signal. If the 'odf' mode is chosen, each column instead contains the values of the tensor ODF for a tensor with a principal diffusion direction corresponding to this vertex. This is used to generate odfs from the fits of the SFM for the purpose of tracking. Examples -------- >>> import dipy.data as dpd >>> data, gtab = dpd.dsi_voxels() >>> sphere = dpd.get_sphere() >>> from dipy.reconst.sfm import sfm_design_matrix A canonical tensor approximating corpus-callosum voxels [Rokem2014]_: >>> tensor_matrix = sfm_design_matrix(gtab, sphere, ... [0.0015, 0.0005, 0.0005]) A 'stick' function ([Behrens2007]_): >>> stick_matrix = sfm_design_matrix(gtab, sphere, [0.001, 0, 0]) Notes ----- .. [Rokem2015] Ariel Rokem, Jason D. Yeatman, Franco Pestilli, Kendrick N. Kay, Aviv Mezer, Stefan van der Walt, Brian A. Wandell (2015). Evaluating the accuracy of diffusion MRI models in white matter. PLoS ONE 10(4): e0123272. doi:10.1371/journal.pone.0123272 .. [Rokem2014] Ariel Rokem, Kimberly L. Chan, Jason D. Yeatman, Franco Pestilli, Brian A. Wandell (2014). Evaluating the accuracy of diffusion models at multiple b-values with cross-validation. ISMRM 2014. .. [Behrens2007] Behrens TEJ, Berg HJ, Jbabdi S, Rushworth MFS, Woolrich MW (2007): Probabilistic diffusion tractography with multiple fibre orientations: What can we gain? Neuroimage 34:144-55. """ if mode == 'signal': mat_gtab = grad.gradient_table(gtab.bvals[~gtab.b0s_mask], gtab.bvecs[~gtab.b0s_mask]) # Preallocate: mat = np.empty((np.sum(~gtab.b0s_mask), sphere.vertices.shape[0])) elif mode == 'odf': mat = np.empty((gtab.x.shape[0], sphere.vertices.shape[0])) # Calculate column-wise: for ii, this_dir in enumerate(sphere.vertices): # Rotate the canonical tensor towards this vertex and calculate the # signal you would have gotten in the direction evecs = sims.all_tensor_evecs(this_dir) if mode == 'signal': sig = sims.single_tensor(mat_gtab, evals=response, evecs=evecs) # For regressors based on the single tensor, remove $e^{-bD}$ iso_sig = np.exp(-mat_gtab.bvals * np.mean(response)) mat[:, ii] = sig - iso_sig elif mode == 'odf': # Stick function if response[1] == 0 or response[2] == 0: jj = sphere.find_closest(evecs[0]) mat[jj, ii] = 1 else: odf = sims.single_tensor_odf(gtab.vertices, evals=response, evecs=evecs) mat[:, ii] = odf return mat
def test_csdeconv(): SNR = 100 S0 = 1 _, fbvals, fbvecs = get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) angles = [(0, 0), (60, 0)] S, sticks = multi_tensor(gtab, mevals, S0, angles=angles, fractions=[50, 50], snr=SNR) sphere = get_sphere('symmetric362') odf_gt = multi_tensor_odf(sphere.vertices, mevals, angles, [50, 50]) response = (np.array([0.0015, 0.0003, 0.0003]), S0) csd = ConstrainedSphericalDeconvModel(gtab, response) csd_fit = csd.fit(S) assert_equal(csd_fit.shm_coeff[0] > 0, True) fodf = csd_fit.odf(sphere) directions, _, _ = peak_directions(odf_gt, sphere) directions2, _, _ = peak_directions(fodf, sphere) ang_sim = angular_similarity(directions, directions2) assert_equal(ang_sim > 1.9, True) assert_equal(directions.shape[0], 2) assert_equal(directions2.shape[0], 2) with warnings.catch_warnings(record=True) as w: ConstrainedSphericalDeconvModel(gtab, response, sh_order=10) assert_equal(len(w) > 0, True) with warnings.catch_warnings(record=True) as w: ConstrainedSphericalDeconvModel(gtab, response, sh_order=8) assert_equal(len(w) > 0, False) mevecs = [] for s in sticks: mevecs += [all_tensor_evecs(s).T] S2 = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None) big_S = np.zeros((10, 10, 10, len(S2))) big_S[:] = S2 aresponse, aratio = auto_response(gtab, big_S, roi_center=(5, 5, 4), roi_radius=3, fa_thr=0.5) assert_array_almost_equal(aresponse[0], response[0]) assert_almost_equal(aresponse[1], 100) assert_almost_equal(aratio, response[0][1]/response[0][0]) aresponse2, aratio2 = auto_response(gtab, big_S, roi_radius=3, fa_thr=0.5) assert_array_almost_equal(aresponse[0], response[0])
def test_kurtosis_elements(): """ Testing symmetry of the elements of the KT As an 4th order tensor, KT has 81 elements. However, due to diffusion symmetry the KT is fully characterized by 15 independent elements. This test checks for this property. """ # two fiber not aligned to planes x = 0, y = 0, or z = 0 mevals = np.array([[0.00099, 0, 0], [0.00226, 0.00087, 0.00087], [0.00099, 0, 0], [0.00226, 0.00087, 0.00087]]) angles = [(80, 10), (80, 10), (20, 30), (20, 30)] fie = 0.49 # intra axonal water fraction frac = [fie * 50, (1-fie) * 50, fie * 50, (1-fie) * 50] sticks = _check_directions(angles) mD = np.zeros((len(frac), 3, 3)) for i in range(len(frac)): R = all_tensor_evecs(sticks[i]) mD[i] = np.dot(np.dot(R, np.diag(mevals[i])), R.T) # compute global DT D = np.zeros((3, 3)) for i in range(len(frac)): D = D + frac[i]*mD[i] # compute voxel's MD MD = (D[0][0] + D[1][1] + D[2][2]) / 3 # Reference dictionary with the 15 independent elements. # Note: The multiplication of the indexes (i+1) * (j+1) * (k+1) * (l+1) # for of an elements is only equal to this multiplication for another # element if an only if the element corresponds to an symmetry element. # Thus indexes multiplication is used as key of the reference dictionary kt_ref = {1: kurtosis_element(mD, frac, 0, 0, 0, 0), 16: kurtosis_element(mD, frac, 1, 1, 1, 1), 81: kurtosis_element(mD, frac, 2, 2, 2, 2), 2: kurtosis_element(mD, frac, 0, 0, 0, 1), 3: kurtosis_element(mD, frac, 0, 0, 0, 2), 8: kurtosis_element(mD, frac, 0, 1, 1, 1), 24: kurtosis_element(mD, frac, 1, 1, 1, 2), 27: kurtosis_element(mD, frac, 0, 2, 2, 2), 54: kurtosis_element(mD, frac, 1, 2, 2, 2), 4: kurtosis_element(mD, frac, 0, 0, 1, 1), 9: kurtosis_element(mD, frac, 0, 0, 2, 2), 36: kurtosis_element(mD, frac, 1, 1, 2, 2), 6: kurtosis_element(mD, frac, 0, 0, 1, 2), 12: kurtosis_element(mD, frac, 0, 1, 1, 2), 18: kurtosis_element(mD, frac, 0, 1, 2, 2)} # Testing all 81 possible elements xyz = [0, 1, 2] for i in xyz: for j in xyz: for k in xyz: for l in xyz: key = (i+1) * (j+1) * (k+1) * (l+1) assert_almost_equal(kurtosis_element(mD, frac, i, k, j, l), kt_ref[key]) # Testing optional funtion inputs assert_almost_equal(kurtosis_element(mD, frac, i, k, j, l), kurtosis_element(mD, frac, i, k, j, l, D, MD))
GTF = np.array([[[0.06, 0.71], [0.33, 0.91]], [[0., 0.], [0., 0.]]]) # S0 multivoxel S0m = 100 * np.ones((2, 2, 2)) # model_params ground truth (to be fill) model_params_mv = np.zeros((2, 2, 2, 13)) for i in range(2): for j in range(2): gtf = GTF[0, i, j] S, p = multi_tensor(gtab_2s, mevals, S0=100, angles=[(90, 0), (90, 0)], fractions=[(1-gtf) * 100, gtf*100], snr=None) DWI[0, i, j] = S FAref[0, i, j] = FAdti MDref[0, i, j] = MDdti R = all_tensor_evecs(p[0]) R = R.reshape((9)) model_params_mv[0, i, j] = np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0) def test_fwdti_singlevoxel(): # Simulation when water contamination is added gtf = 0.44444 # ground truth volume fraction mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]]) S_conta, peaks = multi_tensor(gtab_2s, mevals, S0=100, angles=[(90, 0), (90, 0)], fractions=[(1-gtf) * 100, gtf*100], snr=None) fwdm = fwdti.FreeWaterTensorModel(gtab_2s, 'WLS') fwefit = fwdm.fit(S_conta) FAfwe = fwefit.fa
def test_csdeconv(): SNR = 100 S0 = 1 _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs, b0_threshold=0) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) angles = [(0, 0), (60, 0)] S, sticks = multi_tensor(gtab, mevals, S0, angles=angles, fractions=[50, 50], snr=SNR) sphere = get_sphere('symmetric362') odf_gt = multi_tensor_odf(sphere.vertices, mevals, angles, [50, 50]) response = (np.array([0.0015, 0.0003, 0.0003]), S0) csd = ConstrainedSphericalDeconvModel(gtab, response) csd_fit = csd.fit(S) assert_equal(csd_fit.shm_coeff[0] > 0, True) fodf = csd_fit.odf(sphere) directions, _, _ = peak_directions(odf_gt, sphere) directions2, _, _ = peak_directions(fodf, sphere) ang_sim = angular_similarity(directions, directions2) assert_equal(ang_sim > 1.9, True) assert_equal(directions.shape[0], 2) assert_equal(directions2.shape[0], 2) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) _ = ConstrainedSphericalDeconvModel(gtab, response, sh_order=10) assert_greater(len([lw for lw in w if issubclass(lw.category, UserWarning)]), 0) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) ConstrainedSphericalDeconvModel(gtab, response, sh_order=8) assert_equal(len([lw for lw in w if issubclass(lw.category, UserWarning)]), 0) mevecs = [] for s in sticks: mevecs += [all_tensor_evecs(s).T] S2 = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None) big_S = np.zeros((10, 10, 10, len(S2))) big_S[:] = S2 aresponse, aratio = auto_response(gtab, big_S, roi_center=(5, 5, 4), roi_radius=3, fa_thr=0.5) assert_array_almost_equal(aresponse[0], response[0]) assert_almost_equal(aresponse[1], 100) assert_almost_equal(aratio, response[0][1] / response[0][0]) auto_response(gtab, big_S, roi_radius=3, fa_thr=0.5) assert_array_almost_equal(aresponse[0], response[0]) _, _, nvoxels = auto_response(gtab, big_S, roi_center=(5, 5, 4), roi_radius=30, fa_thr=0.5, return_number_of_voxels=True) assert_equal(nvoxels, 1000) _, _, nvoxels = auto_response(gtab, big_S, roi_center=(5, 5, 4), roi_radius=30, fa_thr=1, return_number_of_voxels=True) assert_equal(nvoxels, 0)
def sfm_design_matrix(gtab, sphere, response, mode='signal'): """ Construct the SFM design matrix Parameters ---------- gtab : GradientTable or Sphere Sets the rows of the matrix, if the mode is 'signal', this should be a GradientTable. If mode is 'odf' this should be a Sphere sphere : Sphere Sets the columns of the matrix response : list of 3 elements The eigenvalues of a tensor which will serve as a kernel function. mode : str {'signal' | 'odf'}, optional Choose the (default) 'signal' for a design matrix containing predicted signal in the measurements defined by the gradient table for putative fascicles oriented along the vertices of the sphere. Otherwise, choose 'odf' for an odf convolution matrix, with values of the odf calculated from a tensor with the provided response eigenvalues, evaluated at the b-vectors in the gradient table, for the tensors with prinicipal diffusion directions along the vertices of the sphere. Returns ------- mat : ndarray A design matrix that can be used for one of the following operations: when the 'signal' mode is used, each column contains the putative signal in each of the bvectors of the `gtab` if a fascicle is oriented in the direction encoded by the sphere vertex corresponding to this column. This is used for deconvolution with a measured DWI signal. If the 'odf' mode is chosen, each column instead contains the values of the tensor ODF for a tensor with a principal diffusion direction corresponding to this vertex. This is used to generate odfs from the fits of the SFM for the purpose of tracking. Examples -------- >>> import dipy.data as dpd >>> data, gtab = dpd.dsi_voxels() >>> sphere = dpd.get_sphere() >>> from dipy.reconst.sfm import sfm_design_matrix A canonical tensor approximating corpus-callosum voxels [Rokem2014]_: >>> tensor_matrix = sfm_design_matrix(gtab, sphere, [0.0015, 0.0005, 0.0005]) A 'stick' function ([Behrens2007]_): >>> stick_matrix = sfm_design_matrix(gtab, sphere, [0.001, 0, 0]) Notes ----- .. [Rokem2014a] Ariel Rokem, Jason D. Yeatman, Franco Pestilli, Kendrick N. Kay, Aviv Mezer, Stefan van der Walt, Brian A. Wandell (2014). Evaluating the accuracy of diffusion MRI models in white matter. http://arxiv.org/abs/1411.0721 .. [Rokem2014b] Ariel Rokem, Kimberly L. Chan, Jason D. Yeatman, Franco Pestilli, Brian A. Wandell (2014). Evaluating the accuracy of diffusion models at multiple b-values with cross-validation. ISMRM 2014. .. [Behrens2007] Behrens TEJ, Berg HJ, Jbabdi S, Rushworth MFS, Woolrich MW (2007): Probabilistic diffusion tractography with multiple fibre orientations: What can we gain? Neuroimage 34:144-55. """ if mode == 'signal': mat_gtab = grad.gradient_table(gtab.bvals[~gtab.b0s_mask], gtab.bvecs[~gtab.b0s_mask]) # Preallocate: mat = np.empty((np.sum(~gtab.b0s_mask), sphere.vertices.shape[0])) elif mode == 'odf': mat = np.empty((gtab.x.shape[0], sphere.vertices.shape[0])) # Calculate column-wise: for ii, this_dir in enumerate(sphere.vertices): # Rotate the canonical tensor towards this vertex and calculate the # signal you would have gotten in the direction evecs = sims.all_tensor_evecs(this_dir) if mode == 'signal': sig = sims.single_tensor(mat_gtab, evals=response, evecs=evecs) # For regressors based on the single tensor, remove $e^{-bD}$ iso_sig = np.exp(-mat_gtab.bvals * np.mean(response)) mat[:, ii] = sig - iso_sig elif mode == 'odf': # Stick function if response[1] == 0 or response[2] == 0: jj = sphere.find_closest(evecs[0]) mat[jj, ii] = 1 else: odf = sims.single_tensor_odf(gtab.vertices, evals=response, evecs=evecs) mat[:, ii] = odf return mat
S0m = 100 * np.ones((2, 2, 2)) # model_params ground truth (to be fill) model_params_mv = np.zeros((2, 2, 2, 13)) for i in range(2): for j in range(2): gtf = GTF[0, i, j] S, p = multi_tensor(gtab_2s, mevals, S0=100, angles=[(90, 0), (90, 0)], fractions=[(1 - gtf) * 100, gtf * 100], snr=None) DWI[0, i, j] = S FAref[0, i, j] = FAdti MDref[0, i, j] = MDdti R = all_tensor_evecs(p[0]) R = R.reshape((9)) model_params_mv[0, i, j] = np.concatenate( ([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0) def test_fwdti_singlevoxel(): # Simulation when water contamination is added gtf = 0.44444 # ground truth volume fraction mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]]) S_conta, peaks = multi_tensor(gtab_2s, mevals, S0=100, angles=[(90, 0), (90, 0)], fractions=[(1 - gtf) * 100, gtf * 100], snr=None)
def test_kurtosis_elements(): """ Testing symmetry of the elements of the KT As an 4th order tensor, KT has 81 elements. However, due to diffusion symmetry the KT is fully characterized by 15 independent elements. This test checks for this property. """ # two fiber not aligned to planes x = 0, y = 0, or z = 0 mevals = np.array([[0.00099, 0, 0], [0.00226, 0.00087, 0.00087], [0.00099, 0, 0], [0.00226, 0.00087, 0.00087]]) angles = [(80, 10), (80, 10), (20, 30), (20, 30)] fie = 0.49 # intra axonal water fraction frac = [fie * 50, (1 - fie) * 50, fie * 50, (1 - fie) * 50] sticks = _check_directions(angles) mD = np.zeros((len(frac), 3, 3)) for i in range(len(frac)): R = all_tensor_evecs(sticks[i]) mD[i] = np.dot(np.dot(R, np.diag(mevals[i])), R.T) # compute global DT D = np.zeros((3, 3)) for i in range(len(frac)): D = D + frac[i] * mD[i] # compute voxel's MD MD = (D[0][0] + D[1][1] + D[2][2]) / 3 # Reference dictionary with the 15 independent elements. # Note: The multiplication of the indexes (i+1) * (j+1) * (k+1) * (l+1) # for of an elements is only equal to this multiplication for another # element if an only if the element corresponds to an symmetry element. # Thus indexes multiplication is used as key of the reference dictionary kt_ref = { 1: kurtosis_element(mD, frac, 0, 0, 0, 0), 16: kurtosis_element(mD, frac, 1, 1, 1, 1), 81: kurtosis_element(mD, frac, 2, 2, 2, 2), 2: kurtosis_element(mD, frac, 0, 0, 0, 1), 3: kurtosis_element(mD, frac, 0, 0, 0, 2), 8: kurtosis_element(mD, frac, 0, 1, 1, 1), 24: kurtosis_element(mD, frac, 1, 1, 1, 2), 27: kurtosis_element(mD, frac, 0, 2, 2, 2), 54: kurtosis_element(mD, frac, 1, 2, 2, 2), 4: kurtosis_element(mD, frac, 0, 0, 1, 1), 9: kurtosis_element(mD, frac, 0, 0, 2, 2), 36: kurtosis_element(mD, frac, 1, 1, 2, 2), 6: kurtosis_element(mD, frac, 0, 0, 1, 2), 12: kurtosis_element(mD, frac, 0, 1, 1, 2), 18: kurtosis_element(mD, frac, 0, 1, 2, 2) } # Testing all 81 possible elements xyz = [0, 1, 2] for i in xyz: for j in xyz: for k in xyz: for l in xyz: key = (i + 1) * (j + 1) * (k + 1) * (l + 1) assert_almost_equal(kurtosis_element(mD, frac, i, k, j, l), kt_ref[key]) # Testing optional function inputs assert_almost_equal( kurtosis_element(mD, frac, i, k, j, l), kurtosis_element(mD, frac, i, k, j, l, D, MD))
def generate_kernel(gtab, sphere, wm_response, gm_response, csf_response): ''' Generate deconvolution kernel Compute kernel mapping orientation densities of white matter fiber populations (along each vertex of the sphere) and isotropic volume fractions to a diffusion weighted signal. Parameters ---------- gtab : GradientTable sphere : Sphere Sphere with which to sample discrete fiber orientations in order to construct kernel wm_response : 1d ndarray or 2d ndarray or AxSymShResponse, optional Tensor eigenvalues as a (3,) ndarray, multishell eigenvalues as a (len(unique_bvals_tolerance(gtab.bvals))-1, 3) ndarray in order of smallest to largest b-value, or an AxSymShResponse. gm_response : float, optional Mean diffusivity for GM compartment. If `None`, then grey matter compartment set to all zeros. csf_response : float, optional Mean diffusivity for CSF compartment. If `None`, then CSF compartment set to all zeros. Returns ------- kernel : 2d ndarray (N, M) Computed kernel; can be multiplied with a vector consisting of volume fractions for each of M-2 fiber populations as well as GM and CSF fractions to produce a diffusion weighted signal. ''' # Coordinates of sphere vertices sticks = sphere.vertices n_grad = len(gtab.gradients) # number of gradient directions n_wm_comp = sticks.shape[0] # number of fiber populations n_comp = n_wm_comp + 2 # plus isotropic compartments kernel = np.zeros((n_grad, n_comp)) # White matter compartments list_bvals = unique_bvals_tolerance(gtab.bvals) n_bvals = len(list_bvals) - 1 # number of unique b-values if isinstance(wm_response, AxSymShResponse): # Data-driven response where_dwi = lazy_index(~gtab.b0s_mask) gradients = gtab.gradients[where_dwi] gradients = gradients / np.linalg.norm(gradients, axis=1)[..., None] S0 = wm_response.S0 for i in range(n_wm_comp): # Response oriented along [0, 0, 1], so must rotate sticks[i] rot_mat = vec2vec_rotmat(sticks[i], np.array([0, 0, 1])) rot_gradients = np.dot(rot_mat, gradients.T).T rot_sphere = Sphere(xyz=rot_gradients) # Project onto rotated sphere and scale rot_response = wm_response.on_sphere(rot_sphere) / S0 kernel[where_dwi, i] = rot_response # Set b0 components kernel[gtab.b0s_mask, :] = 1 elif wm_response.shape == (n_bvals, 3): # Multi-shell response bvals = gtab.bvals bvecs = gtab.bvecs for n, bval in enumerate(list_bvals[1:]): indices = get_bval_indices(bvals, bval) with warnings.catch_warnings(): # extract relevant b-value warnings.simplefilter("ignore") gtab_sub = gradient_table(bvals[indices], bvecs[indices]) for i in range(n_wm_comp): # Signal generated by WM-fiber for each gradient direction S = single_tensor(gtab_sub, evals=wm_response[n], evecs=all_tensor_evecs(sticks[i])) kernel[indices, i] = S # Set b0 components b0_indices = get_bval_indices(bvals, list_bvals[0]) kernel[b0_indices, :] = 1 else: # Single-shell response for i in range(n_wm_comp): # Signal generated by WM-fiber for each gradient direction S = single_tensor(gtab, evals=wm_response, evecs=all_tensor_evecs(sticks[i])) kernel[:, i] = S # Set b0 components kernel[gtab.b0s_mask, :] = 1 # GM compartment if gm_response is None: S_gm = np.zeros((n_grad)) else: S_gm = \ single_tensor(gtab, evals=np.array( [gm_response, gm_response, gm_response])) if csf_response is None: S_csf = np.zeros((n_grad)) else: S_csf = \ single_tensor(gtab, evals=np.array( [csf_response, csf_response, csf_response])) kernel[:, n_comp - 2] = S_gm kernel[:, n_comp - 1] = S_csf return kernel