def test_smooth_pinv(): hemi = hemi_icosahedron.subdivide(2) m, n = sph_harm_ind_list(4) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) B = real_sh_descoteaux_from_index(m, n, hemi.theta[:, None], hemi.phi[:, None]) L = np.zeros(len(m)) C = smooth_pinv(B, L) D = np.dot(npl.inv(np.dot(B.T, B)), B.T) assert_array_almost_equal(C, D) L = n * (n + 1) * .05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D) L = np.arange(len(n)) * .05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D)
def test_sh_to_sf_matrix(): sphere = Sphere(xyz=hemi_icosahedron.vertices) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) B1, invB1 = sh_to_sf_matrix(sphere) B2, m, n = real_sh_descoteaux(4, sphere.theta, sphere.phi) invB2 = smooth_pinv(B2, L=np.zeros_like(n)) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) B3 = sh_to_sf_matrix(sphere, return_inv=False) assert_array_almost_equal(B1, B2.T) assert_array_almost_equal(invB1, invB2.T) assert_array_almost_equal(B3, B1) assert_raises(ValueError, sh_to_sf_matrix, sphere, basis_type="")
def test_real_sh_descoteaux2(): vertices = hemi_icosahedron.subdivide(2).vertices mevals = np.array([[0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003]]) angles = [(0, 0), (60, 0)] odf = multi_tensor_odf(vertices, mevals, angles, [50, 50]) mevals = np.array([[0.0015, 0.0003, 0.0003]]) angles = [(0, 0)] odf2 = multi_tensor_odf(-vertices, mevals, angles, [100]) sphere = Sphere(xyz=np.vstack((vertices, -vertices))) # Asymmetric spherical function with 162 coefficients sf = np.append(odf, odf2) # In order for our approximation to be precise enough, we # will use a SH basis of orders up to 10 (121 coefficients) with warnings.catch_warnings(record=True) as w: B, m, n = real_sh_descoteaux(10, sphere.theta, sphere.phi, full_basis=True) npt.assert_equal(len(w), 1) npt.assert_(issubclass(w[0].category, PendingDeprecationWarning)) npt.assert_(descoteaux07_legacy_msg in str(w[0].message)) invB = smooth_pinv(B, L=np.zeros_like(n)) sh_coefs = np.dot(invB, sf) sf_approx = np.dot(B, sh_coefs) assert_array_almost_equal(sf_approx, sf, 2)
def main(): parser = _build_arg_parser() args = parser.parse_args() assert_inputs_exist(parser, args.input_sh) assert_outputs_exist(parser, args, args.output_name) input_basis = args.sh_basis output_basis = 'descoteaux07' if input_basis == 'tournier07' else 'tournier07' sph_harm_basis_ori = sph_harm_lookup.get(input_basis) sph_harm_basis_des = sph_harm_lookup.get(output_basis) sphere = get_sphere('repulsion724').subdivide(1) img = nib.load(args.input_sh) data = img.get_data() sh_order = find_order_from_nb_coeff(data) b_ori, m_ori, n_ori = sph_harm_basis_ori(sh_order, sphere.theta, sphere.phi) b_des, m_des, n_des = sph_harm_basis_des(sh_order, sphere.theta, sphere.phi) l_des = -n_des * (n_des + 1) inv_b_des = smooth_pinv(b_des, 0 * l_des) indices = np.argwhere(np.any(data, axis=3)) for i, ind in enumerate(indices): ind = tuple(ind) sf_1 = np.dot(data[ind], b_ori.T) data[ind] = np.dot(sf_1, inv_b_des.T) img = nib.Nifti1Image(data, img.affine, img.header) nib.save(nib.Nifti1Image(data, img.affine, img.header), args.output_name)
def sh_smooth(data, bvals, bvecs, sh_order=4, similarity_threshold=50, regul=0.006): """Smooth the raw diffusion signal with spherical harmonics. data : ndarray The diffusion data to smooth. gtab : gradient table object Corresponding gradients table object to data. sh_order : int, default 8 Order of the spherical harmonics to fit. similarity_threshold : int, default 50 All b-values such that |b_1 - b_2| < similarity_threshold will be considered as identical for smoothing purpose. Must be lower than 200. regul : float, default 0.006 Amount of regularization to apply to sh coefficients computation. Return --------- pred_sig : ndarray The smoothed diffusion data, fitted through spherical harmonics. """ if similarity_threshold > 200: raise ValueError("similarity_threshold = {}, which is higher than 200," " please use a lower value".format(similarity_threshold)) m, n = sph_harm_ind_list(sh_order) L = -n * (n + 1) where_b0s = bvals == 0 pred_sig = np.zeros_like(data, dtype=np.float32) # Round similar bvals together for identifying similar shells rounded_bvals = np.zeros_like(bvals) for unique_bval in np.unique(bvals): idx = np.abs(unique_bval - bvals) < similarity_threshold rounded_bvals[idx] = unique_bval # process each b-value separately for unique_bval in np.unique(rounded_bvals): idx = rounded_bvals == unique_bval # Just give back the signal for the b0s since we can't really do anything about it if np.all(idx == where_b0s): if np.sum(where_b0s) > 1: pred_sig[..., idx] = np.mean(data[..., idx], axis=-1, keepdims=True) else: pred_sig[..., idx] = data[..., idx] continue x, y, z = bvecs[:, idx] r, theta, phi = cart2sphere(x, y, z) # Find the sh coefficients to smooth the signal B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None]) invB = smooth_pinv(B_dwi, np.sqrt(regul) * L) sh_coeff = np.dot(data[..., idx], invB.T) # Find the smoothed signal from the sh fit for the given gtab pred_sig[..., idx] = np.dot(sh_coeff, B_dwi.T) return pred_sig
def sf_to_sh_invB(sphere, sh_order=8, basis_type=None, smooth=0.0): sph_harm_basis = sph_harm_lookup.get(basis_type) if sph_harm_basis is None: raise ValueError("Invalid basis name.") B, m, n = sph_harm_basis(sh_order, sphere.theta, sphere.phi) L = -n * (n + 1) invB = smooth_pinv(B, np.sqrt(smooth) * L) return invB.T
def process(self, data_container, points, dwi): raw_sphere = Sphere(xyz=data_container.bvecs) real_sh, _, n = real_sym_sh_mrtrix(self.sh_order, raw_sphere.theta, raw_sphere.phi) l = -n * (n + 1) inv_b = smooth_pinv(real_sh, np.sqrt(self.smooth) * l) data_sh = np.dot(dwi, inv_b.T) return data_sh
def get_spherical_harmonics_coefficients(dwi, bvals, bvecs, sh_order=8, smooth=0.006, first=False, mean_centering=True): """ Compute coefficients of the spherical harmonics basis. Parameters ----------- dwi : `nibabel.NiftiImage` object Diffusion signal as weighted images (4D). bvals : ndarray shape (N,) B-values used with each direction. bvecs : ndarray shape (N, 3) Directions of the diffusion signal. Directions are assumed to be only on the hemisphere. sh_order : int, optional SH order. Default: 8 smooth : float, optional Lambda-regularization in the SH fit. Default: 0.006. mean_centering : bool If True, signal will have zero mean in each direction for all nonzero voxels Returns ------- sh_coeffs : ndarray of shape (X, Y, Z, #coeffs) Spherical harmonics coefficients at every voxel. The actual number of coeffs depends on `sh_order`. """ bvals = np.asarray(bvals) bvecs = np.asarray(bvecs) dwi_weights = dwi.get_data().astype("float32") # Exract the averaged b0. b0_idx = bvals == 0 b0 = dwi_weights[..., b0_idx].mean(axis=3) # Extract diffusion weights and normalize by the b0. bvecs = bvecs[np.logical_not(b0_idx)] weights = dwi_weights[..., np.logical_not(b0_idx)] weights = normalize_dwi(weights, b0) # Assuming all directions are on the hemisphere. raw_sphere = HemiSphere(xyz=bvecs) # Fit SH to signal sph_harm_basis = sph_harm_lookup.get('mrtrix') Ba, m, n = sph_harm_basis(sh_order, raw_sphere.theta, raw_sphere.phi) L = -n * (n + 1) invB = smooth_pinv(Ba, np.sqrt(smooth) * L) data_sh = np.dot(weights, invB.T) if mean_centering: # Normalization in each direction (zero mean) idx = data_sh.sum(axis=-1).nonzero() means = data_sh[idx].mean(axis=0) data_sh[idx] -= means return data_sh
def __init__(self, odf_data, basis, sphere): self.basis = basis self.order = find_order_from_nb_coeff(odf_data) self.sphere = sphere B, self.m, self.n = get_b_matrix(self.order, self.sphere, self.basis, return_all=True) self.B = np.ascontiguousarray(np.matrix(B), dtype=np.float64) invB = smooth_pinv(self.B, np.sqrt(0.006) * (-self.n * (self.n + 1))) self.invB = np.ascontiguousarray(np.matrix(invB), dtype=np.float64)
def test_sh_to_sf_matrix(): sphere = Sphere(xyz=hemi_icosahedron.vertices) B1, invB1 = sh_to_sf_matrix(sphere) B2, m, n = real_sym_sh_basis(4, sphere.theta, sphere.phi) invB2 = smooth_pinv(B2, L=np.zeros_like(n)) B3 = sh_to_sf_matrix(sphere, return_inv=False) assert_array_almost_equal(B1, B2.T) assert_array_almost_equal(invB1, invB2.T) assert_array_almost_equal(B3, B1) assert_raises(ValueError, sh_to_sf_matrix, sphere, basis_type="")
def test_smooth_pinv(): hemi = hemi_icosahedron.subdivide(2) m, n = sph_harm_ind_list(4) B = real_sph_harm(m, n, hemi.theta[:, None], hemi.phi[:, None]) L = np.zeros(len(m)) C = smooth_pinv(B, L) D = np.dot(npl.inv(np.dot(B.T, B)), B.T) assert_array_almost_equal(C, D) L = n * (n + 1) * .05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D) L = np.arange(len(n)) * .05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D)
def test_smooth_pinv(): v, e, f = create_half_unit_sphere(3) m, n = sph_harm_ind_list(4) r, pol, azi = cart2sphere(*v.T) B = real_sph_harm(m, n, azi[:, None], pol[:, None]) L = np.zeros(len(m)) C = smooth_pinv(B, L) D = np.dot(npl.inv(np.dot(B.T, B)), B.T) assert_array_almost_equal(C, D) L = n * (n + 1) * 0.05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D) L = np.arange(len(n)) * 0.05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D)
def test_smooth_pinv(): v, e, f = create_half_unit_sphere(3) m, n = sph_harm_ind_list(4) r, pol, azi = cart2sphere(*v.T) B = real_sph_harm(m, n, azi[:, None], pol[:, None]) L = np.zeros(len(m)) C = smooth_pinv(B, L) D = np.dot(npl.inv(np.dot(B.T, B)), B.T) assert_array_almost_equal(C, D) L = n * (n + 1) * .05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D) L = np.arange(len(n)) * .05 C = smooth_pinv(B, L) L = np.diag(L) D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T) assert_array_almost_equal(C, D)
def get_spherical_harmonics_coefficients(dwi, bvals, bvecs, sh_order=8, smooth=0.006, first=False): """ Compute coefficients of the spherical harmonics basis. Parameters ----------- dwi : `nibabel.NiftiImage` object Diffusion signal as weighted images (4D). bvals : ndarray shape (N,) B-values used with each direction. bvecs : ndarray shape (N, 3) Directions of the diffusion signal. Directions are assumed to be only on the hemisphere. sh_order : int, optional SH order. Default: 8 smooth : float, optional Lambda-regularization in the SH fit. Default: 0.006. Returns ------- sh_coeffs : ndarray of shape (X, Y, Z, #coeffs) Spherical harmonics coefficients at every voxel. The actual number of coeffs depends on `sh_order`. """ bvals = np.asarray(bvals) bvecs = np.asarray(bvecs) dwi_weights = dwi.get_data().astype("float32") # Exract the averaged b0. b0_idx = bvals == 0 b0 = dwi_weights[..., b0_idx].mean(axis=3) # Extract diffusion weights and normalize by the b0. bvecs = bvecs[np.logical_not(b0_idx)] weights = dwi_weights[..., np.logical_not(b0_idx)] weights = normalize_dwi(weights, b0) # Assuming all directions are on the hemisphere. raw_sphere = HemiSphere(xyz=bvecs) # Fit SH to signal sph_harm_basis = sph_harm_lookup.get('mrtrix') Ba, m, n = sph_harm_basis(sh_order, raw_sphere.theta, raw_sphere.phi) L = -n * (n + 1) invB = smooth_pinv(Ba, np.sqrt(smooth) * L) data_sh = np.dot(weights, invB.T) return data_sh
def get_spherical_harmonics_coefficients(self, dwi_weights, bvals, bvecs, sh_order=8, smooth=0.006): """ Compute coefficients of the spherical harmonics basis. Parameters ----------- dwi_weights : `nibabel.NiftiImage` object Diffusion signal as weighted images (4D). bvals : ndarray shape (N,) B-values used with each direction. bvecs : ndarray shape (N, 3) Directions of the diffusion signal. Directions are assumed to be only on the hemisphere. sh_order : int, optional SH order. Default: 8 smooth : float, optional Lambda-regularization in the SH fit. Default: 0.006. Returns ------- sh_coeffs : ndarray of shape (X, Y, Z, #coeffs) Spherical harmonics coefficients at every voxel. The actual number of coeffs depends on `sh_order`. """ # Exract the averaged b0. b0_idx = bvals == 0 b0 = dwi_weights[..., b0_idx].mean(axis=3) + 1e-10 # Extract diffusion weights and normalize by the b0. bvecs = bvecs[np.logical_not(b0_idx)] weights = dwi_weights[..., np.logical_not(b0_idx)] weights = self.normalize_dwi(weights, b0) # Assuming all directions are on the hemisphere. raw_sphere = HemiSphere(xyz=bvecs) # Fit SH to signal sph_harm_basis = sph_harm_lookup.get("tournier07") Ba, m, n = sph_harm_basis(sh_order, raw_sphere.theta, raw_sphere.phi) L = -n * (n + 1) invB = smooth_pinv(Ba, np.sqrt(smooth) * L) data_sh = np.dot(weights, invB.T) return data_sh
def test_real_full_sh_basis(): vertices = hemi_icosahedron.subdivide(2).vertices mevals = np.array([[0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003]]) angles = [(0, 0), (60, 0)] odf = multi_tensor_odf(vertices, mevals, angles, [50, 50]) mevals = np.array([[0.0015, 0.0003, 0.0003]]) angles = [(0, 0)] odf2 = multi_tensor_odf(-vertices, mevals, angles, [100]) sphere = Sphere(xyz=np.vstack((vertices, -vertices))) # Asymmetric spherical function with 162 coefficients sf = np.append(odf, odf2) # In order for our approximation to be precise enough, we # will use a SH basis of orders up to 10 (121 coefficients) B, m, n = real_full_sh_basis(10, sphere.theta, sphere.phi) invB = smooth_pinv(B, L=np.zeros_like(n)) sh_coefs = np.dot(invB, sf) sf_approx = np.dot(B, sh_coefs) assert_array_almost_equal(sf_approx, sf, 2)
def get_B_matrix(gtab=None, sh_order=8, theta=None, phi=None, smooth=0.006): """Function that return matrices for Spherical Harmonics fitting Attributes: gtab (dipy GradientTable): the gradient of the dwi to be fitted sh_order (int): Order of the Spherical Harmonics theta: custom gradient angle used instead of gtab in combinaison w/ phi phi: same as theta smooth (float): the smoothing parameter for the laplacian Returns: B (np.array(nb_dwi_directions, nb_sh_coeff)): matrix to fit SH->DWI invB (np.array(nb_sh_coeff, nb_dwi_directions)): matrix to fit DWI->SH """ if theta is None or phi is None: x, y, z = gtab.gradients[~gtab.b0s_mask].T r, theta, phi = cart2sphere(x, y, z) B, m, n = real_sym_sh_basis(sh_order, theta[:, None], phi[:, None]) # B = real_sph_harm(m, n, theta[:, None], phi[:, None]) L = -n * (n + 1) invB = smooth_pinv(B, np.sqrt(smooth) * L) return B, invB
def sh_smooth(data, bvals, bvecs, sh_order=4, similarity_threshold=50, regul=0.006): """Smooth the raw diffusion signal with spherical harmonics. data : ndarray The diffusion data to smooth. gtab : gradient table object Corresponding gradients table object to data. sh_order : int, default 8 Order of the spherical harmonics to fit. similarity_threshold : int, default 50 All b-values such that |b_1 - b_2| < similarity_threshold will be considered as identical for smoothing purpose. Must be lower than 200. regul : float, default 0.006 Amount of regularization to apply to sh coefficients computation. Return --------- pred_sig : ndarray The smoothed diffusion data, fitted through spherical harmonics. """ if similarity_threshold > 200: raise ValueError( "similarity_threshold = {}, which is higher than 200," " please use a lower value".format(similarity_threshold)) m, n = sph_harm_ind_list(sh_order) L = -n * (n + 1) where_b0s = bvals == 0 pred_sig = np.zeros_like(data, dtype=np.float32) # Round similar bvals together for identifying similar shells rounded_bvals = np.zeros_like(bvals) for unique_bval in np.unique(bvals): idx = np.abs(unique_bval - bvals) < similarity_threshold rounded_bvals[idx] = unique_bval # process each b-value separately for unique_bval in np.unique(rounded_bvals): idx = rounded_bvals == unique_bval # Just give back the signal for the b0s since we can't really do anything about it if np.all(idx == where_b0s): if np.sum(where_b0s) > 1: pred_sig[..., idx] = np.mean(data[..., idx], axis=-1, keepdims=True) else: pred_sig[..., idx] = data[..., idx] continue x, y, z = bvecs[:, idx] r, theta, phi = cart2sphere(x, y, z) # Find the sh coefficients to smooth the signal B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None]) invB = smooth_pinv(B_dwi, np.sqrt(regul) * L) sh_coeff = np.dot(data[..., idx], invB.T) # Find the smoothed signal from the sh fit for the given gtab pred_sig[..., idx] = np.dot(sh_coeff, B_dwi.T) return pred_sig
def peaks_from_model(model, data, sphere, relative_peak_threshold, min_separation_angle, mask=None, return_odf=False, return_sh=True, gfa_thr=0, normalize_peaks=False, sh_order=8, sh_basis_type=None): """Fits the model to data and computes peaks and metrics Parameters ---------- model : a model instance `model` will be used to fit the data. sphere : Sphere The Sphere providing discrete directions for evaluation. relative_peak_threshold : float Only return peaks greater than ``relative_peak_threshold * m`` where m is the largest peak. min_separation_angle : float in [0, 90] The minimum distance between directions. If two peaks are too close only the larger of the two is returned. mask : array, optional If `mask` is provided, voxels that are False in `mask` are skipped and no peaks are returned. return_odf : bool If True, the odfs are returned. return_sh : bool If True, the odf as spherical harmonics coefficients is returned gfa_thr : float Voxels with gfa less than `gfa_thr` are skipped, no peaks are returned. normalize_peaks : bool If true, all peak values are calculated relative to `max(odf)`. sh_order : int, optional Maximum SH order in the SH fit. For `sh_order`, there will be ``(sh_order + 1) * (sh_order + 2) / 2`` SH coefficients (default 8). sh_basis_type : {None, 'mrtrix', 'fibernav'} ``None`` for the default dipy basis which is the fibernav basis, ``mrtrix`` for the MRtrix basis, and ``fibernav`` for the FiberNavigator basis Returns ------- pam : PeaksAndMetrics an object with ``gfa``, ``peak_values``, ``peak_indices``, ``odf``, ``shm_coeffs`` as attributes """ data_flat = data.reshape((-1, data.shape[-1])) size = len(data_flat) if mask is None: mask = np.ones(size, dtype='bool') else: mask = mask.ravel() if len(mask) != size: raise ValueError("mask is not the same size as data") npeaks = 5 sh_smooth=0 gfa_array = np.zeros(size) qa_array = np.zeros((size, npeaks)) peak_values = np.zeros((size, npeaks)) peak_indices = np.zeros((size, npeaks), dtype='int') peak_indices.fill(-1) if return_sh: #import here to avoid circular imports from dipy.reconst.shm import sph_harm_lookup, smooth_pinv sph_harm_basis = sph_harm_lookup.get(sh_basis_type) if sph_harm_basis is None: raise ValueError("Invalid basis name.") B, m, n = sph_harm_basis(sh_order, sphere.theta, sphere.phi) L = -n * (n + 1) invB = smooth_pinv(B, np.sqrt(sh_smooth) * L) n_shm_coeff = (sh_order + 2) * (sh_order + 1) / 2 shm_coeff = np.zeros((size, n_shm_coeff)) invB = invB.T #sh = np.dot(sf, invB.T) if return_odf: odf_array = np.zeros((size, len(sphere.vertices))) global_max = -np.inf for i, sig in enumerate(data_flat): if not mask[i]: continue odf = model.fit(sig).odf(sphere) if return_sh: shm_coeff[i] = np.dot(odf, invB) if return_odf: odf_array[i] = odf gfa_array[i] = gfa(odf) if gfa_array[i] < gfa_thr: global_max = max(global_max, odf.max()) continue # Get peaks of odf _, pk, ind = peak_directions(odf, sphere, relative_peak_threshold, min_separation_angle) # Calculate peak metrics global_max = max(global_max, pk[0]) n = min(npeaks, len(pk)) qa_array[i, :n] = pk[:n] - odf.min() if normalize_peaks: peak_values[i, :n] = pk[:n] / pk[0] else: peak_values[i, :n] = pk[:n] peak_indices[i, :n] = ind[:n] shape = data.shape[:-1] gfa_array = gfa_array.reshape(shape) qa_array = qa_array.reshape(shape + (npeaks,)) / global_max peak_values = peak_values.reshape(shape + (npeaks,)) peak_indices = peak_indices.reshape(shape + (npeaks,)) pam = PeaksAndMetrics() pam.peak_values = peak_values pam.peak_indices = peak_indices pam.gfa = gfa_array pam.qa = qa_array if return_sh: pam.shm_coeff = shm_coeff.reshape(shape + (n_shm_coeff,)) pam.invB = invB else: pam.shm_coeff = None pam.invB = None if return_odf: pam.odf = odf_array.reshape(shape + odf_array.shape[-1:]) else: pam.odf = None return pam
def main(): params = readArgs() # read in from the command line read_args = params.collect_args() params.check_args(read_args) # get img obj dwi_img = nib.load(params.dwi_) mask_img = nib.load(params.mask_) from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(params.bval_, params.bvec_) # need to create the gradient table yo from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs, b0_threshold=25) # get the data from image objects dwi_data = dwi_img.get_data() mask_data = mask_img.get_data() # and get affine img_affine = dwi_img.affine from dipy.data import get_sphere sphere = get_sphere('repulsion724') from dipy.segment.mask import applymask dwi_data = applymask(dwi_data, mask_data) printfl('dwi_data.shape (%d, %d, %d, %d)' % dwi_data.shape) printfl('\nYour bvecs look like this:{0}'.format(bvecs)) printfl('\nYour bvals look like this:{0}\n'.format(bvals)) from dipy.reconst.shm import anisotropic_power, sph_harm_lookup, smooth_pinv, normalize_data from dipy.core.sphere import HemiSphere smooth = 0.0 normed_data = normalize_data(dwi_data, gtab.b0s_mask) normed_data = normed_data[..., np.where(1 - gtab.b0s_mask)[0]] from dipy.core.gradients import gradient_table_from_bvals_bvecs gtab2 = gradient_table_from_bvals_bvecs( gtab.bvals[np.where(1 - gtab.b0s_mask)[0]], gtab.bvecs[np.where(1 - gtab.b0s_mask)[0]]) signal_native_pts = HemiSphere(xyz=gtab2.bvecs) sph_harm_basis = sph_harm_lookup.get(None) Ba, m, n = sph_harm_basis(params.sh_order_, signal_native_pts.theta, signal_native_pts.phi) L = -n * (n + 1) invB = smooth_pinv(Ba, np.sqrt(smooth) * L) # fit SH basis to DWI signal normed_data_sh = np.dot(normed_data, invB.T) # power map call printfl("fitting power map") pow_map = anisotropic_power(normed_data_sh, norm_factor=0.00001, power=2, non_negative=True) pow_map_img = nib.Nifti1Image(pow_map.astype(np.float32), img_affine) # make output name out_name = ''.join( [params.output_, '_powMap_sh', str(params.sh_order_), '.nii.gz']) printfl("writing power map to: {}".format(out_name)) nib.save(pow_map_img, out_name)
def peaks_from_model(model, data, sphere, relative_peak_threshold, min_separation_angle, mask=None, return_odf=False, return_sh=True, gfa_thr=0, normalize_peaks=False, sh_order=8, sh_basis_type=None): """Fits the model to data and computes peaks and metrics Parameters ---------- model : a model instance `model` will be used to fit the data. sphere : Sphere The Sphere providing discrete directions for evaluation. relative_peak_threshold : float Only return peaks greater than ``relative_peak_threshold * m`` where m is the largest peak. min_separation_angle : float in [0, 90] The minimum distance between directions. If two peaks are too close only the larger of the two is returned. mask : array, optional If `mask` is provided, voxels that are False in `mask` are skipped and no peaks are returned. return_odf : bool If True, the odfs are returned. return_sh : bool If True, the odf as spherical harmonics coefficients is returned gfa_thr : float Voxels with gfa less than `gfa_thr` are skipped, no peaks are returned. normalize_peaks : bool If true, all peak values are calculated relative to `max(odf)`. sh_order : int, optional Maximum SH order in the SH fit. For `sh_order`, there will be ``(sh_order + 1) * (sh_order + 2) / 2`` SH coefficients (default 8). sh_basis_type : {None, 'mrtrix', 'fibernav'} ``None`` for the default dipy basis which is the fibernav basis, ``mrtrix`` for the MRtrix basis, and ``fibernav`` for the FiberNavigator basis Returns ------- pam : PeaksAndMetrics an object with ``gfa``, ``peak_values``, ``peak_indices``, ``odf``, ``shm_coeffs`` as attributes """ data_flat = data.reshape((-1, data.shape[-1])) size = len(data_flat) if mask is None: mask = np.ones(size, dtype='bool') else: mask = mask.ravel() if len(mask) != size: raise ValueError("mask is not the same size as data") npeaks = 5 sh_smooth = 0 gfa_array = np.zeros(size) qa_array = np.zeros((size, npeaks)) peak_values = np.zeros((size, npeaks)) peak_indices = np.zeros((size, npeaks), dtype='int') peak_indices.fill(-1) if return_sh: #import here to avoid circular imports from dipy.reconst.shm import sph_harm_lookup, smooth_pinv sph_harm_basis = sph_harm_lookup.get(sh_basis_type) if sph_harm_basis is None: raise ValueError("Invalid basis name.") B, m, n = sph_harm_basis(sh_order, sphere.theta, sphere.phi) L = -n * (n + 1) invB = smooth_pinv(B, np.sqrt(sh_smooth) * L) n_shm_coeff = (sh_order + 2) * (sh_order + 1) / 2 shm_coeff = np.zeros((size, n_shm_coeff)) invB = invB.T #sh = np.dot(sf, invB.T) if return_odf: odf_array = np.zeros((size, len(sphere.vertices))) global_max = -np.inf for i, sig in enumerate(data_flat): if not mask[i]: continue odf = model.fit(sig).odf(sphere) if return_sh: shm_coeff[i] = np.dot(odf, invB) if return_odf: odf_array[i] = odf gfa_array[i] = gfa(odf) if gfa_array[i] < gfa_thr: global_max = max(global_max, odf.max()) continue # Get peaks of odf _, pk, ind = peak_directions(odf, sphere, relative_peak_threshold, min_separation_angle) # Calculate peak metrics global_max = max(global_max, pk[0]) n = min(npeaks, len(pk)) qa_array[i, :n] = pk[:n] - odf.min() if normalize_peaks: peak_values[i, :n] = pk[:n] / pk[0] else: peak_values[i, :n] = pk[:n] peak_indices[i, :n] = ind[:n] shape = data.shape[:-1] gfa_array = gfa_array.reshape(shape) qa_array = qa_array.reshape(shape + (npeaks, )) / global_max peak_values = peak_values.reshape(shape + (npeaks, )) peak_indices = peak_indices.reshape(shape + (npeaks, )) pam = PeaksAndMetrics() pam.peak_values = peak_values pam.peak_indices = peak_indices pam.gfa = gfa_array pam.qa = qa_array if return_sh: pam.shm_coeff = shm_coeff.reshape(shape + (n_shm_coeff, )) pam.invB = invB else: pam.shm_coeff = None pam.invB = None if return_odf: pam.odf = odf_array.reshape(shape + odf_array.shape[-1:]) else: pam.odf = None return pam
def peaks_from_model(model, data, sphere, relative_peak_threshold, min_separation_angle, mask=None, return_odf=False, return_sh=True, gfa_thr=0, normalize_peaks=False, sh_order=8, sh_basis_type=None, ravel_peaks=False, npeaks=5, parallel=False, nbr_process=None): """Fits the model to data and computes peaks and metrics Parameters ---------- model : a model instance `model` will be used to fit the data. sphere : Sphere The Sphere providing discrete directions for evaluation. relative_peak_threshold : float Only return peaks greater than ``relative_peak_threshold * m`` where m is the largest peak. min_separation_angle : float in [0, 90] The minimum distance between directions. If two peaks are too close only the larger of the two is returned. mask : array, optional If `mask` is provided, voxels that are False in `mask` are skipped and no peaks are returned. return_odf : bool If True, the odfs are returned. return_sh : bool If True, the odf as spherical harmonics coefficients is returned gfa_thr : float Voxels with gfa less than `gfa_thr` are skipped, no peaks are returned. normalize_peaks : bool If true, all peak values are calculated relative to `max(odf)`. sh_order : int, optional Maximum SH order in the SH fit. For `sh_order`, there will be ``(sh_order + 1) * (sh_order + 2) / 2`` SH coefficients (default 8). sh_basis_type : {None, 'mrtrix', 'fibernav'} ``None`` for the default dipy basis which is the fibernav basis, ``mrtrix`` for the MRtrix basis, and ``fibernav`` for the FiberNavigator basis ravel_peaks : bool If True, the peaks are returned as [x1, y1, z1, ..., xn, yn, zn] instead of Nx3. Set this flag to True if you want to visualize the peaks in the fibernavigator or in mrtrix. npeaks : int Maximum number of peaks found (default 5 peaks). parallel: bool If True, use multiprocessing to compute peaks and metric (default False). nbr_process: int If `parallel == True`, the number of subprocess to use (default multiprocessing.cpu_count()). Returns ------- pam : PeaksAndMetrics An object with ``gfa``, ``peak_directions``, ``peak_values``, ``peak_indices``, ``odf``, ``shm_coeffs`` as attributes """ if parallel: return __peaks_from_model_parallel(model, data, sphere, relative_peak_threshold, min_separation_angle, mask, return_odf, return_sh, gfa_thr, normalize_peaks, sh_order, sh_basis_type, ravel_peaks, npeaks, nbr_process) shape = data.shape[:-1] if mask is None: mask = np.ones(shape, dtype='bool') else: if mask.shape != shape: raise ValueError("Mask is not the same shape as data.") sh_smooth = 0 gfa_array = np.zeros(shape) qa_array = np.zeros((shape + (npeaks,))) peak_dirs = np.zeros((shape + (npeaks, 3))) peak_values = np.zeros((shape + (npeaks,))) peak_indices = np.zeros((shape + (npeaks,)), dtype='int') peak_indices.fill(-1) if return_sh: # import here to avoid circular imports from dipy.reconst.shm import sph_harm_lookup, smooth_pinv sph_harm_basis = sph_harm_lookup.get(sh_basis_type) if sph_harm_basis is None: raise ValueError("Invalid basis name.") B, m, n = sph_harm_basis(sh_order, sphere.theta, sphere.phi) L = -n * (n + 1) invB = smooth_pinv(B, np.sqrt(sh_smooth) * L) n_shm_coeff = (sh_order + 2) * (sh_order + 1) / 2 shm_coeff = np.zeros((shape + (n_shm_coeff,))) invB = invB.T if return_odf: odf_array = np.zeros((shape + (len(sphere.vertices),))) global_max = -np.inf for idx in ndindex(shape): if not mask[idx]: continue odf = model.fit(data[idx]).odf(sphere) if return_sh: shm_coeff[idx] = np.dot(odf, invB) if return_odf: odf_array[idx] = odf gfa_array[idx] = gfa(odf) if gfa_array[idx] < gfa_thr: global_max = max(global_max, odf.max()) continue # Get peaks of odf direction, pk, ind = peak_directions( odf, sphere, relative_peak_threshold, min_separation_angle) # Calculate peak metrics global_max = max(global_max, pk[0]) n = min(npeaks, len(pk)) qa_array[idx][:n] = pk[:n] - odf.min() peak_dirs[idx][:n] = direction[:n] peak_indices[idx][:n] = ind[:n] peak_values[idx][:n] = pk[:n] if normalize_peaks: peak_values[idx][:n] /= pk[0] peak_dirs[idx] *= peak_values[idx][:, None] #gfa_array = gfa_array qa_array /= global_max #peak_values = peak_values #peak_indices = peak_indices # The fibernavigator only supports float32. Since this form is mainly # for external visualisation, we enforce float32. if ravel_peaks: peak_dirs = peak_dirs.reshape(shape + (3 * npeaks,)).astype('float32') pam = PeaksAndMetrics() pam.peak_dirs = peak_dirs pam.peak_values = peak_values pam.peak_indices = peak_indices pam.gfa = gfa_array pam.qa = qa_array if return_sh: pam.shm_coeff = shm_coeff pam.invB = invB else: pam.shm_coeff = None pam.invB = None if return_odf: pam.odf = odf_array else: pam.odf = None return pam