def _pred_sig(self, theta, phi, beta, lambda1, lambda2): """ The predicted signal for a particular setting of the parameters """ Q = np.array([[lambda1, 0, 0], [0, lambda2, 0], [0, 0, lambda2]]) # If for some reason this is not symmetrical, then something is wrong # (in all likelihood, this means that the optimization process is # trying out some crazy value, such as nan). In that case, abort and # return a nan: if not np.allclose(Q.T, Q): return np.nan response_function = ozt.Tensor(Q, self.bvecs[:, self.b_idx], self.bvals[:, self.b_idx]) # Convert theta and phi to cartesian coordinates: x, y, z = geo.sphere2cart(1, theta, phi) bvec = [x, y, z] evals, evecs = response_function.decompose rot_tensor = ozt.tensor_from_eigs( evecs * ozu.calculate_rotation(bvec, evecs[0]), evals, self.bvecs[:, self.b_idx], self.bvals[:, self.b_idx]) iso_sig = np.exp(-self.bvals[self.b_idx][0] * lambda1) tensor_sig = rot_tensor.predicted_signal(1) return beta * iso_sig + (1 - beta) * tensor_sig
def plot_ellipsoid_mpl(Tensor, n=60): """ Plot an ellipsoid from a tensor using matplotlib Parameters ---------- Tensor: an ozt.Tensor class instance n: optional. If an integer is provided, we will plot this for a sphere with n (grid) equi-sampled points. Otherwise, we will plot the originally provided Tensor's bvecs. """ x, y, z = sphere(n=n) new_bvecs = np.vstack([x.ravel(), y.ravel(), z.ravel()]) Tensor = ozt.Tensor(Tensor.Q, new_bvecs, np.ones(new_bvecs.shape[-1]) * Tensor.bvals[0]) v = Tensor.diffusion_distance.reshape(x.shape) r, phi, theta = geo.cart2sphere(x, y, z) x_plot, y_plot, z_plot = geo.sphere2cart(v, phi, theta) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(x_plot, y_plot, z_plot, rstride=2, cstride=2, shade=True) return fig
def _pred_sig_ball_and_stick(self, params, check_constraints=False): """ This is the signal prediction for the ball-and-stick model """ theta, phi, w, d = params if check_constraints: if self._check_constraints([ [theta, 0, np.pi], [phi, -np.pi, np.pi], [w, 0, 1], # Weights are 0-1 [d, 0, np.inf] ]): # Diffusivity is # non-negative return np.inf # In each one we have a different axial diffusivity for the response # function. Simply multiply it by the current d: # Need to replace the canonical tensor with a self.response_function = ozt.Tensor(np.diag([1, 0, 0]), self.bvecs[:, self.b_idx], self.bvals[self.b_idx]) self.response_function.Q = self.response_function.Q * d rot_tensor = self._tensor_helper(theta, phi) return (1 - w) * d + w * rot_tensor.predicted_signal(1)
def test_Tensor_decompose(): """ Test the eigen-vector/value decomposition of the tensor: """ q = np.array([ [1.5, 0, 0], [0, 0.5, 0], # This needs to be slightly higher, so that # there is no ambiguity about the order of the # evals [0, 0, 0.5] ]) # And the bvecs are unit vectors: bvecs = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) bvals = [1, 1, 1] T1 = mtt.Tensor(q, bvecs, bvals) vals, vecs = T1.decompose npt.assert_equal(vecs, np.eye(3)) npt.assert_equal(vals, np.diag(q)) T2 = mtt.tensor_from_eigs(vecs, vals, bvecs, bvals) npt.assert_equal(T2.Q, q)
def response_function(self): """ A canonical tensor that describes the presumed response of a single fiber """ bvecs = self.bvecs[:, self.b_idx] bvals = self.bvals[self.b_idx] return ozt.Tensor(np.diag([self.ad, self.rd, self.rd]), bvecs, bvals)
def test_Tensor(): """ Test initialization of tensor objects """ bvecs = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] bvals = [1, 1, 1] npt.assert_raises(ValueError, mtt.Tensor, np.arange(20), bvecs, bvals) Q1 = [0, 1, 2, 3, 4, 5] t1 = mtt.Tensor(Q1, bvecs, bvals) # Follows conventions from vistasoft's dt6to33: npt.assert_equal(t1.Q, [[0, 3, 4], [3, 1, 5], [4, 5, 2]]) # This should not be an acceptable input (would lead to asymmetric output): Q2 = [0, 1, 2, 3, 4, 5, 6, 7, 8] npt.assert_raises(ValueError, mtt.Tensor, Q2, bvecs, bvals) # Same here: Q3 = [[0, 1, 2], [3, 4, 5], [6, 7, 8]] npt.assert_raises(ValueError, mtt.Tensor, Q2, bvecs, bvals) # No problem here: Q4 = [[0, 3, 4], [3, 1, 5], [4, 5, 2]] mtt.Tensor(Q4, bvecs, bvals) # More error handling: # bvecs and bvals need to be the same length: Q5 = npt.assert_raises(ValueError, mtt.Tensor, Q4, bvecs[:2], bvals) # bvecs needs to be 3 by n: Q6 = npt.assert_raises( ValueError, mtt.Tensor, Q4, [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], bvals) # bvecs need to be unit length! Q7 = npt.assert_raises(ValueError, mtt.Tensor, Q4, [[1, 0, 0], [0, 1, 0], [0, 1, 1]], bvals) # bvecs and bvals need to have the same length! Q8 = npt.assert_raises(ValueError, mtt.Tensor, Q4, [[1, 0, 0], [0, 1, 0], [0, 0, 1]], np.hstack([bvals, 1]))
def test_response_function(): rf_bvec = np.reshape(bvecs_t[:, 4], (3, 1)) tensor_t = ozt.Tensor(np.diag([1.5, 0.5, 0.5]), rf_bvec, np.array([1000])) evals_a, evecs_a = mb.response_function(1000, rf_bvec).decompose evals_t, evecs_t = tensor_t.decompose npt.assert_equal(evals_t, evals_a) npt.assert_equal(evecs_t, evecs_a) return evals_t, evecs_t
def test_Tensor_ADC(): # If we have a diagonal tensor: Q1_diag = np.random.rand(3) Q1 = np.diag(Q1_diag) # And the bvecs are unit vectors: bvecs = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) bvals = [1, 1, 1] # Initialize: T1 = mtt.Tensor(Q1, bvecs, bvals) # We should simply get back the diagonal elements of the tensor: npt.assert_equal(T1.ADC, Q1_diag)
def response_function(self): """ A canonical tensor that describes the presumed response of a single fiber """ if self.response_file is None: if self.verbose: print("Using tensor-based response function") return ozt.Tensor(np.diag([self.ad, self.rd, self.rd]), self.bvecs[:, self.b_idx], self.bvals[self.b_idx]) else: return _SphericalHarmonicResponseFunction(self)
def test_diffusion_distance(): """ Test for regression on the calculation of diffusion distance """ Q1 = np.array([[0.53343911, 0., 0.], [0., 0.61706389, 0.], [0., 0., 0.64934378]]) bvecs = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [-0.24187, 0.10309, -0.96482]]).T bvals = [1, 1, 1, 1] T1 = mtt.Tensor(Q1, bvecs, bvals) npt.assert_almost_equal( T1.diffusion_distance, np.array([0.73036916, 0.78553414, 0.8058187, 0.80052344]))
def test_Tensor_predicted_signal(): """ Test prediction of the signal from the Tensor: """ Q1_diag = np.random.rand(3) Q1 = np.diag(Q1_diag) # And the bvecs are unit vectors: bvecs = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) bvals = [1, 1, 1] T1 = mtt.Tensor(Q1, bvecs, bvals) # Should be possible to provide a value of S0 per bvec: S0_1 = [1, 1, 1] # In this case, that should give the same answer as just providing a single # scalar value: S0_2 = [1] npt.assert_equal(T1.predicted_signal(S0_1), T1.predicted_signal(S0_2))
def test_convlove_odf(): """ Test convolution of a tensor with a fiber orientation distribution function (odf) """ Q1_diag = np.random.rand(3) Q1 = np.diag(Q1_diag) # And the bvecs are unit vectors (one's taken from actual data, so that we # have a 3 by 4): bvecs = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [-0.24187, 0.10309, -0.96482]]).T bvals = [1, 1, 1, 1] T1 = mtt.Tensor(Q1, bvecs, bvals) S0 = 1000 # Has the same length as the number of bvecs: odf = [0.1, 0.2, 0.3, 0.4] # This performs the convolution T1.convolve_odf(odf, S0)
def plot_tensor_3d(Tensor, cmap='jet', mode='ADC', file_name=None, origin=[0, 0, 0], colorbar=False, figure=None, vmin=None, vmax=None, offset=0, azimuth=60, elevation=90, roll=0, scale_factor=1.0, rgb_pdd=False): """ mode: either "ADC", "ellipse" or "pred_sig" """ Q = Tensor.Q sphere = create_unit_sphere(5) vertices = sphere.vertices faces = sphere.faces x, y, z = vertices.T new_bvecs = np.vstack([x.ravel(), y.ravel(), z.ravel()]) Tensor = ozt.Tensor(Q, new_bvecs, Tensor.bvals[0] * np.ones(new_bvecs.shape[-1])) if mode == 'ADC': v = Tensor.ADC * scale_factor elif mode == 'ellipse': v = Tensor.diffusion_distance * scale_factor elif mode == 'pred_sig': v = Tensor.predicted_signal(1) * scale_factor else: raise ValueError("Mode not recognized") r, phi, theta = geo.cart2sphere(x, y, z) x_plot, y_plot, z_plot = geo.sphere2cart(v, phi, theta) if rgb_pdd: evals, evecs = Tensor.decompose xyz = evecs[0] r = np.abs(xyz[0]) / np.sum(np.abs(xyz)) g = np.abs(xyz[1]) / np.sum(np.abs(xyz)) b = np.abs(xyz[2]) / np.sum(np.abs(xyz)) color = (r, g, b) else: color = None # Call and return straightaway: return _display_maya_voxel(x_plot, y_plot, z_plot, faces, v, origin, cmap=cmap, colorbar=colorbar, color=color, figure=figure, vmin=vmin, vmax=vmax, file_name=file_name, azimuth=azimuth, elevation=elevation)