def normalize_gradients(bvecs, bvals, b0_threshold, bvec_norm_epsilon=0.1, b_scale=True): """ Normalize b-vectors and b-values. The resulting b-vectors will be of unit length for the non-zero b-values. The resultinb b-values will be normalized by the square of the corresponding vector amplitude. Parameters ---------- bvecs : m x n 2d array Raw b-vectors array. bvals : 1d array Raw b-values float array. b0_threshold : float Gradient threshold below which volumes and vectors are considered B0's. Returns ------- bvecs : m x n 2d array Unit-normed b-vectors array. bvals : 1d int array Vector amplitude square normed b-values array. """ from dipy.core.gradients import round_bvals bvals = np.array(bvals, dtype="float32") bvecs = np.array(bvecs, dtype="float32") b0s = bvals < b0_threshold b0_vecs = np.linalg.norm(bvecs, axis=1) < bvec_norm_epsilon # Check for bval-bvec discrepancy. if not np.all(b0s == b0_vecs): raise ValueError( "Inconsistent bvals and bvecs (%d, %d low-b, respectively)." % (b0s.sum(), b0_vecs.sum())) # Rescale b-vals if requested if b_scale: bvals[~b0s] *= np.linalg.norm(bvecs[~b0s], axis=1)**2 # Ensure b0s have (0, 0, 0) vectors bvecs[b0s, :3] = np.zeros(3) # Round bvals bvals = round_bvals(bvals) # Rescale b-vecs, skipping b0's, on the appropriate axis to unit-norm # length. bvecs[~b0s] /= np.linalg.norm(bvecs[~b0s], axis=1)[..., np.newaxis] return bvecs, bvals.astype("uint16")
def test_round_bvals(): bvals_gt = np.array([1000, 1000, 1000, 1000, 2000, 2000, 2000, 2000, 0]) b = round_bvals(bvals_gt) npt.assert_array_almost_equal(bvals_gt, b) bvals = np.array([995, 995, 995, 995, 2005, 2005, 2005, 2005, 0]) # We don't consider differences this small to be sufficient: b = round_bvals(bvals) npt.assert_array_almost_equal(bvals_gt, b) # Unless you specify that you are interested in this magnitude of changes: b = round_bvals(bvals, bmag=0) npt.assert_array_almost_equal(bvals, b) # Case that b-values are in ms/um2 bvals = np.array( [0.995, 0.995, 0.995, 0.995, 2.005, 2.005, 2.005, 2.005, 0]) b = round_bvals(bvals) bvals_gt = np.array([1, 1, 1, 1, 2, 2, 2, 2, 0]) npt.assert_array_almost_equal(bvals_gt, b)
def test_round_bvals(): bvals_gt = np.array([1000, 1000, 1000, 1000, 2000, 2000, 2000, 2000, 0]) b = round_bvals(bvals_gt) npt.assert_array_almost_equal(bvals_gt, b) bvals = np.array([995, 995, 995, 995, 2005, 2005, 2005, 2005, 0]) # We don't consider differences this small to be sufficient: b = round_bvals(bvals) npt.assert_array_almost_equal(bvals_gt, b) # Unless you specify that you are interested in this magnitude of changes: b = round_bvals(bvals, bmag=0) npt.assert_array_almost_equal(bvals, b) # Case that b-valuea are in ms/um2 bvals = np.array([0.995, 0.995, 0.995, 0.995, 2.005, 2.005, 2.005, 2.005, 0]) b = round_bvals(bvals) bvals_gt = np.array([1, 1, 1, 1, 2, 2, 2, 2, 0]) npt.assert_array_almost_equal(bvals_gt, b)
def msdki_prediction(msdki_params, gtab, S0=1.0): """ Predict the mean signal given the parameters of the mean signal DKI, an GradientTable object and S0 signal. Parameters ---------- params : ndarray ([X, Y, Z, ...], 2) Array containing the mean signal diffusivity and mean signal kurtosis in its last axis gtab : a GradientTable class instance The gradient table for this prediction S0 : float or ndarray (optional) The non diffusion-weighted signal in every voxel, or across all voxels. Default: 1 Notes ----- The predicted signal is given by: $MS(b) = S_0 * exp(-bD + 1/6 b^{2} D^{2} K)$, where $D$ and $K$ are the mean signal diffusivity and mean signal kurtosis. References ---------- .. [1] Henriques, R.N., 2018. Advanced Methods for Diffusion MRI Data Analysis and their Application to the Healthy Ageing Brain (Doctoral thesis). Downing College, University of Cambridge. https://doi.org/10.17863/CAM.29356 """ A = design_matrix(round_bvals(gtab.bvals)) params = msdki_params.copy() params[..., 1] = params[..., 1] * params[..., 0] ** 2 if isinstance(S0, float) or isinstance(S0, int): pred_sig = S0 * np.exp(np.dot(params, A[:, :2].T)) elif S0.size == 1: pred_sig = S0 * np.exp(np.dot(params, A[:, :2].T)) else: nv = gtab.bvals.size S0r = np.zeros(S0.shape + gtab.bvals.shape) for vi in range(nv): S0r[..., vi] = S0 pred_sig = S0r * np.exp(np.dot(params, A[:, :2].T)) return pred_sig
def msdki_prediction(msdki_params, gtab, S0=1.0): """ Predict the mean signal given the parameters of the mean signal DKI, an GradientTable object and S0 signal. Parameters ---------- params : ndarray ([X, Y, Z, ...], 2) Array containing the mean signal diffusivity and mean signal kurtosis in its last axis gtab : a GradientTable class instance The gradient table for this prediction S0 : float or ndarray (optional) The non diffusion-weighted signal in every voxel, or across all voxels. Default: 1 Notes ----- The predicted signal is given by: $MS(b) = S_0 * exp(-bD + 1/6 b^{2} D^{2} K)$, where $D$ and $K$ are the mean signal diffusivity and mean signal kurtosis. References ---------- .. [1] Henriques, R.N., 2018. Advanced Methods for Diffusion MRI Data Analysis and their Application to the Healthy Ageing Brain (Doctoral thesis). Downing College, University of Cambridge. https://doi.org/10.17863/CAM.29356 """ A = design_matrix(round_bvals(gtab.bvals)) params = msdki_params.copy() params[..., 1] = params[..., 1] * params[..., 0] ** 2 if isinstance(S0, float) or isinstance(S0, int): pred_sig = S0 * np.exp(np.dot(params, A[:, :2].T)) elif S0.size == 1: pred_sig = S0 * np.exp(np.dot(params, A[:, :2].T)) else: nv = gtab.bvals.size S0r = np.zeros(S0.shape + gtab.bvals.shape) for vi in range(nv): S0r[..., vi] = S0 pred_sig = S0r * np.exp(np.dot(params, A[:, :2].T)) return pred_sig
def normalize_gradients(bvecs, bvals, b0_threshold=B0_THRESHOLD, bvec_norm_epsilon=BVEC_NORM_EPSILON, b_scale=True, raise_error=False): """ Normalize b-vectors and b-values. The resulting b-vectors will be of unit length for the non-zero b-values. The resultinb b-values will be normalized by the square of the corresponding vector amplitude. Parameters ---------- bvecs : m x n 2d array Raw b-vectors array. bvals : 1d array Raw b-values float array. b0_threshold : float Gradient threshold below which volumes and vectors are considered B0's. Returns ------- bvecs : m x n 2d array Unit-normed b-vectors array. bvals : 1d int array Vector amplitude square normed b-values array. Examples -------- >>> bvecs = np.vstack((np.zeros(3), 2.0 * np.eye(3), -0.8 * np.eye(3), np.ones(3))) >>> bvals = np.array([1000] * bvecs.shape[0]) >>> normalize_gradients(bvecs, bvals, 50) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ValueError: >>> bvals[0] = 0.0 >>> norm_vecs, norm_vals = normalize_gradients(bvecs, bvals) >>> np.all(norm_vecs[0] == 0) True >>> norm_vecs[1, ...].tolist() [1.0, 0.0, 0.0] >>> norm_vals[0] 0 >>> norm_vals[1] 4000 >>> norm_vals[-2] 600 >>> norm_vals[-1] 3000 >>> norm_vecs, norm_vals = normalize_gradients(bvecs, bvals, b_scale=False) >>> norm_vals[0] 0 >>> np.all(norm_vals[1:] == 1000) True """ bvals = np.array(bvals, dtype='float32') bvecs = np.array(bvecs, dtype='float32') b0s = bvals < b0_threshold b0_vecs = np.linalg.norm(bvecs, axis=1) < bvec_norm_epsilon # Check for bval-bvec discrepancy. if not np.all(b0s == b0_vecs): msg = f"Inconsistent bvals and bvecs ({b0s.sum()}, {b0_vecs.sum()} low-b, respectively)." if raise_error: raise ValueError(msg) config.loggers.cli.warning(msg) # Rescale b-vals if requested if b_scale: bvals[~b0s] *= np.linalg.norm(bvecs[~b0s], axis=1)**2 # Ensure b0s have (0, 0, 0) vectors bvecs[b0s, :3] = np.zeros(3) # Round bvals bvals = round_bvals(bvals) # Rescale b-vecs, skipping b0's, on the appropriate axis to unit-norm length. bvecs[~b0s] /= np.linalg.norm(bvecs[~b0s], axis=1)[..., np.newaxis] return bvecs, bvals.astype('uint16')
""" Testing Mean Signal DKI (MSDKI) """ from __future__ import division, print_function, absolute_import import numpy as np import random from numpy.testing import assert_array_almost_equal, assert_raises from dipy.sims.voxel import multi_tensor_dki from dipy.io.gradients import read_bvals_bvecs from dipy.core.gradients import (gradient_table, unique_bvals, round_bvals) from dipy.data import get_fnames import dipy.reconst.msdki as msdki fimg, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) bvals = round_bvals(bvals) gtab = gradient_table(bvals, bvecs) # 2 shells for techniques that requires multishell data bvals_3s = np.concatenate((bvals, bvals * 1.5, bvals * 2), axis=0) bvecs_3s = np.concatenate((bvecs, bvecs, bvecs), axis=0) gtab_3s = gradient_table(bvals_3s, bvecs_3s) # Simulation 1. Spherical kurtosis tensor - MSK and MSD from the MSDKI model # should be equal to the MK and MD of the DKI tensor for cases of # spherical kurtosis tensors Di = 0.00099 De = 0.00226 mevals_sph = np.array([[Di, Di, Di], [De, De, De]]) f = 0.5 frac_sph = [f * 100, (1.0 - f) * 100]
from __future__ import division, print_function, absolute_import import numpy as np import random from numpy.testing import assert_array_almost_equal from nose.tools import assert_raises from dipy.sims.voxel import multi_tensor_dki from dipy.io.gradients import read_bvals_bvecs from dipy.core.gradients import (gradient_table, unique_bvals, round_bvals) from dipy.data import get_fnames import dipy.reconst.msdki as msdki fimg, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) bvals = round_bvals(bvals) gtab = gradient_table(bvals, bvecs) # 2 shells for techniques that requires multishell data bvals_3s = np.concatenate((bvals, bvals*1.5, bvals * 2), axis=0) bvecs_3s = np.concatenate((bvecs, bvecs, bvecs), axis=0) gtab_3s = gradient_table(bvals_3s, bvecs_3s) # Simulation 1. Spherical kurtosis tensor - MSK and MSD from the MSDKI model # should be equa to the MK and MD of the DKI tensor for cases of # spherical kurtosis tensors Di = 0.00099 De = 0.00226 mevals_sph = np.array([[Di, Di, Di], [De, De, De]]) f = 0.5 frac_sph = [f * 100, (1.0 - f) * 100]