def test_real_sym_sh_basis(): # This test should do for now # The tournier07 basis should be the same as re-ordering and re-scaling the # descoteaux07 basis new_order = [0, 5, 4, 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, 7, 6] sphere = hemi_icosahedron.subdivide(2) basis, m, n = real_sym_sh_mrtrix(4, sphere.theta, sphere.phi) expected = basis[:, new_order] expected *= np.where(m == 0, 1., np.sqrt(2)) descoteaux07_basis, m, n = real_sym_sh_basis(4, sphere.theta, sphere.phi) assert_array_almost_equal(descoteaux07_basis, expected)
def test_real_sym_sh_mrtrix(): coef, expected, sphere = mrtrix_spherical_functions() basis, m, n = real_sym_sh_mrtrix(8, sphere.theta, sphere.phi) func = np.dot(coef, basis.T) assert_array_almost_equal(func, expected, 4)
def __init__(self, dmipy_acquisition_scheme, N_angular_samples=10): self.Nsamples = N_angular_samples scheme = dmipy_acquisition_scheme thetas = np.linspace(0, np.pi / 2, N_angular_samples) r = np.ones(N_angular_samples) phis = np.zeros(N_angular_samples) angles = np.c_[r, thetas, phis] angles_cart = utils.sphere2cart(angles) b_all_shells = [] Gdirs_all_shells = [] delta_all_shells = [] Delta_all_shells = [] shell_indices = [] for shell_index in scheme.unique_dwi_indices: b = scheme.shell_bvalues[shell_index] b_all_shells.append(np.tile(b, N_angular_samples)) if scheme.shell_delta is not None: delta = scheme.shell_delta[shell_index] delta_all_shells.append(np.tile(delta, N_angular_samples)) if scheme.shell_Delta is not None: Delta = scheme.shell_Delta[shell_index] Delta_all_shells.append(np.tile(Delta, N_angular_samples)) Gdirs_all_shells.append(angles_cart) shell_indices.append(np.tile(shell_index, N_angular_samples)) self.shell_indices = np.hstack(shell_indices) self.bvalues = np.hstack(b_all_shells) self.gradient_directions = np.vstack(Gdirs_all_shells) self.delta = None if scheme.shell_delta is not None: self.delta = np.hstack(delta_all_shells) self.Delta = None if scheme.shell_Delta is not None: self.Delta = np.hstack(Delta_all_shells) if self.delta is not None and self.Delta is not None: self.gradient_strengths = g_from_b(self.bvalues, self.delta, self.Delta) self.qvalues = q_from_g(self.gradient_strengths, self.delta) self.tau = self.Delta - self.delta / 3.0 else: self.gradient_strengths = self.qvalues = self.tau = None self.b0_mask = np.tile(False, len(self.bvalues)) self.shell_delta = scheme.shell_delta self.shell_Delta = scheme.shell_Delta self.unique_b0_indices = scheme.unique_b0_indices self.unique_shell_indices = scheme.unique_shell_indices self.unique_dwi_indices = scheme.unique_dwi_indices self.N_b0_shells = len(self.unique_b0_indices) self.N_dwi_shells = len(self.unique_dwi_indices) self.N_shells = len(self.unique_shell_indices) self.number_of_measurements = len(self.bvalues) self.shell_sh_matrices = {} self.shell_sh_orders = {} for shell_index in scheme.unique_dwi_indices: self.shell_sh_orders[shell_index] = int( scheme.shell_sh_orders[shell_index]) self.shell_sh_matrices[shell_index] = real_sym_sh_mrtrix( self.shell_sh_orders[shell_index], thetas, phis)[0] self.inverse_rh_matrix = { rh_order: np.linalg.pinv(real_sym_rh_basis(rh_order, thetas, phis)) for rh_order in np.arange(0, 15, 2) }
def __init__(self, bvalues, gradient_directions, qvalues, gradient_strengths, delta, Delta, TE, min_b_shell_distance, b0_threshold): self.min_b_shell_distance = float(min_b_shell_distance) self.b0_threshold = float(b0_threshold) self.bvalues = bvalues.astype(float) self.b0_mask = self.bvalues <= b0_threshold self.number_of_b0s = np.sum(self.b0_mask) self.number_of_measurements = len(self.bvalues) self.gradient_directions = gradient_directions.astype(float) self.qvalues = None if qvalues is not None: self.qvalues = qvalues.astype(float) self.gradient_strengths = None if gradient_strengths is not None: self.gradient_strengths = gradient_strengths.astype(float) self.delta = None if delta is not None: self.delta = delta.astype(float) self.Delta = None if Delta is not None: self.Delta = Delta.astype(float) self.TE = None self.N_TE = 1 # default if not given if TE is not None: self.TE = TE.astype(float) self.tau = None if self.delta is not None and self.Delta is not None: self.tau = Delta - delta / 3. # if there are more then 1 measurement if self.number_of_measurements > 1: # we check if there are multiple unique delta-Delta combinations if self.TE is not None: deltas = np.c_[self.delta, self.Delta, self.TE] elif self.delta is not None and self.Delta is not None: deltas = np.c_[self.delta, self.Delta] elif self.delta is None and self.Delta is not None: deltas = np.c_[self.Delta] elif self.delta is not None and self.Delta is None: deltas = np.c_[self.delta] else: deltas = [] if deltas == []: deltas = np.c_[np.zeros(len(self.bvalues))] unique_deltas = np.unique(deltas, axis=0) self.shell_indices = np.zeros(len(bvalues), dtype=int) self.shell_bvalues = [] max_index = 0 # for every unique combination we separate shells based on bvalue # reason for separation is that different combinations of # delta and Delta can result in the same b-value, which could # result in wrong classification of DWIs to unique shells. for unique_deltas_ in unique_deltas: delta_mask = np.all(deltas == unique_deltas_, axis=1) masked_bvals = bvalues[delta_mask] if len(masked_bvals) > 1: shell_indices_, shell_bvalues_ = ( calculate_shell_bvalues_and_indices( masked_bvals, min_b_shell_distance)) else: shell_indices_, shell_bvalues_ = np.array(0), masked_bvals self.shell_indices[delta_mask] = shell_indices_ + max_index self.shell_bvalues.append(shell_bvalues_) max_index = max(self.shell_indices + 1) self.shell_bvalues = np.hstack(self.shell_bvalues) self.shell_b0_mask = self.shell_bvalues <= b0_threshold first_indices = [ np.argmax(self.shell_indices == ind) for ind in np.arange(self.shell_indices.max() + 1) ] self.shell_qvalues = None if self.qvalues is not None: self.shell_qvalues = self.qvalues[first_indices] self.shell_gradient_strengths = None if self.gradient_strengths is not None: self.shell_gradient_strengths = ( self.gradient_strengths[first_indices]) self.shell_delta = None if self.delta is not None: self.shell_delta = self.delta[first_indices] self.shell_Delta = None if self.Delta is not None: self.shell_Delta = self.Delta[first_indices] self.shell_TE = None if self.TE is not None: self.shell_TE = self.TE[first_indices] if (len(np.unique(self.TE)) != len( np.unique(self.TE[self.b0_mask]))): msg = "Not every TE shell has b0 measurements.\n" msg += "This is required to properly normalize the signal." msg += " Make sure the TE values for b0-measurements have " msg += "not defaulted to 0 for example." raise ValueError(msg) self.N_TE = len(self.shell_TE) # if for some reason only one measurement is given (for testing) else: self.shell_bvalues = self.bvalues self.shell_indices = np.r_[int(0)] if self.shell_bvalues > b0_threshold: self.shell_b0_mask = np.r_[False] else: self.shell_b0_mask = np.r_[True] self.shell_qvalues = self.qvalues self.shell_gradient_strengths = self.gradient_strengths self.shell_delta = self.delta self.shell_Delta = self.Delta self.shell_TE = TE # calculates observation matrices to convert spherical harmonic # coefficients to the positions on the sphere for every shell self.unique_b0_indices = np.unique(self.shell_indices[self.b0_mask]) self.unique_dwi_indices = np.unique(self.shell_indices[~self.b0_mask]) self.unique_shell_indices = np.unique(self.shell_indices) self.N_b0_shells = len(self.unique_b0_indices) self.N_dwi_shells = len(self.unique_dwi_indices) self.N_shells = len(self.unique_shell_indices) self.shell_sh_matrices = {} self.shell_sh_orders = {} for shell_index in self.unique_b0_indices: self.shell_sh_orders[shell_index] = 0 for shell_index in self.unique_dwi_indices: shell_mask = self.shell_indices == shell_index bvecs_shell = self.gradient_directions[shell_mask] _, theta_, phi_ = utils.cart2sphere(bvecs_shell).T self.shell_sh_orders[shell_index] = get_sh_order_from_bval( self.shell_bvalues[shell_index]) self.shell_sh_matrices[shell_index] = real_sym_sh_mrtrix( self.shell_sh_orders[shell_index], theta_, phi_)[0] # warning in case there are no b0 measurements if sum(self.b0_mask) == 0: msg = "No b0 measurements were detected. Check if the b0_threshold" msg += " option is high enough, or if there is a mistake in the " msg += "acquisition design." warn(msg) self.spherical_mean_scheme = SphericalMeanAcquisitionScheme( self.shell_bvalues, self.shell_qvalues, self.shell_gradient_strengths, self.shell_Delta, self.shell_delta) if len(self.unique_dwi_indices) > 0: self.rotational_harmonics_scheme = ( RotationalHarmonicsAcquisitionScheme(self))
numba, have_numba, _ = optional_package("numba") GRADIENT_TABLES_PATH = pkg_resources.resource_filename('dmipy', 'data/gradient_tables') SIGNAL_MODELS_PATH = pkg_resources.resource_filename('dmipy', 'signal_models') DATA_PATH = pkg_resources.resource_filename('dmipy', 'data') SPHERE_CARTESIAN = np.loadtxt(join(GRADIENT_TABLES_PATH, 'sphere_with_cap.txt')) SPHERE_SPHERICAL = utils.cart2sphere(SPHERE_CARTESIAN) log_bingham_normalization_splinefit = np.load(join( DATA_PATH, "bingham_normalization_splinefit.npz"), encoding='bytes')['arr_0'] inverse_sh_matrix_kernel = { sh_order: np.linalg.pinv( real_sym_sh_mrtrix(sh_order, hemisphere.theta, hemisphere.phi)[0]) for sh_order in np.arange(0, 15, 2) } BETA_SCALING = 1e-6 __all__ = [ 'get_sh_order_from_odi', 'SD1Watson', 'SD2Bingham', 'DD1Gamma', 'odi2kappa', 'kappa2odi' ] def get_sh_order_from_odi(odi): "Returns minimum sh_order to estimate spherical harmonics for given odi." odis = np.array([ 0.80606061, 0.46666667, 0.25333333, 0.15636364, 0.09818182, 0.06909091, 0.