Example #1
0
def precompute_rotation_matrices(lmax, ndirs):
    """Precompute the rotation matrices to rotate the high-resolution kernels (500 directions/shell).

    Parameters
    ----------
    lmax : int
        Maximum SH order to use for the rotation phase (default : 12)
    ndirs : int
        Number of directions on the half of the sphere representing the possible orientations of the response functions (default : 32761)
    """
    if not isdir(dipy_home):
        makedirs(dipy_home)
    filename = pjoin(
        dipy_home,
        'AMICO_aux_matrices_lmax=%d_ndirs=%d.pickle' % (lmax, ndirs))
    if isfile(filename):
        return

    directions = load_directions(ndirs)

    LOG('\n-> Precomputing rotation matrices for l_max=%d:' % lmax)
    AUX = {}
    AUX['lmax'] = lmax
    AUX['ndirs'] = ndirs

    # matrix to fit the SH coefficients
    _, theta, phi = cart2sphere(grad[:, 0], grad[:, 1], grad[:, 2])
    tmp, _, _ = real_sym_sh_basis(lmax, theta, phi)
    AUX['fit'] = np.dot(np.linalg.pinv(np.dot(tmp.T, tmp)), tmp.T)

    # matrices to rotate the functions in SH space
    AUX['Ylm_rot'] = np.zeros(ndirs, dtype=np.object)
    for i in range(ndirs):
        _, theta, phi = cart2sphere(directions[i][0], directions[i][1],
                                    directions[i][2])
        tmp, _, _ = real_sym_sh_basis(lmax, theta, phi)
        AUX['Ylm_rot'][i] = tmp.reshape(-1)

    # auxiliary data to perform rotations
    AUX['const'] = np.zeros(AUX['fit'].shape[0], dtype=np.float64)
    AUX['idx_m0'] = np.zeros(AUX['fit'].shape[0], dtype=np.int32)
    i = 0
    for l in range(0, AUX['lmax'] + 1, 2):
        const = np.sqrt(4.0 * np.pi / (2.0 * l + 1.0))
        idx_m0 = (l * l + l + 2.0) / 2.0 - 1
        for m in range(-l, l + 1):
            AUX['const'][i] = const
            AUX['idx_m0'][i] = idx_m0
            i += 1

    with open(filename, 'wb+') as fid:
        pickle.dump(AUX, fid, protocol=2)

    LOG('   [ DONE ]')
Example #2
0
    def __init__(self, data, model, sphere, sh_order=None, tol=1e-2):
        if sh_order is None:
            if hasattr(model, "sh_order"):
                sh_order = model.sh_order
            else:
                sh_order = default_SH
            
        self.where_dwi = shm.lazy_index(~model.gtab.b0s_mask)
        if not isinstance(self.where_dwi, slice):
            msg = ("For optimal bootstrap tracking consider reordering the "
                   "diffusion volumes so that all the b0 volumes are at the "
                   "beginning")
            warn(msg)
        x, y, z = model.gtab.gradients[self.where_dwi].T
        r, theta, phi = cart2sphere(x, y, z)
        b_range = (r.max() - r.min()) / r.min()
        if b_range > tol:
            raise ValueError("BootOdfGen only supports single shell data")

        B, m, n = shm.real_sym_sh_basis(sh_order, theta, phi)
        H = shm.hat(B)
        R = shm.lcr_matrix(H)

        self.data = np.asarray(data, "float64")
        self.model = model
        self.sphere = sphere
        self.H = H
        self.R = R
Example #3
0
def aux_structures_resample( scheme, lmax = 12 ) :
    """Compute the auxiliary data structures to resample the kernels to the original acquisition scheme.

    Parameters
    ----------
    scheme : Scheme class
        Acquisition scheme of the acquired signal
    lmax : int
        Maximum SH order to use for the rotation phase (default : 12)

    Returns
    -------
    idx_OUT : numpy array
        Indices of the samples belonging to each shell
    Ylm_OUT : numpy array
        Operator to transform each shell from Spherical harmonics to original signal space
    """
    nSH = (lmax+1)*(lmax+2)/2
    idx_OUT = np.zeros( scheme.dwi_count, dtype=np.int32 )
    Ylm_OUT = np.zeros( (scheme.dwi_count,nSH*len(scheme.shells)), dtype=np.float32 ) # matrix from SH to real space
    idx = 0
    for s in xrange( len(scheme.shells) ) :
        nS = len( scheme.shells[s]['idx'] )
        idx_OUT[ idx:idx+nS ] = scheme.shells[s]['idx']
        _, theta, phi = cart2sphere( scheme.shells[s]['grad'][:,0], scheme.shells[s]['grad'][:,1], scheme.shells[s]['grad'][:,2] )
        tmp, _, _ = real_sym_sh_basis( lmax, theta, phi )
        Ylm_OUT[ idx:idx+nS, nSH*s:nSH*(s+1) ] = tmp
        idx += nS
    return ( idx_OUT, Ylm_OUT )
Example #4
0
    def __init__(self, *args, sh_order=6, smooth=0.006, **kwargs):
        ModelHARDI.__init__(self, *args, **kwargs)

        c = self.constvars

        x, y, z = self.data.gtab.gradients[self.data.gtab.bvals > 0].T
        r, theta, phi = cart2sphere(x, y, z)
        B, m, n = real_sym_sh_basis(sh_order, theta[:, None], phi[:, None])
        L = -n * (n + 1)
        legendre0 = lpn(sh_order, 0)[0]
        F = legendre0[n]
        self.sh_order = sh_order
        self.B = B
        self.m = m
        self.n = n
        self._fit_matrix_fw = (F * L) / (8 * np.pi)

        c['Y'] = np.ascontiguousarray(self.B)
        c['l_shm'] = c['Y'].shape[1]
        c['M'] = np.zeros(self._fit_matrix_fw.shape)
        c['M'][1:] = 1.0 / self._fit_matrix_fw[1:]
        assert (c['M'].size == c['l_shm'])
        c['YM'] = np.einsum('lk,k->lk', c['Y'], c['M'])

        logging.info("HARDI SHM setup ({l_labels} labels, {l_shm} shm; " \
                     "img: {imagedims}; lambda={lbd:.3g}) ready.".format(
                         lbd=c['lbd'],
                         l_labels=c['l_labels'],
                         l_shm=c['l_shm'],
                         imagedims="x".join(map(str,c['imagedims']))))
Example #5
0
def aux_structures_resample(scheme, lmax=12):
    """Compute the auxiliary data structures to resample the kernels to the original acquisition scheme.

    Parameters
    ----------
    scheme : Scheme class
        Acquisition scheme of the acquired signal
    lmax : int
        Maximum SH order to use for the rotation phase (default : 12)

    Returns
    -------
    idx_OUT : numpy array
        Indices of the samples belonging to each shell
    Ylm_OUT : numpy array
        Operator to transform each shell from Spherical harmonics to original signal space
    """
    nSH = (lmax + 1) * (lmax + 2) // 2
    idx_OUT = np.zeros(scheme.dwi_count, dtype=np.int32)
    Ylm_OUT = np.zeros((scheme.dwi_count, nSH * len(scheme.shells)),
                       dtype=np.float32)  # matrix from SH to real space
    idx = 0
    for s in range(len(scheme.shells)):
        nS = len(scheme.shells[s]['idx'])
        idx_OUT[idx:idx + nS] = scheme.shells[s]['idx']
        _, theta, phi = cart2sphere(scheme.shells[s]['grad'][:, 0],
                                    scheme.shells[s]['grad'][:, 1],
                                    scheme.shells[s]['grad'][:, 2])
        tmp, _, _ = real_sym_sh_basis(lmax, theta, phi)
        Ylm_OUT[idx:idx + nS, nSH * s:nSH * (s + 1)] = tmp
        idx += nS
    return (idx_OUT, Ylm_OUT)
Example #6
0
def test_real_sym_sh_basis():

    new_order = [0, 5, 4, 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, 7, 6]
    sphere = hemi_icosahedron.subdivide(2)

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore")

        basis, m, n = real_sym_sh_mrtrix(4, sphere.theta, sphere.phi)

    expected = basis[:, new_order]
    expected *= np.where(m == 0, 1., np.sqrt(2))

    with warnings.catch_warnings(record=True) as w:
        descoteaux07_basis, m, n = real_sym_sh_basis(4, sphere.theta,
                                                     sphere.phi)

    npt.assert_equal(len(w), 2)
    npt.assert_(issubclass(w[0].category, DeprecationWarning))
    npt.assert_(
        "dipy.reconst.shm.real_sym_sh_basis is deprecated, Please use "
        "dipy.reconst.shm.real_sh_descoteaux instead" in str(w[0].message))
    npt.assert_(issubclass(w[1].category, PendingDeprecationWarning))
    npt.assert_(descoteaux07_legacy_msg in str(w[1].message))

    assert_array_almost_equal(descoteaux07_basis, expected)
Example #7
0
def test_default_lambda_csdmodel():
    """We check that the default value of lambda is the expected value with
    the symmetric362 sphere. This value has empirically been found to work well
    and changes to this default value should be discussed with the dipy team.
    """
    expected_lambda = {4: 27.5230088, 8: 82.5713865, 16: 216.0843135}
    expected_warnings = {4: 0, 8: 0, 16: 1}
    sphere = default_sphere

    # Create gradient table
    _, fbvals, fbvecs = get_fnames('small_64D')
    bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs)
    gtab = gradient_table(bvals, bvecs)

    # Some response function
    response = (np.array([0.0015, 0.0003, 0.0003]), 100)

    for sh_order, expected, e_warn in zip(expected_lambda.keys(),
                                          expected_lambda.values(),
                                          expected_warnings.values()):
        with warnings.catch_warnings(record=True) as w:
            model_full = ConstrainedSphericalDeconvModel(gtab, response,
                                                         sh_order=sh_order,
                                                         reg_sphere=sphere)
            npt.assert_equal(len(w), e_warn)
            if e_warn:
                npt.assert_(issubclass(w[0].category, UserWarning))
                npt.assert_("Number of parameters required " in str(w[0].
                                                                    message))

        B_reg, _, _ = real_sym_sh_basis(sh_order, sphere.theta, sphere.phi)
        npt.assert_array_almost_equal(model_full.B_reg, expected * B_reg)
Example #8
0
def precompute_rotation_matrices(lmax=12):
    """Precompute the rotation matrices to rotate the high-resolution kernels (500 directions/shell).

    Parameters
    ----------
    lmax : int
        Maximum SH order to use for the rotation phase (default : 12)
    """
    if not isdir(dipy_home):
        makedirs(dipy_home)
    filename = pjoin(dipy_home, 'AMICO_aux_matrices_lmax=%d.pickle' % lmax)
    if isfile(filename):
        return

    print('\n-> Precomputing rotation matrices for l_max=%d:' % lmax)
    AUX = {}
    AUX['lmax'] = lmax

    # matrix to fit the SH coefficients
    _, theta, phi = cart2sphere(grad[:, 0], grad[:, 1], grad[:, 2])
    tmp, _, _ = real_sym_sh_basis(lmax, theta, phi)
    AUX['fit'] = np.dot(np.linalg.pinv(np.dot(tmp.T, tmp)), tmp.T)

    # matrices to rotate the functions in SH space
    AUX['Ylm_rot'] = np.zeros((181, 181), dtype=np.object)
    for ox in range(181):
        for oy in range(181):
            tmp, _, _ = real_sym_sh_basis(lmax, ox / 180.0 * np.pi,
                                          oy / 180.0 * np.pi)
            AUX['Ylm_rot'][ox, oy] = tmp.reshape(-1)

    # auxiliary data to perform rotations
    AUX['const'] = np.zeros(AUX['fit'].shape[0], dtype=np.float64)
    AUX['idx_m0'] = np.zeros(AUX['fit'].shape[0], dtype=np.int32)
    i = 0
    for l in range(0, AUX['lmax'] + 1, 2):
        const = np.sqrt(4.0 * np.pi / (2.0 * l + 1.0))
        idx_m0 = (l * l + l + 2.0) / 2.0 - 1
        for m in range(-l, l + 1):
            AUX['const'][i] = const
            AUX['idx_m0'][i] = idx_m0
            i += 1

    with open(filename, 'wb+') as fid:
        pickle.dump(AUX, fid, protocol=2)

    print('   [ DONE ]')
Example #9
0
def precompute_rotation_matrices( lmax = 12 ) :
    """Precompute the rotation matrices to rotate the high-resolution kernels (500 directions/shell).

    Parameters
    ----------
    lmax : int
        Maximum SH order to use for the rotation phase (default : 12)
    """
    if not isdir(dipy_home) :
        makedirs(dipy_home)
    filename = pjoin( dipy_home, 'AMICO_aux_matrices_lmax=%d.pickle'%lmax )
    if isfile( filename ) :
        return

    print '\n-> Precomputing rotation matrices for l_max=%d:' % lmax
    AUX = {}
    AUX['lmax'] = lmax

    # matrix to fit the SH coefficients
    _, theta, phi = cart2sphere( grad[:,0], grad[:,1], grad[:,2] )
    tmp, _, _ = real_sym_sh_basis( lmax, theta, phi )
    AUX['fit'] = np.dot( np.linalg.pinv( np.dot(tmp.T,tmp) ), tmp.T )

    # matrices to rotate the functions in SH space
    AUX['Ylm_rot'] = np.zeros( (181,181), dtype=np.object )
    for ox in xrange(181) :
        for oy in xrange(181) :
            tmp, _, _ = real_sym_sh_basis( lmax, ox/180.0*np.pi, oy/180.0*np.pi )
            AUX['Ylm_rot'][ox,oy] = tmp.reshape(-1)

    # auxiliary data to perform rotations
    AUX['const'] = np.zeros( AUX['fit'].shape[0], dtype=np.float64 )
    AUX['idx_m0'] = np.zeros( AUX['fit'].shape[0], dtype=np.int32 )
    i = 0
    for l in xrange(0,AUX['lmax']+1,2) :
        const  = np.sqrt(4.0*np.pi/(2.0*l+1.0))
        idx_m0 = (l*l + l + 2.0)/2.0 - 1
        for m in xrange(-l,l+1) :
            AUX['const'][i]  = const
            AUX['idx_m0'][i] = idx_m0
            i += 1

    with open( filename, 'wb+' ) as fid :
        cPickle.dump( AUX, fid, protocol=2 )

    print '   [ DONE ]'
Example #10
0
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="")
Example #11
0
def test_real_sym_sh_basis():
    # This test should do for now
    # The mrtrix basis should be the same as re-ordering and re-scaling the
    # fibernav basis
    new_order = [0, 5, 4, 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, 7, 6]
    sphere = hemi_icosahedron.subdivide(2)
    basis, m, n = real_sym_sh_mrtrix(4, sphere.theta, sphere.phi)
    expected = basis[:, new_order]
    expected *= np.where(m == 0, 1., np.sqrt(2))

    fibernav_basis, m, n = real_sym_sh_basis(4, sphere.theta, sphere.phi)
    assert_array_almost_equal(fibernav_basis, expected)
Example #12
0
def test_real_sym_sh_basis():
    # This test should do for now
    # The tournier07 basis should be the same as re-ordering and re-scaling the
    # descoteaux07 basis
    new_order = [0, 5, 4, 3, 2, 1, 14, 13, 12, 11, 10, 9, 8, 7, 6]
    sphere = hemi_icosahedron.subdivide(2)
    basis, m, n = real_sym_sh_mrtrix(4, sphere.theta, sphere.phi)
    expected = basis[:, new_order]
    expected *= np.where(m == 0, 1., np.sqrt(2))

    descoteaux07_basis, m, n = real_sym_sh_basis(4, sphere.theta, sphere.phi)
    assert_array_almost_equal(descoteaux07_basis, expected)
def test_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, 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
    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, response, sh_order=6)
    npt.assert_raises(ValueError, BootPmfGen, data, csd_model, hsph_updated, 6)
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)
Example #15
0
    def predict(self, sh_coeff, gtab=None, S0=1):
        """Compute a signal prediction given spherical harmonic coefficients
        and (optionally) a response function for the provided GradientTable
        class instance.

        Parameters
        ----------
        sh_coeff : ndarray
            The spherical harmonic representation of the FOD from which to make
            the signal prediction.
        gtab : GradientTable
            The gradients for which the signal will be predicted. Use the
            model's gradient table by default.
        S0 : ndarray or float
            The non diffusion-weighted signal value.

        Returns
        -------
        pred_sig : ndarray
            The predicted signal.

        """
        if gtab is None or gtab is self.gtab:
            SH_basis = self.B_dwi
            gtab = self.gtab
        else:
            x, y, z = gtab.gradients[~gtab.b0s_mask].T
            r, theta, phi = cart2sphere(x, y, z)
            SH_basis, m, n = real_sym_sh_basis(self.sh_order, theta, phi)

        # Because R is diagonal, the matrix multiply is written as a multiply
        predict_matrix = SH_basis * self.R.diagonal()
        S0 = np.asarray(S0)[..., None]
        scaling = S0 / self.response_scaling

        # This is the key operation: convolve and multiply by S0:
        pre_pred_sig = scaling * np.dot(predict_matrix, sh_coeff)

        # Now put everything in its right place:
        pred_sig = np.zeros(pre_pred_sig.shape[:-1] + (gtab.bvals.shape[0],))
        pred_sig[..., ~gtab.b0s_mask] = pre_pred_sig
        pred_sig[..., gtab.b0s_mask] = S0

        return pred_sig
Example #16
0
def test_default_lambda_csdmodel():
    """We check that the default value of lambda is the expected value with
    the symmetric362 sphere. This value has empirically been found to work well
    and changes to this default value should be discussed with the dipy team.
    """
    sphere = default_sphere

    # Create gradient table
    _, fbvals, fbvecs = get_fnames('small_64D')
    bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs)
    gtab = gradient_table(bvals, bvecs)

    # Some response function
    response = (np.array([0.0015, 0.0003, 0.0003]), 100)

    for sh_order, expected in expected_lambda.items():
        model_full = ConstrainedSphericalDeconvModel(gtab, response,
                                                     sh_order=sh_order,
                                                     reg_sphere=sphere)
        B_reg, _, _ = real_sym_sh_basis(sh_order, sphere.theta, sphere.phi)
        npt.assert_array_almost_equal(model_full.B_reg, expected * B_reg)
Example #17
0
def test_default_lambda_csdmodel():
    """We check that the default value of lambda is the expected value with
    the symmetric362 sphere. This value has empirically been found to work well
    and changes to this default value should be discusses with the dipy team.

    """
    sphere = get_sphere("symmetric362")

    # Create gradient table
    _, fbvals, fbvecs = get_data("small_64D")
    bvals = np.load(fbvals)
    bvecs = np.load(fbvecs)
    gtab = gradient_table(bvals, bvecs)

    # Some response function
    response = (np.array([0.0015, 0.0003, 0.0003]), 100)

    for sh_order, expected in expected_lambda.items():
        model_full = ConstrainedSphericalDeconvModel(gtab, response, sh_order=sh_order, reg_sphere=sphere)
        B_reg, _, _ = real_sym_sh_basis(sh_order, sphere.theta, sphere.phi)
        npt.assert_array_almost_equal(model_full.B_reg, expected * B_reg)
Example #18
0
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
Example #19
0
        #Workaround: don't rotate bvec
        bvecRotated = bvecs[index]
        #print bvecs[index]
        #print bvecRotated

        if bvals[index] < 100:
            attenuatedBrainData = segmentedBrainData
        else:
            #Convert bvecs to angles
            x = bvecRotated[0]
            y = bvecRotated[1]
            z = bvecRotated[2]
            r, theta, phi = shm.cart2sphere(x, y, z)

            #Make design matrix
            B, m, n = shm.real_sym_sh_basis(order, theta, phi)

            #Get attenuated data
            print('Attenuating volume ' + str(index))
            if bvals[index] < 1500:
                attenuatedBrainData = pl.attenuateImageSphericalHarmonics(
                    segmentedBrainData, B, coefficientsb1000, bvals[index],
                    1000)
            elif bvals[index] > 1500 and bvals[index] < 2500:
                attenuatedBrainData = pl.attenuateImageSphericalHarmonics(
                    segmentedBrainData, B, coefficientsb2000, bvals[index],
                    2000)

        attenuatedBrainNii = nib.Nifti1Image(attenuatedBrainData,
                                             segmentedBrain.get_affine(),
                                             segmentedBrain.get_header())
Example #20
0
    def __init__(self,
                 gtab,
                 response,
                 reg_sphere=default_sphere,
                 sh_order=8,
                 iso=2):
        r"""
        Multi-Shell Multi-Tissue Constrained Spherical Deconvolution
        (MSMT-CSD) [1]_. This method extends the CSD model proposed in [2]_ by
        the estimation of multiple response functions as a function of multiple
        b-values and multiple tissue types.

        Spherical deconvolution computes a fiber orientation distribution
        (FOD), also called fiber ODF (fODF) [2]_. The fODF is derived from
        different tissue types and thus overcomes the overestimation of WM in
        GM and CSF areas.

        The response function is based on the different tissue types
        and is provided as input to the MultiShellDeconvModel.
        It will be used as deconvolution kernel, as described in [2]_.

        Parameters
        ----------
        gtab : GradientTable
        response : ndarray or MultiShellResponse object
            Pre-computed multi-shell fiber response function in the form of a
            MultiShellResponse object, or simple response function as a ndarray.
            The later must be of shape (3, len(bvals)-1, 4), because it will be
            converted into a MultiShellResponse object via the
            `multi_shell_fiber_response` method (important note: the function
            `unique_bvals_tolerance` is used here to select unique bvalues from
            gtab as input). Each column (3,) has two elements. The first is the
            eigen-values as a (3,) ndarray and the second is the signal value
            for the response function without diffusion weighting (S0). Note
            that in order to use more than three compartments, one must create
            a MultiShellResponse object on the side.
        reg_sphere : Sphere (optional)
            sphere used to build the regularization B matrix.
            Default: 'symmetric362'.
        sh_order : int (optional)
            maximal spherical harmonics order. Default: 8
        iso: int (optional)
            Number of tissue compartments for running the MSMT-CSD. Minimum
            number of compartments required is 2.
            Default: 2

        References
        ----------
        .. [1] Jeurissen, B., et al. NeuroImage 2014. Multi-tissue constrained
               spherical deconvolution for improved analysis of multi-shell
               diffusion MRI data
        .. [2] Tournier, J.D., et al. NeuroImage 2007. Robust determination of
               the fibre orientation distribution in diffusion MRI:
               Non-negativity constrained super-resolved spherical
               deconvolution
        .. [3] Tournier, J.D, et al. Imaging Systems and Technology
               2012. MRtrix: Diffusion Tractography in Crossing Fiber Regions
        """
        if not iso >= 2:
            msg = ("Multi-tissue CSD requires at least 2 tissue compartments")
            raise ValueError(msg)

        super(MultiShellDeconvModel, self).__init__(gtab)

        if not isinstance(response, MultiShellResponse):
            bvals = unique_bvals_tolerance(gtab.bvals, tol=20)
            if iso > 2:
                msg = """Too many compartments for this kind of response
                input. It must be two tissue compartments."""
                raise ValueError(msg)
            if response.shape != (3, len(bvals) - 1, 4):
                msg = """Response must be of shape (3, len(bvals)-1, 4) or be a
                MultiShellResponse object."""
                raise ValueError(msg)
            response = multi_shell_fiber_response(sh_order,
                                                  bvals=bvals,
                                                  wm_rf=response[0],
                                                  gm_rf=response[1],
                                                  csf_rf=response[2])

        B, m, n = multi_tissue_basis(gtab, sh_order, iso)

        delta = _basic_delta(response.iso, response.m, response.n, 0., 0.)
        self.delta = delta
        multiplier_matrix = _inflate_response(response, gtab, n, delta)

        r, theta, phi = geo.cart2sphere(*reg_sphere.vertices.T)
        odf_reg, _, _ = shm.real_sym_sh_basis(sh_order, theta, phi)
        reg = np.zeros([i + iso for i in odf_reg.shape])
        reg[:iso, :iso] = np.eye(iso)
        reg[iso:, iso:] = odf_reg

        X = B * multiplier_matrix

        self.fitter = QpFitter(X, reg)
        self.sh_order = sh_order
        self._X = X
        self.sphere = reg_sphere
        self.gtab = gtab
        self.B_dwi = B
        self.m = m
        self.n = n
        self.response = response
Example #21
0
# Setup direction getter args
print('Bootstrap direction getter')
#boot_dg = BootDirectionGetter.from_data(data, model, max_angle=60., sphere=small_sphere)
b0s_mask = gtab.b0s_mask
dwi_mask = ~b0s_mask

# get fit_matrix from model
fit_matrix = model._fit_matrix
delta_b, delta_q = fit_matrix

# setup sampling matrix
sphere = small_sphere
theta = sphere.theta
phi = sphere.phi
sampling_matrix, _, _ = shm.real_sym_sh_basis(sh_order, theta, phi)

## from BootPmfGen __init__
# setup H and R matrices
# TODO: figure out how to get H, R matrices from direction getter object
x, y, z = model.gtab.gradients[dwi_mask].T
r, theta, phi = shm.cart2sphere(x, y, z)
B, _, _ = shm.real_sym_sh_basis(sh_order, theta, phi)
H = shm.hat(B)
R = shm.lcr_matrix(H)

# create floating point copy of data
dataf = np.asarray(data, dtype=float)

print('streamline gen')
global_chunk_size = args.chunk_size * args.ngpus
Example #22
0
def csd_predict(sh_coeff, gtab, response=None, S0=1, R=None):
    """
    Compute a signal prediction given spherical harmonic coefficients and
    (optionally) a response function for the provided GradientTable class
    instance

    Parameters
    ----------
    sh_coeff : ndarray
       Spherical harmonic coefficients

    gtab : GradientTable class instance

    response : tuple
        A tuple with two elements. The first is the eigen-values as an (3,)
        ndarray and the second is the signal value for the response
        function without diffusion weighting.
        Default: (np.array([0.0015, 0.0003, 0.0003]), 1)

    S0 : ndarray or float
        The non diffusion-weighted signal value.

    R : ndarray
        Optionally, provide an R matrix. If not provided, calculated from the
        gtab, response function, etc.

    Returns
    -------
    pred_sig : ndarray
        The signal predicted from the provided SH coefficients for a
        measurement with the provided GradientTable. The last dimension of the
        resulting array is the same as the number of bvals/bvecs in the
        GradientTable. The first dimensions have shape: `sh_coeff.shape[:-1]`.
    """
    n_coeff = sh_coeff.shape[-1]
    sh_order = order_from_ncoef(n_coeff)
    x, y, z = gtab.gradients[~gtab.b0s_mask].T
    r, theta, phi = cart2sphere(x, y, z)
    SH_basis, m, n = real_sym_sh_basis(sh_order, theta, phi)
    if R is None:
        # for the gradient sphere
        B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])

        if response is None:
            response = (np.array([0.0015, 0.0003, 0.0003]), 1)
        else:
            response = response

        S_r = estimate_response(gtab, response[0], response[1])
        r_sh = np.linalg.lstsq(B_dwi, S_r[~gtab.b0s_mask])[0]
        r_rh = sh_to_rh(r_sh, m, n)
        R = forward_sdeconv_mat(r_rh, n)

    predict_matrix = np.dot(SH_basis, R)

    if np.iterable(S0):
        # If it's an array, we need to give it one more dimension:
        S0 = S0[..., None]

    # This is the key operation: convolve and multiply by S0:
    pre_pred_sig = S0 * np.dot(predict_matrix, sh_coeff)

    # Now put everything in its right place:
    pred_sig = np.zeros(pre_pred_sig.shape[:-1] + (gtab.bvals.shape[0],))
    pred_sig[..., ~gtab.b0s_mask] = pre_pred_sig
    pred_sig[..., gtab.b0s_mask] = S0

    return pred_sig
Example #23
0
    def __init__(self, gtab, response, reg_sphere=default_sphere, iso=2):
        r"""
        Multi-Shell Multi-Tissue Constrained Spherical Deconvolution
        (MSMT-CSD) [1]_. This method extends the CSD model proposed in [2]_ by
        the estimation of multiple response functions as a function of multiple
        b-values and multiple tissue types.

        Spherical deconvolution computes a fiber orientation distribution
        (FOD), also called fiber ODF (fODF) [2]_. The fODF is derived from
        different tissue types and thus overcomes the overestimation of WM in
        GM and CSF areas.

        The response function is based on the different tissue types
        and is provided as input to the MultiShellDeconvModel.
        It will be used as deconvolution kernel, as described in [2]_.

        Parameters
        ----------
        gtab : GradientTable
        response : tuple or AxSymShResponse object
            A tuple with two elements. The first is the eigen-values as an (3,)
            ndarray and the second is the signal value for the response
            function without diffusion weighting.  This is to be able to
            generate a single fiber synthetic signal. The response function
            will be used as deconvolution kernel ([1]_)
        reg_sphere : Sphere (optional)
            sphere used to build the regularization B matrix.
            Default: 'symmetric362'.
        iso: int (optional)
            Number of tissue compartments for running the MSMT-CSD. Minimum
            number of compartments required is 2.
            Default: 2

        References
        ----------
        .. [1] Jeurissen, B., et al. NeuroImage 2014. Multi-tissue constrained
               spherical deconvolution for improved analysis of multi-shell
               diffusion MRI data
        .. [2] Tournier, J.D., et al. NeuroImage 2007. Robust determination of
               the fibre orientation distribution in diffusion MRI:
               Non-negativity constrained super-resolved spherical
               deconvolution
        .. [3] Tournier, J.D, et al. Imaging Systems and Technology
               2012. MRtrix: Diffusion Tractography in Crossing Fiber Regions
        """
        if not iso >= 2:
            msg = ("Multi-tissue CSD requires at least 2 tissue compartments")
            raise ValueError(msg)

        sh_order = response.sh_order
        super(MultiShellDeconvModel, self).__init__(gtab)
        B, m, n = multi_tissue_basis(gtab, sh_order, iso)

        delta = _basic_delta(response.iso, response.m, response.n, 0., 0.)
        self.delta = delta
        multiplier_matrix = _inflate_response(response, gtab, n, delta)

        r, theta, phi = geo.cart2sphere(*reg_sphere.vertices.T)
        odf_reg, _, _ = shm.real_sym_sh_basis(sh_order, theta, phi)
        reg = np.zeros([i + iso for i in odf_reg.shape])
        reg[:iso, :iso] = np.eye(iso)
        reg[iso:, iso:] = odf_reg

        X = B * multiplier_matrix

        self.fitter = QpFitter(X, reg)
        self.sh_order = sh_order
        self._X = X
        self.sphere = reg_sphere
        self.response = response
        self.B_dwi = B
        self.m = m
        self.n = n
    # Normalise data
    dataNormalised = shm.normalize_data(data, gtab.b0s_mask)
    # dataNormalisedNii = nib.Nifti1Image(dataNormalised, img.get_affine(),
    # img.get_header())
    # dataNormalisedNii.to_filename('dataNormalised.nii.gz')

    # Convert bvecs to angles
    where_dwis = 1 - gtab.b0s_mask
    x = gtab.gradients[where_dwis == True, 0]
    y = gtab.gradients[where_dwis == True, 1]
    z = gtab.gradients[where_dwis == True, 2]
    r, theta, phi = shm.cart2sphere(x, y, z)

    # Make design matrix
    B, m, n = shm.real_sym_sh_basis(order, theta[:, None], phi[:, None])
    Binverse = shm.pinv(B)

    # Make matrix to hold coefficients
    dataSize = data.shape
    coefficientArray = np.zeros(
        (dataSize[0], dataSize[1], dataSize[2], len(B[1, :])))

    # Get coefficients
    for i in range(0, dataSize[0]):
        for j in range(0, dataSize[1]):
            for k in range(0, dataSize[2]):
                if maskData[i, j, k] != 0:
                    dataColumn = dataNormalised[i, j, k, where_dwis == True]
                    coefficientArray[i, j, k] = np.dot(Binverse, dataColumn)