def test_vector_norm(): A = np.array([[1, 0, 0], [3, 4, 0], [0, 5, 12], [1, 2, 3]]) expected = np.array([1, 5, 13, np.sqrt(14)]) assert_array_almost_equal(vector_norm(A), expected) expected.shape = (4, 1) assert_array_almost_equal(vector_norm(A, keepdims=True), expected) assert_array_almost_equal(vector_norm(A.T, axis=0, keepdims=True), expected.T)
def gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0, atol=1e-2, **kwargs): """Creates a GradientTable from a bvals array and a bvecs array Parameters ---------- bvals : array_like (N,) The b-value, or magnitude, of each gradient direction. bvecs : array_like (N, 3) The direction, represented as a unit vector, of each gradient. b0_threshold : float Gradients with b-value less than or equal to `bo_threshold` are considered to not have diffusion weighting. atol : float Each vector in `bvecs` must be a unit vectors up to a tolerance of `atol`. Other Parameters ---------------- **kwargs : dict Other keyword inputs are passed to GradientTable. Returns ------- gradients : GradientTable A GradientTable with all the gradient information. See Also -------- GradientTable, gradient_table """ bvals = np.asarray(bvals, np.float) bvecs = np.asarray(bvecs, np.float) dwi_mask = bvals > b0_threshold # check that bvals is (N,) array and bvecs is (N, 3) unit vectors if bvals.ndim != 1 or bvecs.ndim != 2 or bvecs.shape[0] != bvals.shape[0]: raise ValueError("bvals and bvecs should be (N,) and (N, 3) arrays " "respectively, where N is the number of diffusion " "gradients") bvecs = np.where(np.isnan(bvecs), 0, bvecs) bvecs_close_to_1 = abs(vector_norm(bvecs) - 1) <= atol if bvecs.shape[1] != 3 or not np.all(bvecs_close_to_1[dwi_mask]): raise ValueError("bvecs should be (N, 3), a set of N unit vectors") bvecs = np.where(bvecs_close_to_1[:, None], bvecs, 0) bvals = bvals * bvecs_close_to_1 gradients = bvals[:, None] * bvecs grad_table = GradientTable(gradients, b0_threshold=b0_threshold, **kwargs) grad_table.bvals = bvals grad_table.bvecs = bvecs grad_table.b0s_mask = ~dwi_mask return grad_table
def test_inplace_norm(): vec = [[8, 15, 0], [0, 36, 77]] norm1 = vector_norm(vec) norm2 = in_place_norm(vec) npt.assert_equal(norm1, norm2) vec = [[8.0, 15.0, 0.0], [0.0, 36.0, 77.0]] norm1 = vector_norm(vec) norm2 = in_place_norm(vec) npt.assert_equal(norm1, norm2) vec = [[8, 15, 0], [0, 36, 77]] norm1 = vector_norm(vec, keepdims=True) norm2 = in_place_norm(vec, keepdims=True) npt.assert_equal(norm1, norm2) vec = [[8, 15, 0], [0, 36, 77]] norm1 = vector_norm(vec, axis=0) norm2 = in_place_norm(vec, axis=0) npt.assert_equal(norm1, norm2)
def subdivide(self, n=1): """Subdivides each face of the sphere into four new faces. New vertices are created at a, b, and c. Then each face [x, y, z] is divided into faces [x, a, c], [y, a, b], [z, b, c], and [a, b, c]. :: y /\ / \ a/____\b /\ /\ / \ / \ /____\/____\ x c z Parameters ---------- n : int, optional The number of subdivisions to preform. Returns ------- new_sphere : Sphere The subdivided sphere. """ vertices = self.vertices faces = self.faces for _ in xrange(n): edges, mapping = unique_edges(faces, return_mapping=True) new_vertices = vertices[edges].sum(1) new_vertices /= vector_norm(new_vertices, keepdims=True) mapping += len(vertices) vertices = np.vstack([vertices, new_vertices]) x, y, z = faces.T a, b, c = mapping.T face1 = np.column_stack([x, a, c]) face2 = np.column_stack([y, b, a]) face3 = np.column_stack([z, c, b]) face4 = mapping faces = np.concatenate([face1, face2, face3, face4]) if len(vertices) < 2**16: faces = np.asarray(faces, dtype='uint16') return Sphere(xyz=vertices, faces=faces)
def subdivide(self, n=1): """Subdivides each face of the sphere into four new faces. New vertices are created at a, b, and c. Then each face [x, y, z] is divided into faces [x, a, c], [y, a, b], [z, b, c], and [a, b, c]. :: y /\ / \ a/____\b /\ /\ / \ / \ /____\/____\ x c z Parameters ---------- n : int, optional The number of subdivisions to preform. Returns ------- new_sphere : Sphere The subdivided sphere. """ vertices = self.vertices faces = self.faces for i in xrange(n): edges, mapping = unique_edges(faces, return_mapping=True) new_vertices = vertices[edges].sum(1) new_vertices /= vector_norm(new_vertices, keepdims=True) mapping += len(vertices) vertices = np.vstack([vertices, new_vertices]) x, y, z = faces.T a, b, c = mapping.T face1 = np.column_stack([x, a, c]) face2 = np.column_stack([y, b, a]) face3 = np.column_stack([z, c, b]) face4 = mapping faces = np.concatenate([face1, face2, face3, face4]) if len(vertices) < 2**16: faces = np.asarray(faces, dtype='uint16') return Sphere(xyz=vertices, faces=faces)
def test_hemisphere_faces(): t = (1 + np.sqrt(5)) / 2 vertices = np.array([ [-t, -1, 0], [-t, 1, 0], [1, 0, t], [-1, 0, t], [0, t, 1], [0, -t, 1], ]) vertices /= vector_norm(vertices, keepdims=True) faces = np.array([ [0, 1, 2], [0, 1, 3], [0, 2, 4], [1, 3, 4], [2, 3, 4], [1, 2, 5], [0, 3, 5], [2, 3, 5], [0, 4, 5], [1, 4, 5], ]) edges = np.array([ (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5), ]) h = HemiSphere(xyz=vertices) nt.assert_equal(len(h.edges), len(edges)) nt.assert_equal(array_to_set(h.edges), array_to_set(edges)) nt.assert_equal(len(h.faces), len(faces)) nt.assert_equal(array_to_set(h.faces), array_to_set(faces))
def test_hemisphere_faces(): t = (1 + np.sqrt(5)) / 2 vertices = np.array( [[ -t, -1, 0], [ -t, 1, 0], [ 1, 0, t], [ -1, 0, t], [ 0, t, 1], [ 0, -t, 1], ]) vertices /= vector_norm(vertices, keepdims=True) faces = np.array( [[0, 1, 2], [0, 1, 3], [0, 2, 4], [1, 3, 4], [2, 3, 4], [1, 2, 5], [0, 3, 5], [2, 3, 5], [0, 4, 5], [1, 4, 5], ]) edges = np.array( [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5), ]) h = HemiSphere(xyz=vertices) nt.assert_equal(len(h.edges), len(edges)) nt.assert_equal(array_to_set(h.edges), array_to_set(edges)) nt.assert_equal(len(h.faces), len(faces)) nt.assert_equal(array_to_set(h.faces), array_to_set(faces))
def test_b2q(): # conversion of b matrix to q q = np.array([1,2,3]) s = vector_norm(q) B = np.outer(q, q) yield assert_array_almost_equal(q*s, B2q(B)) q = np.array([1,2,3]) # check that the sign of the vector as positive x convention B = np.outer(-q, -q) yield assert_array_almost_equal(q*s, B2q(B)) q = np.array([-1, 2, 3]) B = np.outer(q, q) yield assert_array_almost_equal(-q*s, B2q(B)) B = np.eye(3) * -1 yield assert_raises(ValueError, B2q, B) # no error if we up the tolerance q = B2q(B, tol=1)
def test_b2q(): # conversion of b matrix to q q = np.array([1, 2, 3]) s = vector_norm(q) B = np.outer(q, q) yield assert_array_almost_equal(q * s, B2q(B)) q = np.array([1, 2, 3]) # check that the sign of the vector as positive x convention B = np.outer(-q, -q) yield assert_array_almost_equal(q * s, B2q(B)) q = np.array([-1, 2, 3]) B = np.outer(q, q) yield assert_array_almost_equal(-q * s, B2q(B)) B = np.eye(3) * -1 yield assert_raises(ValueError, B2q, B) # no error if we up the tolerance q = B2q(B, tol=1)
def tensor_odf(evals, evecs, sphere): """ Calculate the tensor Orientation Distribution Function Parameters ---------- evals : array (4D) Eigenvalues of a tensor. Shape (x, y, z, 3). evecs : array (5D) Eigenvectors of a tensor. Shape (x, y, z, 3, 3) sphere : sphere object The ODF will be calculated in each vertex of this sphere. """ odf = np.zeros((evals.shape[:3] + (sphere.vertices.shape[0], ))) mask = np.where((evals[..., 0] > 0) & (evals[..., 1] > 0) & (evals[..., 2] > 0)) lower = 4 * np.pi * np.sqrt(np.prod(evals[mask], -1)) projection = np.dot(sphere.vertices, evecs[mask]) projection /= np.sqrt(evals[mask]) odf[mask] = ((vector_norm(projection)**-3) / lower).T return odf
def tensor_odf(evals, evecs, sphere): """ Calculate the tensor Orientation Distribution Function Parameters ---------- evals : array (4D) Eigenvalues of a tensor. Shape (x, y, z, 3). evecs : array (5D) Eigenvectors of a tensor. Shape (x, y, z, 3, 3) sphere : sphere object The ODF will be calculated in each vertex of this sphere. """ odf = np.zeros((evals.shape[:3] + (sphere.vertices.shape[0],))) mask = np.where((evals[..., 0] > 0) & (evals[..., 1] > 0) & (evals[..., 2] > 0)) lower = 4 * np.pi * np.sqrt(np.prod(evals[mask], -1)) projection = np.dot(sphere.vertices, evecs[mask]) projection /= np.sqrt(evals[mask]) odf[mask] = ((vector_norm(projection) ** -3) / lower).T return odf
def gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=50, atol=1e-2, btens=None, **kwargs): """Creates a GradientTable from a bvals array and a bvecs array Parameters ---------- bvals : array_like (N,) The b-value, or magnitude, of each gradient direction. bvecs : array_like (N, 3) The direction, represented as a unit vector, of each gradient. b0_threshold : float Gradients with b-value less than or equal to `bo_threshold` are considered to not have diffusion weighting. atol : float Each vector in `bvecs` must be a unit vectors up to a tolerance of `atol`. btens : can be any of three options 1. a string specifying the shape of the encoding tensor for all volumes in data. Options: 'LTE', 'PTE', 'STE', 'CTE' corresponding to linear, planar, spherical, and "cigar-shaped" tensor encoding. Tensors are rotated so that linear and cigar tensors are aligned with the corresponding gradient direction and the planar tensor's normal is aligned with the corresponding gradient direction. Magnitude is scaled to match the b-value. 2. an array of strings of shape (N,), (N, 1), or (1, N) specifying encoding tensor shape for each volume separately. N corresponds to the number volumes in data. Options for elements in array: 'LTE', 'PTE', 'STE', 'CTE' corresponding to linear, planar, spherical, and "cigar-shaped" tensor encoding. Tensors are rotated so that linear and cigar tensors are aligned with the corresponding gradient direction and the planar tensor's normal is aligned with the corresponding gradient direction. Magnitude is scaled to match the b-value. 3. an array of shape (N,3,3) specifying the b-tensor of each volume exactly. N corresponds to the number volumes in data. No rotation or scaling is performed. Other Parameters ---------------- **kwargs : dict Other keyword inputs are passed to GradientTable. Returns ------- gradients : GradientTable A GradientTable with all the gradient information. See Also -------- GradientTable, gradient_table """ bvals = np.asarray(bvals, float) bvecs = np.asarray(bvecs, float) dwi_mask = bvals > b0_threshold # check that bvals is (N,) array and bvecs is (N, 3) unit vectors if bvals.ndim != 1 or bvecs.ndim != 2 or bvecs.shape[0] != bvals.shape[0]: raise ValueError("bvals and bvecs should be (N,) and (N, 3) arrays " "respectively, where N is the number of diffusion " "gradients") # checking for negative bvals if b0_threshold < 0: raise ValueError("Negative bvals in the data are not feasible") # Upper bound for the b0_threshold if b0_threshold >= 200: warn("b0_threshold has a value > 199") # checking for the correctness of bvals if b0_threshold < bvals.min(): warn("b0_threshold (value: {0}) is too low, increase your \ b0_threshold. It should be higher than the lowest b0 value \ ({1}).".format(b0_threshold, bvals.min())) bvecs = np.where(np.isnan(bvecs), 0, bvecs) bvecs_close_to_1 = abs(vector_norm(bvecs) - 1) <= atol if bvecs.shape[1] != 3: raise ValueError("bvecs should be (N, 3)") if not np.all(bvecs_close_to_1[dwi_mask]): raise ValueError("The vectors in bvecs should be unit (The tolerance " "can be modified as an input parameter)") bvecs = np.where(bvecs_close_to_1[:, None], bvecs, 0) bvals = bvals * bvecs_close_to_1 gradients = bvals[:, None] * bvecs grad_table = GradientTable(gradients, b0_threshold=b0_threshold, btens=btens, **kwargs) grad_table.bvals = bvals grad_table.bvecs = bvecs grad_table.b0s_mask = ~dwi_mask return grad_table
def gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=50, atol=1e-2, **kwargs): """Creates a GradientTable from a bvals array and a bvecs array Parameters ---------- bvals : array_like (N,) The b-value, or magnitude, of each gradient direction. bvecs : array_like (N, 3) The direction, represented as a unit vector, of each gradient. b0_threshold : float Gradients with b-value less than or equal to `bo_threshold` are considered to not have diffusion weighting. atol : float Each vector in `bvecs` must be a unit vectors up to a tolerance of `atol`. Other Parameters ---------------- **kwargs : dict Other keyword inputs are passed to GradientTable. Returns ------- gradients : GradientTable A GradientTable with all the gradient information. See Also -------- GradientTable, gradient_table """ bvals = np.asarray(bvals, np.float) bvecs = np.asarray(bvecs, np.float) dwi_mask = bvals > b0_threshold # check that bvals is (N,) array and bvecs is (N, 3) unit vectors if bvals.ndim != 1 or bvecs.ndim != 2 or bvecs.shape[0] != bvals.shape[0]: raise ValueError("bvals and bvecs should be (N,) and (N, 3) arrays " "respectively, where N is the number of diffusion " "gradients") # checking for negative bvals if b0_threshold < 0: raise ValueError("Negative bvals in the data are not feasible") # Upper bound for the b0_threshold if b0_threshold >= 200: warn("b0_threshold has a value > 199") # checking for the correctness of bvals if b0_threshold < bvals.min(): warn("b0_threshold (value: {0}) is too low, increase your \ b0_threshold. It should higher than the first b0 value \ ({1}).".format(b0_threshold, bvals.min())) bvecs = np.where(np.isnan(bvecs), 0, bvecs) bvecs_close_to_1 = abs(vector_norm(bvecs) - 1) <= atol if bvecs.shape[1] != 3: raise ValueError("bvecs should be (N, 3)") if not np.all(bvecs_close_to_1[dwi_mask]): raise ValueError("The vectors in bvecs should be unit (The tolerance " "can be modified as an input parameter)") bvecs = np.where(bvecs_close_to_1[:, None], bvecs, 0) bvals = bvals * bvecs_close_to_1 gradients = bvals[:, None] * bvecs grad_table = GradientTable(gradients, b0_threshold=b0_threshold, **kwargs) grad_table.bvals = bvals grad_table.bvecs = bvecs grad_table.b0s_mask = ~dwi_mask return grad_table
def bvals(self): return vector_norm(self.gradients)
t = (1 + np.sqrt(5)) / 2 icosahedron_vertices = np.array( [[ t, 1, 0], # 0 [ -t, 1, 0], # 1 [ t, -1, 0], # 2 [ -t, -1, 0], # 3 [ 1, 0, t], # 4 [ 1, 0, -t], # 5 [ -1, 0, t], # 6 [ -1, 0, -t], # 7 [ 0, t, 1], # 8 [ 0, -t, 1], # 9 [ 0, t, -1], # 10 [ 0, -t, -1], # 11 ]) icosahedron_vertices /= vector_norm(icosahedron_vertices, keepdims=True) icosahedron_faces = np.array( [[ 8, 4, 0], [ 2, 5, 0], [ 2, 5, 11], [ 9, 2, 11], [ 2, 4, 0], [ 9, 2, 4], [10, 8, 1], [10, 8, 0], [10, 5, 0], [ 6, 3, 1], [ 9, 6, 3], [ 6, 8, 1], [ 6, 8, 4], [ 9, 6, 4],
icosahedron_vertices = np.array([ [t, 1, 0], # 0 [-t, 1, 0], # 1 [t, -1, 0], # 2 [-t, -1, 0], # 3 [1, 0, t], # 4 [1, 0, -t], # 5 [-1, 0, t], # 6 [-1, 0, -t], # 7 [0, t, 1], # 8 [0, -t, 1], # 9 [0, t, -1], # 10 [0, -t, -1], ]) # 11 icosahedron_vertices /= vector_norm(icosahedron_vertices, keepdims=True) icosahedron_faces = np.array([ [8, 4, 0], [2, 5, 0], [2, 5, 11], [9, 2, 11], [2, 4, 0], [9, 2, 4], [10, 8, 1], [10, 8, 0], [10, 5, 0], [6, 3, 1], [9, 6, 3], [6, 8, 1], [6, 8, 4], [9, 6, 4],
def test_reorient_bvecs(): sq2 = np.sqrt(2) / 2 bvals = np.concatenate([[0], np.ones(6) * 1000]) bvecs = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [sq2, sq2, 0], [sq2, 0, sq2], [0, sq2, sq2]]) gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0) # The simple case: all affines are identity affs = np.zeros((6, 4, 4)) for i in range(4): affs[:, i, i] = 1 # We should get back the same b-vectors new_gt = reorient_bvecs(gt, affs) npt.assert_equal(gt.bvecs, new_gt.bvecs) # Now apply some rotations rotation_affines = [] rotated_bvecs = bvecs[:] for i in np.where(~gt.b0s_mask)[0]: rot_ang = np.random.rand() cos_rot = np.cos(rot_ang) sin_rot = np.sin(rot_ang) rotation_affines.append( np.array([[1, 0, 0, 0], [0, cos_rot, -sin_rot, 0], [0, sin_rot, cos_rot, 0], [0, 0, 0, 1]])) rotated_bvecs[i] = np.dot(rotation_affines[-1][:3, :3], bvecs[i]) # Copy over the rotation affines full_affines = rotation_affines[:] # And add some scaling and translation to each one to make this harder for i in range(len(full_affines)): full_affines[i] = np.dot( full_affines[i], np.array([[2.5, 0, 0, -10], [0, 2.2, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]])) gt_rot = gradient_table_from_bvals_bvecs(bvals, rotated_bvecs, b0_threshold=0) new_gt = reorient_bvecs(gt_rot, full_affines) # At the end of all this, we should be able to recover the original # vectors npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # We should be able to pass just the 3-by-3 rotation components to the same # effect new_gt = reorient_bvecs(gt_rot, np.array(rotation_affines)[:, :3, :3]) npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # Verify that giving the wrong number of affines raises an error: full_affines.append(np.zeros((4, 4))) npt.assert_raises(ValueError, reorient_bvecs, gt_rot, full_affines) # Shear components in the matrix need to be decomposed into rotation only, # and should not lead to scaling of the bvecs shear_affines = [] for i in np.where(~gt.b0s_mask)[0]: shear_affines.append( np.array([[1, 0, 1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) # atol is set to 1 here to do the scaling verification here, # so that the reorient_bvecs function does not throw an error itself new_gt = reorient_bvecs(gt, np.array(shear_affines)[:, :3, :3], atol=1) bvecs_close_to_1 = abs(vector_norm(new_gt.bvecs[~gt.b0s_mask]) - 1) <= 0.001 npt.assert_(np.all(bvecs_close_to_1))
def fix_bvals_bvecs(fbvals, fbvecs, b0_threshold=50, atol=1e-2, outpath=None, identifier="_fix", writeformat="classic"): """ Read b-values and b-vectors from disk Parameters ---------- fbvals : str Full path to file with b-values. None to not read bvals. fbvecs : str Full path of file with b-vectors. None to not read bvecs. Returns ------- bvals : array, (N,) or None bvecs : array, (N, 3) or None Notes ----- Files can be either '.bvals'/'.bvecs' or '.txt' or '.npy' (containing arrays stored with the appropriate values). """ # Loop over the provided inputs, reading each one in turn and adding them # to this list: bvals, bvecs = read_bvals(fbvals, fbvecs) # If bvecs is None, you can just return now w/o making more checks: if bvecs is None: return bvals, bvecs if bvecs.ndim != 2: raise IOError('bvec file should be saved as a two dimensional array') if bvecs.shape[1] > bvecs.shape[0]: bvecs = bvecs.T if bvecs.shape[1] == 4: if np.max(bvecs[:, 0]) > b0_threshold: if bvals is None: bvals = bvec[0, :] bvecs = np.delete(bvecs, 0, 1) # If bvals is None, you don't need to check that they have the same shape: if bvals is None: return bvals, bvecs if len(bvals.shape) > 1: raise IOError('bval file should have one row') if max(bvals.shape) != max(bvecs.shape): raise IOError('b-values and b-vectors shapes do not correspond') from dipy.core.geometry import vector_norm bvecs_close_to_1 = abs(vector_norm(bvecs) - 1) <= atol if bvecs.shape[1] != 3: raise ValueError("bvecs should be (N, 3)") dwi_mask = bvals > b0_threshold if not np.all(bvecs_close_to_1[dwi_mask]): correctvals = [ i for i, val in enumerate(bvecs_close_to_1) if val and dwi_mask[i] ] incorrectvals = [ i for i, val in enumerate(bvecs_close_to_1) if not val and dwi_mask[i] ] baseline_bval = bvals[correctvals[0]] for i in incorrectvals: if dwi_mask[i]: bvecs[i, :] = bvecs[i, :] / np.sqrt(bvals[i] / baseline_bval) bvecs_close_to_1 = abs(vector_norm(bvecs) - 1) <= atol if not np.all(bvecs_close_to_1[dwi_mask]): incorrectvals = [ i for i, val in enumerate(bvecs_close_to_1) if not val and dwi_mask[i] ] raise ValueError( "The vectors in bvecs should be unit (The tolerance " "can be modified as an input parameter)") if outpath is None: base, ext = splitext(fbvals) else: base = os.path.join(outpath, os.path.basename(fbvals).replace(".txt", "")) ext = ".txt" fbvals = base + identifier + ext if writeformat == "classic": np.savetxt(fbvals, bvals) if writeformat == "dsi": with open(fbvals, 'w') as File_object: for bval in bvals: if bval > 10: bval = int(round(bval)) else: bval = 0 File_object.write(str(bval) + "\t") #base, ext = splitext(fbvecs) basevecs = base.replace("bvals", "bvecs") fbvecs = basevecs + identifier + ext if writeformat == "classic": if not os.path.isfile(fbvecs): np.savetxt(fbvecs, bvecs) # with open(fbvecs, 'w') as f: # f.write(str(bvec)) if writeformat == "dsi": with open(fbvecs, 'w') as File_object: for i in [0, 1, 2]: for j in np.arange(np.shape(bvecs)[0]): if bvecs[j, i] == 0: bvec = 0 else: bvec = round(bvecs[j, i], 3) File_object.write(str(bvec) + "\t") File_object.write("\n") File_object.close() return fbvals, fbvecs
def nii2streamlines(imgfile, maskfile, bvals, bvecs, output_prefix): import numpy as np import nibabel as nib import os from dipy.core.geometry import vector_norm from dipy.reconst.dti import TensorModel print "nii2streamlines" img = nib.load(imgfile) bvals = np.genfromtxt(bvals) bvecs = np.genfromtxt(bvecs) # transpose if necessary to be Nx3 matrix if bvecs.shape[1] != 3: bvecs = bvecs.T # re-scale vectors to be close to 1 for bvec_i in range(bvecs.shape[0]): coeff = vector_norm(bvecs[bvec_i]) coeff_before = abs(coeff - 1) if coeff==0: continue bvecs[bvec_i] = bvecs[bvec_i]/coeff coeff_after = abs(vector_norm(bvecs[bvec_i]) - 1) print "Norm " + str(coeff_before) + "->" + str(coeff_after) from nipype.utils.filemanip import split_filename _, prefix, _ = split_filename(imgfile) from dipy.data import gradient_table gtab = gradient_table(bvals, bvecs) data = img.get_data() affine = img.get_affine() zooms = img.get_header().get_zooms()[:3] new_zooms = (2., 2., 2.) data2, affine2 = data, affine mask = nib.load(maskfile).get_data().astype(np.bool) tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data2, mask) from dipy.reconst.dti import fractional_anisotropy FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 fa_img = nib.Nifti1Image(FA, img.get_affine()) nib.save(fa_img, experiment_dir + '/' + output_prefix + '/' + ('%s_tensor_fa.nii.gz' % prefix)) evecs = tenfit.evecs evec_img = nib.Nifti1Image(evecs, img.get_affine()) nib.save(evec_img, experiment_dir + '/' + output_prefix + '/' + ('%s_tensor_evec.nii.gz' % prefix)) from dipy.data import get_sphere sphere = get_sphere('symmetric724') from dipy.reconst.dti import quantize_evecs peak_indices = quantize_evecs(tenfit.evecs, sphere.vertices) from dipy.tracking.eudx import EuDX eu = EuDX(FA, peak_indices, odf_vertices = sphere.vertices, a_low=0.2, seeds=10**6, ang_thr=35) tensor_streamlines = [streamline for streamline in eu] hdr = nib.trackvis.empty_header() hdr['voxel_size'] = new_zooms hdr['voxel_order'] = 'LPS' hdr['dim'] = data2.shape[:3] import dipy.tracking.metrics as dmetrics tensor_streamlines = ((sl, None, None) for sl in tensor_streamlines if dmetrics.length(sl) > 15) ten_sl_fname = experiment_dir + '/' + output_prefix + '/' + ('%s_streamline.trk' % prefix) nib.trackvis.write(ten_sl_fname, tensor_streamlines, hdr, points_space='voxel') return ten_sl_fname