def test_disperse_charges(): charges = np.array([[1., 0, 0], [0, 1., 0], [0, 0, 1.]]) d_sphere, pot = disperse_charges(HemiSphere(xyz=charges), 10) nt.assert_array_almost_equal(charges, d_sphere.vertices) a = np.sqrt(3) / 2 charges = np.array([[3. / 5, 4. / 5, 0], [4. / 5, 3. / 5, 0]]) expected_charges = np.array([[0, 1., 0], [1., 0, 0]]) d_sphere, pot = disperse_charges(HemiSphere(xyz=charges), 1000, .2) nt.assert_array_almost_equal(expected_charges, d_sphere.vertices) for ii in xrange(1, len(pot)): #check that the potential of the system is going down nt.assert_(pot[ii] - pot[ii - 1] <= 0) # Check that the disperse_charges does not blow up with a large constant d_sphere, pot = disperse_charges(HemiSphere(xyz=charges), 1000, 20.) nt.assert_array_almost_equal(expected_charges, d_sphere.vertices) for ii in xrange(1, len(pot)): #check that the potential of the system is going down nt.assert_(pot[ii] - pot[ii - 1] <= 0) #check that the function seems to work with a larger number of charges charges = np.arange(21).reshape(7, 3) norms = np.sqrt((charges * charges).sum(-1)) charges = charges / norms[:, None] d_sphere, pot = disperse_charges(HemiSphere(xyz=charges), 1000, .05) for ii in xrange(1, len(pot)): #check that the potential of the system is going down nt.assert_(pot[ii] - pot[ii - 1] <= 0) #check that the resulting charges all lie on the unit sphere d_charges = d_sphere.vertices norms = np.sqrt((d_charges * d_charges).sum(-1)) nt.assert_array_almost_equal(norms, 1)
def test_hemisphere_subdivide(): def flip(vertices): x, y, z = vertices.T f = (z < 0) | ((z == 0) & (y < 0)) | ((z == 0) & (y == 0) & (x < 0)) return 1 - 2 * f[:, None] decimals = 6 # Test HemiSphere.subdivide # Create a hemisphere by dividing a hemi-icosahedron hemi1 = HemiSphere.from_sphere(unit_icosahedron).subdivide(4) vertices1 = np.round(hemi1.vertices, decimals) vertices1 *= flip(vertices1) order = np.lexsort(vertices1.T) vertices1 = vertices1[order] # Create a hemisphere from a subdivided sphere sphere = unit_icosahedron.subdivide(4) hemi2 = HemiSphere.from_sphere(sphere) vertices2 = np.round(hemi2.vertices, decimals) vertices2 *= flip(vertices2) order = np.lexsort(vertices2.T) vertices2 = vertices2[order] # The two hemispheres should have the same vertices up to their order nt.assert_array_equal(vertices1, vertices2) # Create a hemisphere from vertices hemi3 = HemiSphere(xyz=hemi1.vertices) nt.assert_array_equal(hemi1.faces, hemi3.faces) nt.assert_array_equal(hemi1.edges, hemi3.edges)
def test_hemisphere_subdivide(): def flip(vertices): x, y, z = vertices.T f = (z < 0) | ((z == 0) & (y < 0)) | ((z == 0) & (y == 0) & (x < 0)) return 1 - 2*f[:, None] decimals = 6 # Test HemiSphere.subdivide # Create a hemisphere by dividing a hemi-icosahedron hemi1 = HemiSphere.from_sphere(unit_icosahedron).subdivide(4) vertices1 = np.round(hemi1.vertices, decimals) vertices1 *= flip(vertices1) order = np.lexsort(vertices1.T) vertices1 = vertices1[order] # Create a hemisphere from a subdivided sphere sphere = unit_icosahedron.subdivide(4) hemi2 = HemiSphere.from_sphere(sphere) vertices2 = np.round(hemi2.vertices, decimals) vertices2 *= flip(vertices2) order = np.lexsort(vertices2.T) vertices2 = vertices2[order] # The two hemispheres should have the same vertices up to their order nt.assert_array_equal(vertices1, vertices2) # Create a hemisphere from vertices hemi3 = HemiSphere(xyz=hemi1.vertices) nt.assert_array_equal(hemi1.faces, hemi3.faces) nt.assert_array_equal(hemi1.edges, hemi3.edges)
def test_mirror(): verts = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [-1, -1, -1]] verts = np.array(verts, 'float') verts = verts / np.sqrt((verts * verts).sum(-1)[:, None]) faces = [[0, 1, 3], [0, 2, 3], [1, 2, 3]] h = HemiSphere(xyz=verts, faces=faces) s = h.mirror() nt.assert_equal(len(s.vertices), 8) nt.assert_equal(len(s.faces), 6) verts = s.vertices def _angle(a, b): return np.arccos(np.dot(a, b)) for triangle in s.faces: a, b, c = triangle nt.assert_(_angle(verts[a], verts[b]) <= np.pi/2) nt.assert_(_angle(verts[a], verts[c]) <= np.pi/2) nt.assert_(_angle(verts[b], verts[c]) <= np.pi/2)
def test_mirror(): verts = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [-1, -1, -1]] verts = np.array(verts, 'float') verts = verts / np.sqrt((verts * verts).sum(-1)[:, None]) faces = [[0, 1, 3], [0, 2, 3], [1, 2, 3]] h = HemiSphere(xyz=verts, faces=faces) s = h.mirror() nt.assert_equal(len(s.vertices), 8) nt.assert_equal(len(s.faces), 6) verts = s.vertices def _angle(a, b): return np.arccos(np.dot(a, b)) for triangle in s.faces: a, b, c = triangle nt.assert_(_angle(verts[a], verts[b]) <= np.pi/2) nt.assert_(_angle(verts[a], verts[c]) <= np.pi/2) nt.assert_(_angle(verts[b], verts[c]) <= np.pi/2)
def test_hemisphere_constructor(): s0 = HemiSphere(xyz=verts) s1 = HemiSphere(theta=theta, phi=phi) s2 = HemiSphere(*verts.T) uniq_verts = verts[::2].T rU, thetaU, phiU = cart2sphere(*uniq_verts) nt.assert_array_almost_equal(s0.theta, s1.theta) nt.assert_array_almost_equal(s0.theta, s2.theta) nt.assert_array_almost_equal(s0.theta, thetaU) nt.assert_array_almost_equal(s0.phi, s1.phi) nt.assert_array_almost_equal(s0.phi, s2.phi) nt.assert_array_almost_equal(s0.phi, phiU)
def test_peak_direction_tracker(): """This tests that the Peaks And Metrics Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing peaks_values_lookup = np.array([[0., 0.], [1., 0.], [1., 0.], [0.5, 0.5]]) peaks_indices_lookup = np.array([[-1, -1], [0, -1], [1, -1], [0, 1]]) # PeaksAndMetricsDirectionGetter needs at 3 slices on each axis to work simple_image = np.zeros([5, 6, 3], dtype=int) simple_image[:, :, 1] = np.array([[0, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) dg = PeaksAndMetrics() dg.sphere = sphere dg.peak_values = peaks_values_lookup[simple_image] dg.peak_indices = peaks_indices_lookup[simple_image] dg.ang_thr = 90 mask = (simple_image >= 0).astype(float) tc = ThresholdTissueClassifier(mask, 0.5) seeds = [np.array([1., 1., 1.]), np.array([2., 4., 1.]), np.array([1., 3., 1.]), np.array([4., 4., 1.])] streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 1.], [1., 1., 1.], [2., 1., 1.], [3., 1., 1.], [4., 1., 1.]]), np.array([[2., 0., 1.], [2., 1., 1.], [2., 2., 1.], [2., 3., 1.], [2., 4., 1.], [2., 5., 1.]]), np.array([[0., 3., 1.], [1., 3., 1.], [2., 3., 1.], [2., 4., 1.], [2., 5., 1.]]), np.array([[4., 4., 1.]])] for i, sl in enumerate(streamlines): npt.assert_(np.allclose(sl, expected[i]))
def test_bdg_initial_direction(): """This test the number of inital direction." """ hsph_updated = HemiSphere.from_sphere(unit_icosahedron).subdivide(2) vertices = hsph_updated.vertices bvecs = vertices bvals = np.ones(len(vertices)) * 1000 bvecs = np.insert(bvecs, 0, np.array([0, 0, 0]), axis=0) bvals = np.insert(bvals, 0, 0) gtab = gradient_table(bvals, bvecs) # test that we get one direction when we have a single tensor voxel = single_tensor(gtab).reshape([1, 1, 1, -1]) dti_model = dti.TensorModel(gtab) boot_dg = BootDirectionGetter.from_data(voxel, dti_model, 30, sh_order=6) initial_direction = boot_dg.initial_direction(np.zeros(3)) npt.assert_equal(len(initial_direction), 1) npt.assert_allclose(initial_direction[0], [1, 0, 0], atol=0.1) # test that we get multiple directions when we have a multi-tensor mevals = np.array([[1.5, 0.4, 0.4], [1.5, 0.4, 0.4]]) * 1e-3 fracs = [60, 40] voxel, primary_evecs = multi_tensor(gtab, mevals, fractions=fracs, snr=None) voxel = voxel.reshape([1, 1, 1, -1]) response = (np.array([0.0015, 0.0004, 0.0004]), 1) csd_model = ConstrainedSphericalDeconvModel(gtab, response=response, sh_order=4) boot_dg = BootDirectionGetter.from_data(voxel, csd_model, 30) initial_direction = boot_dg.initial_direction(np.zeros(3)) npt.assert_equal(len(initial_direction), 2) npt.assert_allclose(initial_direction, primary_evecs, atol=0.1)
def _write_external_formats(self, runtime, fit_obj, mask_img, suffix): if not (self.inputs.write_fibgz or self.inputs.write_mif): return # Convert to amplitudes for other software verts, faces = get_dsi_studio_ODF_geometry("odf8") num_dirs, _ = verts.shape hemisphere = num_dirs // 2 x, y, z = verts[:hemisphere].T hs = HemiSphere(x=x, y=y, z=z) odf_amplitudes = nb.Nifti1Image(fit_obj.odf(hs), mask_img.affine, mask_img.header) if self.inputs.write_fibgz: output_fib_file = fname_presuffix(self.inputs.dwi_file, suffix=suffix+".fib", newpath=runtime.cwd, use_ext=False) LOGGER.info("Writing DSI Studio fib file %s", output_fib_file) amplitudes_to_fibgz(odf_amplitudes, verts, faces, output_fib_file, mask_img, num_fibers=5) self._results['fibgz'] = output_fib_file if self.inputs.write_mif: output_mif_file = fname_presuffix(self.inputs.dwi_file, suffix=suffix+".mif", newpath=runtime.cwd, use_ext=False) LOGGER.info("Writing sh mif file %s", output_mif_file) amplitudes_to_sh_mif(odf_amplitudes, verts, output_mif_file, runtime.cwd) self._results['fod_sh_mif'] = output_mif_file
def test_bdg_residual(): """This tests the bootstrapping residual. """ hsph_updated = HemiSphere.from_sphere(unit_icosahedron).subdivide(2) vertices = hsph_updated.vertices bvecs = vertices bvals = np.ones(len(vertices)) * 1000 bvecs = np.insert(bvecs, 0, np.array([0, 0, 0]), axis=0) bvals = np.insert(bvals, 0, 0) gtab = gradient_table(bvals, bvecs) r, theta, phi = cart2sphere(*vertices.T) with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=shm.descoteaux07_legacy_msg, category=PendingDeprecationWarning) B, m, n = shm.real_sh_descoteaux(6, theta, phi) shm_coeff = np.random.random(B.shape[1]) # sphere_func is sampled of the spherical function for each point of # the sphere sphere_func = np.dot(shm_coeff, B.T) voxel = np.concatenate((np.zeros(1), sphere_func)) data = np.tile(voxel, (3, 3, 3, 1)) with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=shm.descoteaux07_legacy_msg, category=PendingDeprecationWarning) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=6) boot_pmf_gen = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=6) # Two boot samples should be the same odf1 = boot_pmf_gen.get_pmf(np.array([1.5, 1.5, 1.5])) odf2 = boot_pmf_gen.get_pmf(np.array([1.5, 1.5, 1.5])) npt.assert_array_almost_equal(odf1, odf2) # A boot sample with less sh coeffs should have residuals, thus the two # should be different with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=shm.descoteaux07_legacy_msg, category=PendingDeprecationWarning) boot_pmf_gen2 = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=4) odf1 = boot_pmf_gen2.get_pmf(np.array([1.5, 1.5, 1.5])) odf2 = boot_pmf_gen2.get_pmf(np.array([1.5, 1.5, 1.5])) npt.assert_(np.any(odf1 != odf2)) # test with a gtab with two shells and assert you get an error bvals[-1] = 2000 gtab = gradient_table(bvals, bvecs) with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=shm.descoteaux07_legacy_msg, category=PendingDeprecationWarning) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=6) npt.assert_raises(ValueError, BootPmfGen, data, csd_model, hsph_updated, 6)
def test_sphere_scaling_csdmodel(): """Check that mirroring regulization sphere does not change the result of csddeconv model""" _, fbvals, fbvecs = get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) angles = [(0, 0), (60, 0)] S, sticks = multi_tensor(gtab, mevals, 100., angles=angles, fractions=[50, 50], snr=None) sphere = get_sphere('symmetric362') hemi = HemiSphere.from_sphere(sphere) response = (np.array([0.0015, 0.0003, 0.0003]), 100) model_full = ConstrainedSphericalDeconvModel(gtab, response, reg_sphere=sphere) model_hemi = ConstrainedSphericalDeconvModel(gtab, response, reg_sphere=hemi) csd_fit_full = model_full.fit(S) csd_fit_hemi = model_hemi.fit(S) assert_array_almost_equal(csd_fit_full.shm_coeff, csd_fit_hemi.shm_coeff)
def _create_mt_sim(mevals, angles, fractions, S0, SNR, half_sphere=False): _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) S, sticks = multi_tensor(gtab, mevals, S0, angles=angles, fractions=fractions, snr=SNR) sphere = get_sphere('symmetric724').subdivide(2) if half_sphere: sphere = HemiSphere.from_sphere(sphere) odf_gt = multi_tensor_odf(sphere.vertices, mevals, angles=angles, fractions=fractions) return odf_gt, sticks, sphere
def generate_bvecs(N, iters=5000): """Generates N bvectors. Uses dipy.core.sphere.disperse_charges to model electrostatic repulsion on a unit sphere. Parameters ---------- N : int The number of bvectors to generate. This should be equal to the number of bvals used. iters : int Number of iterations to run. Returns ------- bvecs : (N,3) ndarray The generated directions, represented as a unit vector, of each gradient. """ theta = np.pi * np.random.rand(N) phi = 2 * np.pi * np.random.rand(N) hsph_initial = HemiSphere(theta=theta, phi=phi) hsph_updated, potential = disperse_charges(hsph_initial, iters) bvecs = hsph_updated.vertices return bvecs
def _generate_gradients(ndirs=64, values=[1000, 3000], nb0s=1): """ Automatically generate a `gradient table <http://nipy.org/dipy/examples_built/gradients_spheres.html#example-gradients-spheres>`_ """ import numpy as np from dipy.core.sphere import disperse_charges, Sphere, HemiSphere from dipy.core.gradients import gradient_table theta = np.pi * np.random.rand(ndirs) phi = 2 * np.pi * np.random.rand(ndirs) hsph_initial = HemiSphere(theta=theta, phi=phi) hsph_updated, potential = disperse_charges(hsph_initial, 5000) values = np.atleast_1d(values).tolist() vertices = hsph_updated.vertices bvecs = vertices.copy() bvals = np.ones(vertices.shape[0]) * values[0] for v in values[1:]: bvecs = np.vstack((bvecs, vertices)) bvals = np.hstack((bvals, v * np.ones(vertices.shape[0]))) for i in range(0, nb0s): bvals = bvals.tolist() bvals.insert(0, 0) bvecs = bvecs.tolist() bvecs.insert(0, np.zeros(3)) return gradient_table(bvals, bvecs)
def test_peak_direction_tracker(): """This tests that the Peaks And Metrics Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing peaks_values_lookup = np.array([[0., 0.], [1., 0.], [1., 0.], [0.5, 0.5]]) peaks_indices_lookup = np.array([[-1, -1], [0, -1], [1, -1], [0, 1]]) # PeaksAndMetricsDirectionGetter needs at 3 slices on each axis to work simple_image = np.zeros([5, 6, 3], dtype=int) simple_image[:, :, 1] = np.array([[0, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) dg = PeaksAndMetrics() dg.sphere = sphere dg.peak_values = peaks_values_lookup[simple_image] dg.peak_indices = peaks_indices_lookup[simple_image] dg.ang_thr = 90 mask = (simple_image >= 0).astype(float) tc = ThresholdTissueClassifier(mask, 0.5) seeds = [np.array([1., 1., 1.]), np.array([2., 4., 1.]), np.array([1., 3., 1.]), np.array([4., 4., 1.])] streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 1.], [1., 1., 1.], [2., 1., 1.], [3., 1., 1.], [4., 1., 1.]]), np.array([[2., 0., 1.], [2., 1., 1.], [2., 2., 1.], [2., 3., 1.], [2., 4., 1.], [2., 5., 1.]]), np.array([[0., 3., 1.], [1., 3., 1.], [2., 3., 1.], [2., 4., 1.], [2., 5., 1.]]), np.array([[4., 4., 1.]])] for i, sl in enumerate(streamlines): npt.assert_(np.allclose(sl, expected[i]))
def test_csd_superres(): """ Check the quality of csdfit with high SH order. """ _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) # img, gtab = read_stanford_hardi() evals = np.array([[1.5, .3, .3]]) * [[1.], [1.]] / 1000. S, sticks = multi_tensor(gtab, evals, snr=None, fractions=[55., 45.]) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings(action="always", message="Number of parameters required.*", category=UserWarning) model16 = ConstrainedSphericalDeconvModel(gtab, (evals[0], 3.), sh_order=16) assert_greater_equal(len(w), 1) npt.assert_(issubclass(w[-1].category, UserWarning)) fit16 = model16.fit(S) sphere = HemiSphere.from_sphere(get_sphere('symmetric724')) # print local_maxima(fit16.odf(default_sphere), default_sphere.edges) d, v, ind = peak_directions(fit16.odf(sphere), sphere, relative_peak_threshold=.2, min_separation_angle=0) # Check that there are two peaks assert_equal(len(d), 2) # Check that peaks line up with sticks cos_sim = abs((d * sticks).sum(1)) ** .5 assert_(all(cos_sim > .99))
def create_sphere(n_pts): theta = np.pi * np.random.rand(n_pts) phi = 2 * np.pi * np.random.rand(n_pts) hsph_initial = HemiSphere(theta=theta, phi=phi) hsph_updated, _ = disperse_charges(hsph_initial, 5000) sph = Sphere(xyz=np.vstack((hsph_updated.vertices, -hsph_updated.vertices))) return sph
def test_MaximumDeterministicTracker(): """This tests that the Maximum Deterministic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.4, .6, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 90, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[ 0., 1., 0.], [ 1., 1., 0.], [ 2., 1., 0.], [ 2., 2., 0.], [ 2., 3., 0.], [ 2., 4., 0.], [ 2., 5., 0.]]), np.array([[ 0., 1., 0.], [ 1., 1., 0.], [ 2., 1., 0.], [ 3., 1., 0.], [ 4., 1., 0.]]) ] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) for sl in streamlines: dir = ( -sphere.vertices[0] ).copy() if not allclose(sl, expected[0]): raise AssertionError() # The first path is not possible if 90 degree turns are excluded dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 80, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1]))
def genDirs(N): init_dirs = np.random.randn(N, 3) init_dirs /= np.linalg.norm(init_dirs, axis=1)[:, None] init_hemi = HemiSphere(xyz=init_dirs) dirs_hemi, _ = disperse_charges(init_hemi, iters=1000) pts = dirs_hemi.vertices # shifting to z+ hemi pts = pts * np.sign(pts[:, 2])[:, None] return pts
def test_MaximumDeterministicTracker(): """This tests that the Maximum Deterministic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.4, .6, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 90, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) for sl in streamlines: if not allclose(sl, expected[0]): raise AssertionError() # The first path is not possible if 90 degree turns are excluded dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 80, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1]))
def test_save_seeds(): tissue = np.array([[2, 1, 1, 2, 1], [2, 2, 1, 1, 2], [1, 1, 1, 1, 1], [1, 1, 1, 2, 2], [0, 1, 1, 1, 2], [0, 1, 1, 0, 2], [1, 0, 1, 1, 1]]) tissue = tissue[None] sphere = HemiSphere.from_sphere(unit_octahedron) pmf_lookup = np.array([[0., 0., 0., ], [0., 0., 1.]]) pmf = pmf_lookup[(tissue > 0).astype("int")] # Create a seeds along x = np.array([0., 0, 0, 0, 0, 0, 0]) y = np.array([0., 1, 2, 3, 4, 5, 6]) z = np.array([1., 1, 1, 0, 1, 1, 1]) seeds = np.column_stack([x, y, z]) # Set up tracking endpoint_mask = tissue == TissueTypes.ENDPOINT invalidpoint_mask = tissue == TissueTypes.INVALIDPOINT tc = ActTissueClassifier(endpoint_mask, invalidpoint_mask) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) # valid streamlines only streamlines_generator = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=False, save_seeds=True) streamlines_not_all = iter(streamlines_generator) # Verifiy that seeds are returned by the LocalTracker _, seed = next(streamlines_not_all) npt.assert_equal(seed, seeds[0]) _, seed = next(streamlines_not_all) npt.assert_equal(seed, seeds[1]) # Verifiy that seeds are returned by the PFTTracker also pft_streamlines = ParticleFilteringTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., max_cross=1, return_all=False, save_seeds=True) streamlines = iter(pft_streamlines) _, seed = next(streamlines) npt.assert_equal(seed, seeds[0]) _, seed = next(streamlines) npt.assert_equal(seed, seeds[1])
def send_targets_to_half_sphere(t, sphere): # toDo. See how to include this to use classification on the half sphere. half_sphere = HemiSphere.from_sphere(sphere) peak = half_sphere.find_closest(t) # Checking cosine similarity: if abs(np.dot(peak, t)) > abs(np.dot(peak, -t)): t = -t return t
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 get_uniform_hemisphere_with_points(action_space: int, seed=42) -> HemiSphere: if seed is not None: np.random.seed(seed) phi = np.pi * np.random.rand(action_space) theta = 2 * np.pi * np.random.rand(action_space) sphere = HemiSphere(theta=theta, phi=phi) # Sphere(theta=theta, phi=phi) sphere, _ = disperse_charges( sphere, 5000) # enforce uniform distribution of our points return sphere
def displaySphericalHist(odf, pts, minmax=False): # assumes pts and odf are hemisphere fullsphere = HemiSphere(xyz=pts).mirror() fullodf = np.concatenate((odf, odf), axis=0) r = fvtk.ren() if minmax: a = fvtk.sphere_funcs(fullodf - fullodf.min(), fullsphere) else: a = fvtk.sphere_funcs(fullodf, fullsphere) fvtk.add(r, a) fvtk.show(r)
def test_pmf_from_sh(): sphere = HemiSphere.from_sphere(unit_octahedron) pmfgen = SHCoeffPmfGen(np.ones([2, 2, 2, 28]), sphere, None) # Test that the pmf is greater than 0 for a valid point pmf = pmfgen.get_pmf(np.array([0, 0, 0], dtype='float')) npt.assert_equal(np.sum(pmf) > 0, True) # Test that the pmf is 0 for invalid Points npt.assert_array_equal(pmfgen.get_pmf(np.array([-1, 0, 0], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_array_equal(pmfgen.get_pmf(np.array([0, 0, 10], dtype='float')), np.zeros(len(sphere.vertices)))
def create_repulsion_sphere(n_points, n_iter): """ Create a sphere using electrostatic repulsion. params: npoints: number of points in the electrostatic repulsion n_iter: number of iterations to optimise energy return: HemiSphere object with n points vertices """ theta = np.pi * np.random.rand(n_points) phi = 2 * np.pi * np.random.rand(n_points) hsph_initial = HemiSphere(theta=theta, phi=phi) hsph_updated, energy = disperse_charges(hsph_initial, iters=n_iter) sph = hsph_updated return sph
def test_pmf_from_sh(): sphere = HemiSphere.from_sphere(unit_octahedron) pmfgen = SHCoeffPmfGen(np.ones([2, 2, 2, 28]), sphere, None) # Test that the pmf is greater than 0 for a valid point pmf = pmfgen.get_pmf(np.array([0, 0, 0], dtype='float')) npt.assert_equal(np.sum(pmf) > 0, True) # Test that the pmf is 0 for invalid Points npt.assert_array_equal(pmfgen.get_pmf(np.array([-1, 0, 0], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_array_equal(pmfgen.get_pmf(np.array([0, 0, 10], dtype='float')), np.zeros(len(sphere.vertices)))
def test_closest_peak_tracker(): """This tests that the Closest Peak Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.5, .5, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [2, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.]), np.array([2., 4., 0.])] mask = (simple_image > 0).astype(float) tc = BinaryTissueClassifier(mask) dg = ClosestPeakDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = Streamlines(LocalTracking(dg, tc, seeds, np.eye(4), 1.)) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]]), np.array([[2., 0., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) if not allclose(streamlines[0], expected[0]): raise AssertionError() if not allclose(streamlines[1], expected[1]): raise AssertionError()
def test_closest_peak_tracker(): """This tests that the Closest Peak Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.5, .5, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [2, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.]), np.array([2., 4., 0.])] mask = (simple_image > 0).astype(float) tc = BinaryTissueClassifier(mask) dg = ClosestPeakDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = Streamlines(LocalTracking(dg, tc, seeds, np.eye(4), 1.)) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]]), np.array([[2., 0., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) if not allclose(streamlines[0], expected[0]): raise AssertionError() if not allclose(streamlines[1], expected[1]): raise AssertionError()
def create_symmetric_repulsion_sphere(n_points, n_iter): """ Create a full Sphere object using electrostatic repulsion. params: npoints: number of points in the electrostatic repulsion n_iter: number of iterations to optimise energy return: Sphere object with 2*npoints vertices """ theta = np.pi * np.random.rand(n_points) phi = 2 * np.pi * np.random.rand(n_points) hsph_initial = HemiSphere(theta=theta, phi=phi) hsph_updated, energy = disperse_charges(hsph_initial, iters=n_iter) sph = Sphere(xyz=np.vstack((hsph_updated.vertices, -hsph_updated.vertices))) return sph
def test_boot_pmf(): # This tests the local model used for the bootstrapping. hsph_updated = HemiSphere.from_sphere(unit_octahedron) vertices = hsph_updated.vertices bvecs = vertices bvals = np.ones(len(vertices)) * 1000 bvecs = np.insert(bvecs, 0, np.array([0, 0, 0]), axis=0) bvals = np.insert(bvals, 0, 0) gtab = gradient_table(bvals, bvecs) voxel = single_tensor(gtab) data = np.tile(voxel, (3, 3, 3, 1)) point = np.array([1., 1., 1.]) tensor_model = TensorModel(gtab) boot_pmf_gen = BootPmfGen(data, model=tensor_model, sphere=hsph_updated) no_boot_pmf = boot_pmf_gen.get_pmf_no_boot(point) model_pmf = tensor_model.fit(voxel).odf(hsph_updated) npt.assert_equal(len(hsph_updated.vertices), no_boot_pmf.shape[0]) npt.assert_array_almost_equal(no_boot_pmf, model_pmf) # test model spherical harmonic order different than bootstrap order with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=6) # Tests that the first catched warning comes from # the CSD model constructor npt.assert_(issubclass(w[0].category, UserWarning)) npt.assert_("Number of parameters required " in str(w[0].message)) # Tests that additionnal warnings are raised for outdated SH basis npt.assert_(len(w) > 1) boot_pmf_gen_sh4 = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=4) pmf_sh4 = boot_pmf_gen_sh4.get_pmf(point) npt.assert_equal(len(hsph_updated.vertices), pmf_sh4.shape[0]) npt.assert_(np.sum(pmf_sh4.shape) > 0) boot_pmf_gen_sh8 = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=8) pmf_sh8 = boot_pmf_gen_sh8.get_pmf(point) npt.assert_equal(len(hsph_updated.vertices), pmf_sh8.shape[0]) npt.assert_(np.sum(pmf_sh8.shape) > 0)
def _qti_gtab(): """Return a gradient table with b0, 2 shells, 30 directions, and linear and planar tensor encoding for fitting QTI.""" np.random.seed(123) n_dir = 30 hsph_initial = HemiSphere(theta=np.pi * np.random.rand(n_dir), phi=2 * np.pi * np.random.rand(n_dir)) hsph_updated, _ = disperse_charges(hsph_initial, 100) directions = hsph_updated.vertices bvecs = np.vstack([np.zeros(3)] + [directions for _ in range(4)]) bvals = np.concatenate((np.zeros(1), np.ones(n_dir), np.ones(n_dir) * 2, np.ones(n_dir), np.ones(n_dir) * 2)) btens = np.array(['LTE' for i in range(1 + n_dir * 2)] + ['PTE' for i in range(n_dir * 2)]) gtab = gradient_table(bvals, bvecs, btens=btens) return gtab
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 _get_direction_getter(args, mask_data): sh_data = nib.load(args.sh_file).get_data().astype('float64') sphere = HemiSphere.from_sphere(get_sphere(args.sphere)) theta = get_theta(args.theta, args.algo) if args.algo in ['det', 'prob']: if args.algo == 'det': dg_class = DeterministicMaximumDirectionGetter else: dg_class = ProbabilisticDirectionGetter return dg_class.from_shcoeff(shcoeff=sh_data, max_angle=theta, sphere=sphere, basis_type=args.sh_basis, relative_peak_threshold=args.sf_threshold) # Code for type EUDX. We don't use peaks_from_model # because we want the peaks from the provided sh. sh_shape_3d = sh_data.shape[:-1] npeaks = 5 peak_dirs = np.zeros((sh_shape_3d + (npeaks, 3))) peak_values = np.zeros((sh_shape_3d + (npeaks, ))) peak_indices = np.full((sh_shape_3d + (npeaks, )), -1, dtype='int') b_matrix = get_b_matrix(find_order_from_nb_coeff(sh_data), sphere, args.sh_basis) for idx in np.ndindex(sh_shape_3d): if not mask_data[idx]: continue directions, values, indices = get_maximas(sh_data[idx], sphere, b_matrix, args.sf_threshold, 0) if values.shape[0] != 0: n = min(npeaks, values.shape[0]) peak_dirs[idx][:n] = directions[:n] peak_values[idx][:n] = values[:n] peak_indices[idx][:n] = indices[:n] dg = PeaksAndMetrics() dg.sphere = sphere dg.peak_dirs = peak_dirs dg.peak_values = peak_values dg.peak_indices = peak_indices dg.ang_thr = theta dg.qa_thr = args.sf_threshold return dg
def test_pmf_from_array(): sphere = HemiSphere.from_sphere(unit_octahedron) pmfgen = SimplePmfGen(np.ones([2, 2, 2, len(sphere.vertices)])) # Test that the pmf is greater than 0 for a valid point pmf = pmfgen.get_pmf(np.array([0, 0, 0], dtype='float')) npt.assert_equal(np.sum(pmf) > 0, True) # Test that the pmf is 0 for invalid Points npt.assert_array_equal(pmfgen.get_pmf(np.array([-1, 0, 0], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_array_equal(pmfgen.get_pmf(np.array([0, 0, 10], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_raises( ValueError, lambda: SimplePmfGen(np.ones([2, 2, 2, len(sphere.vertices)]) * -1))
def test_pmf_from_array(): sphere = HemiSphere.from_sphere(unit_octahedron) pmfgen = SimplePmfGen(np.ones([2, 2, 2, len(sphere.vertices)])) # Test that the pmf is greater than 0 for a valid point pmf = pmfgen.get_pmf(np.array([0, 0, 0], dtype='float')) npt.assert_equal(np.sum(pmf) > 0, True) # Test that the pmf is 0 for invalid Points npt.assert_array_equal(pmfgen.get_pmf(np.array([-1, 0, 0], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_array_equal(pmfgen.get_pmf(np.array([0, 0, 10], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_raises( ValueError, lambda: SimplePmfGen(np.ones([2, 2, 2, len(sphere.vertices)])*-1))
def test_pmf_from_sh(): sphere = HemiSphere.from_sphere(unit_octahedron) with warnings.catch_warnings(): warnings.filterwarnings("ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) pmfgen = SHCoeffPmfGen(np.ones([2, 2, 2, 28]), sphere, None) # Test that the pmf is greater than 0 for a valid point pmf = pmfgen.get_pmf(np.array([0, 0, 0], dtype='float')) npt.assert_equal(np.sum(pmf) > 0, True) # Test that the pmf is 0 for invalid Points npt.assert_array_equal(pmfgen.get_pmf(np.array([-1, 0, 0], dtype='float')), np.zeros(len(sphere.vertices))) npt.assert_array_equal(pmfgen.get_pmf(np.array([0, 0, 10], dtype='float')), np.zeros(len(sphere.vertices)))
def test_bdg_residual(): """This tests the bootstrapping residual. """ hsph_updated = HemiSphere.from_sphere(unit_icosahedron).subdivide(2) vertices = hsph_updated.vertices bvecs = vertices bvals = np.ones(len(vertices)) * 1000 bvecs = np.insert(bvecs, 0, np.array([0, 0, 0]), axis=0) bvals = np.insert(bvals, 0, 0) gtab = gradient_table(bvals, bvecs) r, theta, phi = cart2sphere(*vertices.T) B, m, n = shm.real_sym_sh_basis(6, theta, phi) shm_coeff = np.random.random(B.shape[1]) # sphere_func is sampled of the spherical function for each point of # the sphere sphere_func = np.dot(shm_coeff, B.T) voxel = np.concatenate((np.zeros(1), sphere_func)) data = np.tile(voxel, (3, 3, 3, 1)) csd_model = ConstrainedSphericalDeconvModel(gtab, None, sh_order=6) boot_pmf_gen = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=6) # Two boot samples should be the same odf1 = boot_pmf_gen.get_pmf(np.array([1.5, 1.5, 1.5])) odf2 = boot_pmf_gen.get_pmf(np.array([1.5, 1.5, 1.5])) npt.assert_array_almost_equal(odf1, odf2) # A boot sample with less sh coeffs should have residuals, thus the two # should be different boot_pmf_gen2 = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=4) odf1 = boot_pmf_gen2.get_pmf(np.array([1.5, 1.5, 1.5])) odf2 = boot_pmf_gen2.get_pmf(np.array([1.5, 1.5, 1.5])) npt.assert_(np.any(odf1 != odf2)) # test with a gtab with two shells and assert you get an error bvals[-1] = 2000 gtab = gradient_table(bvals, bvecs) csd_model = ConstrainedSphericalDeconvModel(gtab, None, sh_order=6) npt.assert_raises(ValueError, BootPmfGen, data, csd_model, hsph_updated, 6)
def test_boot_pmf(): """This tests the local model used for the bootstrapping. """ hsph_updated = HemiSphere.from_sphere(unit_octahedron) vertices = hsph_updated.vertices bvecs = vertices bvals = np.ones(len(vertices)) * 1000 bvecs = np.insert(bvecs, 0, np.array([0, 0, 0]), axis=0) bvals = np.insert(bvals, 0, 0) gtab = gradient_table(bvals, bvecs) voxel = single_tensor(gtab) data = np.tile(voxel, (3, 3, 3, 1)) point = np.array([1., 1., 1.]) tensor_model = TensorModel(gtab) boot_pmf_gen = BootPmfGen(data, model=tensor_model, sphere=hsph_updated) no_boot_pmf = boot_pmf_gen.get_pmf_no_boot(point) model_pmf = tensor_model.fit(voxel).odf(hsph_updated) npt.assert_equal(len(hsph_updated.vertices), no_boot_pmf.shape[0]) npt.assert_array_almost_equal(no_boot_pmf, model_pmf) # test model sherical harminic order different than bootstrap order with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) csd_model = ConstrainedSphericalDeconvModel(gtab, None, sh_order=6) assert_greater(len([lw for lw in w if issubclass(lw.category, UserWarning)]), 0) boot_pmf_gen_sh4 = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=4) pmf_sh4 = boot_pmf_gen_sh4.get_pmf(point) npt.assert_equal(len(hsph_updated.vertices), pmf_sh4.shape[0]) npt.assert_(np.sum(pmf_sh4.shape) > 0) boot_pmf_gen_sh8 = BootPmfGen(data, model=csd_model, sphere=hsph_updated, sh_order=8) pmf_sh8 = boot_pmf_gen_sh8.get_pmf(point) npt.assert_equal(len(hsph_updated.vertices), pmf_sh8.shape[0]) npt.assert_(np.sum(pmf_sh8.shape) > 0)
def create_unit_hemisphere(recursion_level=2): """Creates a unit sphere by subdividing a unit octahedron, returns half the sphere. Parameters ------------- recursion_level : int Level of subdivision, recursion_level=1 will return an octahedron, anything bigger will return a more subdivided sphere. The sphere will have $(4^recursion_level+2)/2$ vertices. Returns --------- HemiSphere : Half of a unit sphere. See Also ---------- create_unit_sphere, Sphere, HemiSphere """ sphere = create_unit_sphere(recursion_level) return HemiSphere.from_sphere(sphere)
def _create_mt_sim(mevals, angles, fractions, S0, SNR, half_sphere=False): _, fbvals, fbvecs = get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) gtab = gradient_table(bvals, bvecs) S, sticks = multi_tensor(gtab, mevals, S0, angles=angles, fractions=fractions, snr=SNR) sphere = get_sphere('symmetric724').subdivide(2) if half_sphere: sphere = HemiSphere.from_sphere(sphere) odf_gt = multi_tensor_odf(sphere.vertices, mevals, angles=angles, fractions=fractions) return odf_gt, sticks, sphere
def test_bdg_get_direction(): """This tests the direction found by the bootstrap direction getter. """ sphere = HemiSphere.from_sphere(unit_icosahedron.subdivide()) two_neighbors = sphere.edges[0] direction1 = sphere.vertices[two_neighbors[0]] direction2 = sphere.vertices[two_neighbors[1]] angle = np.rad2deg(direction1.dot(direction2)) point = np.zeros(3) prev_direction = direction2.copy() pmf = np.zeros([1, 1, 1, len(sphere.vertices)]) pmf[:, :, :, two_neighbors[0]] = 1 pmf_gen = SimplePmfGen(pmf) # test case in which no valid direction is found with default maxdir boot_dg = BootDirectionGetter(pmf_gen, angle / 2., sphere=sphere) npt.assert_equal(boot_dg.get_direction(point, prev_direction), 1) npt.assert_equal(direction2, prev_direction) # test case in which no valid direction is found with new max attempts boot_dg = BootDirectionGetter(pmf_gen, angle / 2., sphere=sphere, max_attempts=3) npt.assert_equal(boot_dg.get_direction(point, prev_direction), 1) npt.assert_equal(direction2, prev_direction) # test case in which a valid direction is found boot_dg = BootDirectionGetter(pmf_gen, angle * 2., sphere=sphere, max_attempts=1) npt.assert_equal(boot_dg.get_direction(point, prev_direction), 0) npt.assert_equal(direction1, prev_direction) # test invalid max_attempts parameters npt.assert_raises( ValueError, lambda: BootDirectionGetter(pmf_gen, angle * 2., sphere=sphere, max_attempts=0))
from __future__ import division from warnings import warn import numpy as np from .recspeed import local_maxima, remove_similar_vertices from ..core.onetime import auto_attr from dipy.core.sphere import unique_edges, unit_icosahedron, HemiSphere #Classes OdfModel and OdfFit are using API ReconstModel and ReconstFit from .base default_sphere = HemiSphere.from_sphere(unit_icosahedron.subdivide(3)) class DirectionFinder(object): """Abstract class for direction finding""" def __init__(self): self._config = {} def __call__(self, sphere_eval): """To be impemented by subclasses""" raise NotImplementedError() def config(self, **kwargs): """Update direction finding parameters""" for i in kwargs: if i not in self._config: warn("{} is not a known parameter".format(i)) self._config.update(kwargs) class DiscreteDirectionFinder(DirectionFinder): """Discrete Direction Finder
def test_affine_transformations(): """This tests that the input affine is properly handled by LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.4, .6, 0.]]) simple_image = np.array([[0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.]), np.array([2., 4., 0.])] expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]]), np.array([[2., 0., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]])] mask = (simple_image > 0).astype(float) tc = BinaryTissueClassifier(mask) dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 60, sphere, pmf_threshold=0.1) # TST- bad affine wrong shape bad_affine = np.eye(3) npt.assert_raises(ValueError, LocalTracking, dg, tc, seeds, bad_affine, 1.) # TST - bad affine with shearing bad_affine = np.eye(4) bad_affine[0, 1] = 1. npt.assert_raises(ValueError, LocalTracking, dg, tc, seeds, bad_affine, 1.) # TST - identity a0 = np.eye(4) # TST - affines with positive/negative offsets a1 = np.eye(4) a1[:3, 3] = [1, 2, 3] a2 = np.eye(4) a2[:3, 3] = [-2, 0, -1] # TST - affine with scaling a3 = np.eye(4) a3[0, 0] = a3[1, 1] = a3[2, 2] = 8 # TST - affine with axes inverting (negative value) a4 = np.eye(4) a4[1, 1] = a4[2, 2] = -1 # TST - combined affines a5 = a1 + a2 + a3 a5[3, 3] = 1 # TST - in vivo affine exemple # Sometimes data have affines with tiny shear components. # For example, the small_101D data-set has some of that: fdata, _, _ = get_data('small_101D') a6 = nib.load(fdata).affine for affine in [a0, a1, a2, a3, a4, a5, a6]: lin = affine[:3, :3] offset = affine[:3, 3] seeds_trans = [np.dot(lin, s) + offset for s in seeds] # We compute the voxel size to ajust the step size to one voxel voxel_size = np.mean(np.sqrt(np.dot(lin, lin).diagonal())) streamlines = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds_trans, affine=affine, step_size=voxel_size, return_all=True) # We apply the inverse affine transformation to the generated # streamlines. It should be equals to the expected streamlines # (generated with the identity affine matrix). affine_inv = np.linalg.inv(affine) lin = affine_inv[:3, :3] offset = affine_inv[:3, 3] streamlines_inv = [] for line in streamlines: streamlines_inv.append([np.dot(pts, lin) + offset for pts in line]) npt.assert_equal(len(streamlines_inv[0]), len(expected[0])) npt.assert_(np.allclose(streamlines_inv[0], expected[0], atol=0.3)) npt.assert_equal(len(streamlines_inv[1]), len(expected[1])) npt.assert_(np.allclose(streamlines_inv[1], expected[1], atol=0.3))
def test_ProbabilisticOdfWeightedTracker(): """This tests that the Probabalistic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.6, .4, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for sl in streamlines: if allclose(sl, expected[0]): path[0] = True elif allclose(sl, expected[1]): path[1] = True else: raise AssertionError() npt.assert_(all(path)) # The first path is not possible if 90 degree turns are excluded dg = ProbabilisticDirectionGetter.from_pmf(pmf, 80, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # The first path is not possible if pmf_threshold > 0.4 dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.5) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1]))
def test_maximum_deterministic_tracker(): """This tests that the Maximum Deterministic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.4, .6, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) for sl in streamlines: if not allclose(sl, expected[0]): raise AssertionError() # The first path is not possible if 90 degree turns are excluded dg = DeterministicMaximumDirectionGetter.from_pmf(pmf, 80, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # Both path are not possible if 90 degree turns are exclude and # if pmf_threshold is larger than 0.67. Streamlines should stop at # the crossing. # 0.4/0.6 < 2/3, multiplying the pmf should not change the ratio dg = DeterministicMaximumDirectionGetter.from_pmf(10*pmf, 80, sphere, pmf_threshold=0.67) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[2]))
def test_probabilistic_odf_weighted_tracker(): """This tests that the Probabalistic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.6, .4, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for sl in streamlines: if allclose(sl, expected[0]): path[0] = True elif allclose(sl, expected[1]): path[1] = True else: raise AssertionError() npt.assert_(all(path)) # The first path is not possible if 90 degree turns are excluded dg = ProbabilisticDirectionGetter.from_pmf(pmf, 80, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # The first path is not possible if pmf_threshold > 0.67 # 0.4/0.6 < 2/3, multiplying the pmf should not change the ratio dg = ProbabilisticDirectionGetter.from_pmf(10*pmf, 90, sphere, pmf_threshold=0.67) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # Test non WM seed position seeds = [[0, 0, 0], [5, 5, 5]] streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 0.2, max_cross=1, return_all=True) streamlines = Streamlines(streamlines) npt.assert_(len(streamlines[0]) == 3) # INVALIDPOINT npt.assert_(len(streamlines[1]) == 1) # OUTSIDEIMAGE # Test that all points are within the image volume seeds = seeds_from_mask(np.ones(mask.shape), density=2) streamline_generator = LocalTracking(dg, tc, seeds, np.eye(4), 0.5, return_all=True) streamlines = Streamlines(streamline_generator) for s in streamlines: npt.assert_(np.all((s + 0.5).astype(int) >= 0)) npt.assert_(np.all((s + 0.5).astype(int) < mask.shape)) # Test that the number of streamline return with return_all=True equal the # number of seeds places npt.assert_(np.array([len(streamlines) == len(seeds)])) # Test reproducibility tracking_1 = Streamlines(LocalTracking(dg, tc, seeds, np.eye(4), 0.5, random_seed=0)).data tracking_2 = Streamlines(LocalTracking(dg, tc, seeds, np.eye(4), 0.5, random_seed=0)).data npt.assert_equal(tracking_1, tracking_2)
def test_ProbabilisticOdfWeightedTracker(): sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing odf_list = [np.array([0., 0., 0.]), np.array([1., 0., 0.]), np.array([0., 1., 0.]), np.array([1., 1., 0.]), ] simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) # Make the image 4d simple_image = simple_image[..., None, None] # Simple model and fit for this image class MyModel(): def fit(self, data): return MyFit(data) class MyFit(object): def __init__(self, n): self.n = n def odf(self, sphere): return odf_list[self.n] seeds = [np.array([1.5, 1.5, .5])] * 30 model = MyModel() mask = np.ones([5, 6, 1], dtype="bool") stepper = FixedSizeStepper(1.) interpolator = NearestNeighborInterpolator(simple_image, (1, 1, 1)) # These are the only two possible paths though the simple_image pwt = ProbabilisticOdfWeightedTracker(model, interpolator, mask, stepper, 90, seeds, sphere) expected = [np.array([[0.5, 1.5, 0.5], [1.5, 1.5, 0.5], [2.5, 1.5, 0.5], [2.5, 2.5, 0.5], [2.5, 3.5, 0.5], [2.5, 4.5, 0.5], [2.5, 5.5, 0.5]]), np.array([[0.5, 1.5, 0.5], [1.5, 1.5, 0.5], [2.5, 1.5, 0.5], [3.5, 1.5, 0.5], [4.5, 1.5, 0.5]]) ] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for streamline in pwt: if allclose(streamline, expected[0]): path[0] = True elif allclose(streamline, expected[1]): path[1] = True else: raise AssertionError() assert_(all(path)) # The first path is not possible if 90 degree turns are excluded pwt = ProbabilisticOdfWeightedTracker(model, interpolator, mask, stepper, 80, seeds, sphere) for streamline in pwt: assert_(np.allclose(streamline, expected[1]))
def test_stop_conditions(): """This tests that the Local Tracker behaves as expected for the following tissue types. """ # TissueTypes.TRACKPOINT = 1 # TissueTypes.ENDPOINT = 2 # TissueTypes.INVALIDPOINT = 0 tissue = np.array([[2, 1, 1, 2, 1], [2, 2, 1, 1, 2], [1, 1, 1, 1, 1], [1, 1, 1, 2, 2], [0, 1, 1, 1, 2], [0, 1, 1, 0, 2], [1, 0, 1, 1, 1]]) tissue = tissue[None] sphere = HemiSphere.from_sphere(unit_octahedron) pmf_lookup = np.array([[0., 0., 0., ], [0., 0., 1.]]) pmf = pmf_lookup[(tissue > 0).astype("int")] # Create a seeds along x = np.array([0., 0, 0, 0, 0, 0, 0]) y = np.array([0., 1, 2, 3, 4, 5, 6]) z = np.array([1., 1, 1, 0, 1, 1, 1]) seeds = np.column_stack([x, y, z]) # Set up tracking endpoint_mask = tissue == TissueTypes.ENDPOINT invalidpoint_mask = tissue == TissueTypes.INVALIDPOINT tc = ActTissueClassifier(endpoint_mask, invalidpoint_mask) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) # valid streamlines only streamlines_generator = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=False) streamlines_not_all = iter(streamlines_generator) # all streamlines streamlines_all_generator = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=True) streamlines_all = iter(streamlines_all_generator) # Check that the first streamline stops at 0 and 3 (ENDPOINT) y = 0 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 4) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 4) # Check that the first streamline stops at 0 and 4 (ENDPOINT) y = 1 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # This streamline should be the same as above. This row does not have # ENDPOINTs, but the streamline should stop at the edge and not include # OUTSIDEIMAGE points. y = 2 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # If we seed on the edge, the first (or last) point in the streamline # should be the seed. y = 3 sl = next(streamlines_not_all) npt.assert_equal(sl[0], seeds[y]) sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) # The last 3 seeds should not produce streamlines, # INVALIDPOINT streamlines are rejected (return_all=False). npt.assert_equal(len(list(streamlines_not_all)), 0) # The last 3 seeds should produce invalid streamlines, # INVALIDPOINT streamlines are kept (return_all=True). # The streamline stops at 0 (INVALIDPOINT) and 4 (ENDPOINT) y = 4 sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # The streamline stops at 0 (INVALIDPOINT) and 4 (INVALIDPOINT) y = 5 sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 4) # The last streamline should contain only one point, the seed point, # because no valid inital direction was returned. y = 6 sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) npt.assert_equal(sl[-1], seeds[y]) npt.assert_equal(len(sl), 1)
from ..utils.six.moves import xrange import numpy as np import scipy.optimize as opt from .recspeed import local_maxima, remove_similar_vertices, search_descending from ..core.onetime import auto_attr from dipy.core.sphere import HemiSphere, Sphere from dipy.data import get_sphere #from dipy.reconst.shm import sph_harm_ind_list, smooth_pinv # Classes OdfModel and OdfFit are using API ReconstModel and ReconstFit from # .base default_sphere = HemiSphere.from_sphere(get_sphere('symmetric724')) def peak_directions_nl(sphere_eval, relative_peak_threshold=.25, min_separation_angle=45, sphere=default_sphere, xtol=1e-7): """Non Linear Direction Finder Parameters ---------- sphere_eval : callable A function which can be evaluated on a sphere. 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]
(720, 3) >>> verts, faces = get_sphere('not a sphere name') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DataError: No sphere called "not a sphere name" """ fname = SPHERE_FILES.get(name) if fname is None: raise DataError('No sphere called "%s"' % name) res = np.load(fname) # Set to native byte order to avoid errors in compiled routines for # big-endian platforms, when using these spheres. return Sphere(xyz=as_native_array(res["vertices"]), faces=as_native_array(res["faces"])) default_sphere = HemiSphere.from_sphere(get_sphere("symmetric724")) small_sphere = HemiSphere.from_sphere(get_sphere("symmetric362")) def get_data(name="small_64D"): """ provides filenames of some test datasets or other useful parametrisations Parameters ---------- name : str the filename/s of which dataset to return, one of: 'small_64D' small region of interest nifti,bvecs,bvals 64 directions 'small_101D' small region of interest nifti,bvecs,bvals 101 directions 'aniso_vox' volume with anisotropic voxel size as Nifti 'fornix' 300 tracks in Trackvis format (from Pittsburgh Brain Competition) 'gqi_vectors' the scanner wave vectors needed for a GQI acquisitions of 101 directions tested on Siemens 3T Trio