""" 0.21197 We can double-check that we have a good response function by visualizing the response function's ODF. Here is how you would do that: """ from dipy.viz import fvtk ren = fvtk.ren() evals = response[0] evecs = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]).T from dipy.data import get_sphere sphere = get_sphere('symmetric724') from dipy.sims.voxel import single_tensor_odf response_odf = single_tensor_odf(sphere.vertices, evals, evecs) response_actor = fvtk.sphere_funcs(response_odf, sphere) fvtk.add(ren, response_actor) print('Saving illustration as csd_response.png') fvtk.record(ren, out_path='csd_response.png', size=(200, 200)) """ .. figure:: csd_response.png :align: center **Estimated response function**. """ fvtk.rm(ren, response_actor)
def test_recursive_response_calibration(): """ Test the recursive response calibration method. """ SNR = 100 S0 = 1 sh_order = 8 _, fbvals, fbvecs = get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) sphere = get_sphere('symmetric724') gtab = gradient_table(bvals, bvecs) evals = np.array([0.0015, 0.0003, 0.0003]) evecs = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]).T mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) angles = [(0, 0), (90, 0)] where_dwi = lazy_index(~gtab.b0s_mask) S_cross, sticks_cross = multi_tensor(gtab, mevals, S0, angles=angles, fractions=[50, 50], snr=SNR) S_single = single_tensor(gtab, S0, evals, evecs, snr=SNR) data = np.concatenate((np.tile(S_cross, (8, 1)), np.tile(S_single, (2, 1))), axis=0) odf_gt_cross = multi_tensor_odf(sphere.vertices, mevals, angles, [50, 50]) odf_gt_single = single_tensor_odf(sphere.vertices, evals, evecs) response = recursive_response(gtab, data, mask=None, sh_order=8, peak_thr=0.01, init_fa=0.05, init_trace=0.0021, iter=8, convergence=0.001, parallel=False) csd = ConstrainedSphericalDeconvModel(gtab, response) csd_fit = csd.fit(data) assert_equal(np.all(csd_fit.shm_coeff[:, 0] >= 0), True) fodf = csd_fit.odf(sphere) directions_gt_single, _, _ = peak_directions(odf_gt_single, sphere) directions_gt_cross, _, _ = peak_directions(odf_gt_cross, sphere) directions_single, _, _ = peak_directions(fodf[8, :], sphere) directions_cross, _, _ = peak_directions(fodf[0, :], sphere) ang_sim = angular_similarity(directions_cross, directions_gt_cross) assert_equal(ang_sim > 1.9, True) assert_equal(directions_cross.shape[0], 2) assert_equal(directions_gt_cross.shape[0], 2) ang_sim = angular_similarity(directions_single, directions_gt_single) assert_equal(ang_sim > 0.9, True) assert_equal(directions_single.shape[0], 1) assert_equal(directions_gt_single.shape[0], 1) sphere = Sphere(xyz=gtab.gradients[where_dwi]) sf = response.on_sphere(sphere) S = np.concatenate(([response.S0], sf)) tenmodel = dti.TensorModel(gtab, min_signal=0.001) tenfit = tenmodel.fit(S) FA = fractional_anisotropy(tenfit.evals) FA_gt = fractional_anisotropy(evals) assert_almost_equal(FA, FA_gt, 1)
We can double-check that we have a good response function by visualizing the response function's ODF. Here is how you would do that: """ from dipy.viz import window, actor from dipy.sims.voxel import single_tensor_odf # Enables/disables interactive visualization interactive = False scene = window.Scene() evals = response[0] evecs = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]).T response_odf = single_tensor_odf(default_sphere.vertices, evals, evecs) # transform our data from 1D to 4D response_odf = response_odf[None, None, None, :] response_actor = actor.odf_slicer(response_odf, sphere=default_sphere, colormap='plasma') scene.add(response_actor) print('Saving illustration as csd_response.png') window.record(scene, out_path='csd_response.png', size=(200, 200)) if interactive: window.show(scene) """ .. figure:: csd_response.png :align: center Estimated response function.
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_recursive_response_calibration(): """ Test the recursive response calibration method. """ SNR = 100 S0 = 1 _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) sphere = default_sphere gtab = gradient_table(bvals, bvecs) evals = np.array([0.0015, 0.0003, 0.0003]) evecs = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]).T mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) angles = [(0, 0), (90, 0)] where_dwi = lazy_index(~gtab.b0s_mask) S_cross, _ = multi_tensor(gtab, mevals, S0, angles=angles, fractions=[50, 50], snr=SNR) S_single = single_tensor(gtab, S0, evals, evecs, snr=SNR) data = np.concatenate((np.tile(S_cross, (8, 1)), np.tile(S_single, (2, 1))), axis=0) odf_gt_cross = multi_tensor_odf(sphere.vertices, mevals, angles, [50, 50]) odf_gt_single = single_tensor_odf(sphere.vertices, evals, evecs) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) response = recursive_response(gtab, data, mask=None, sh_order=8, peak_thr=0.01, init_fa=0.05, init_trace=0.0021, iter=8, convergence=0.001, parallel=False) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) csd = ConstrainedSphericalDeconvModel(gtab, response) csd_fit = csd.fit(data) assert_equal(np.all(csd_fit.shm_coeff[:, 0] >= 0), True) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) fodf = csd_fit.odf(sphere) directions_gt_single, _, _ = peak_directions(odf_gt_single, sphere) directions_gt_cross, _, _ = peak_directions(odf_gt_cross, sphere) directions_single, _, _ = peak_directions(fodf[8, :], sphere) directions_cross, _, _ = peak_directions(fodf[0, :], sphere) ang_sim = angular_similarity(directions_cross, directions_gt_cross) assert_equal(ang_sim > 1.9, True) assert_equal(directions_cross.shape[0], 2) assert_equal(directions_gt_cross.shape[0], 2) ang_sim = angular_similarity(directions_single, directions_gt_single) assert_equal(ang_sim > 0.9, True) assert_equal(directions_single.shape[0], 1) assert_equal(directions_gt_single.shape[0], 1) with warnings.catch_warnings(record=True) as w: sphere = Sphere(xyz=gtab.gradients[where_dwi]) npt.assert_equal(len(w), 1) npt.assert_(issubclass(w[0].category, UserWarning)) npt.assert_("Vertices are not on the unit sphere" in str(w[0].message)) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) sf = response.on_sphere(sphere) S = np.concatenate(([response.S0], sf)) tenmodel = TensorModel(gtab, min_signal=0.001) tenfit = tenmodel.fit(S) FA = fractional_anisotropy(tenfit.evals) FA_gt = fractional_anisotropy(evals) assert_almost_equal(FA, FA_gt, 1)
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 ----- .. [Rokem2014] 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 .. [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. """ # Each column of the matrix is the signal in each measurement, as # predicted by a "canonical", symmetrical tensor rotated towards this # vertex of the sphere: canonical_tensor = np.diag(response) 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 rot_matrix = geo.vec2vec_rotmat(np.array([1, 0, 0]), this_dir) this_tensor = np.dot(rot_matrix, canonical_tensor) evals, evecs = dti.decompose_tensor(this_tensor) if mode == 'signal': sig = sims.single_tensor(mat_gtab, evals=response, evecs=evecs) mat[:, ii] = sig - np.mean(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
We can visualize what this default response looks like. """ from dipy.sims.voxel import single_tensor_odf from dipy.viz import window, actor # Enables/disables interactive visualization interactive = False scene = window.Scene() evals = rumba.wm_response evecs = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]).T response_odf = single_tensor_odf(sphere.vertices, evals, evecs) # Transform our data from 1D to 4D response_odf = response_odf[None, None, None, :] response_actor = actor.odf_slicer(response_odf, sphere=sphere, colormap='plasma') scene.add(response_actor) print('Saving illustration as default_response.png') window.record(scene, out_path='default_response.png', size=(200, 200)) if interactive: window.show(scene) """ .. figure:: default_response.png :align: center