예제 #1
0
def sim_response(sh_order, bvals, evals=evals_d, csf_md=csf_md, gm_md=gm_md):
    bvals = np.array(bvals, copy=True)
    evecs = np.zeros((3, 3))
    z = np.array([0, 0, 1.])
    evecs[:, 0] = z
    evecs[:2, 1:] = np.eye(2)

    n = np.arange(0, sh_order + 1, 2)
    m = np.zeros_like(n)

    big_sphere = default_sphere.subdivide()
    theta, phi = big_sphere.theta, big_sphere.phi

    B = shm.real_sph_harm(m, n, theta[:, None], phi[:, None])
    A = shm.real_sph_harm(0, 0, 0, 0)

    response = np.empty([len(bvals), len(n) + 2])
    for i, bvalue in enumerate(bvals):
        gtab = GradientTable(big_sphere.vertices * bvalue)
        wm_response = single_tensor(gtab, 1., evals, evecs, snr=None)
        response[i, 2:] = np.linalg.lstsq(B, wm_response)[0]

        response[i, 0] = np.exp(-bvalue * csf_md) / A
        response[i, 1] = np.exp(-bvalue * gm_md) / A

    return MultiShellResponse(response, sh_order, bvals)
예제 #2
0
def multi_shell_fiber_response(sh_order,
                               bvals,
                               evals,
                               csf_md,
                               gm_md,
                               sphere=None):
    """Fiber response function estimation for multi-shell data.

    Parameters
    ----------
    sh_order : int
         Maximum spherical harmonics order.
    bvals : ndarray
        Array containing the b-values.
    evals : (3,) ndarray
        Eigenvalues of the diffusion tensor.
    csf_md : float
        CSF tissue mean diffusivity value.
    gm_md : float
        GM tissue mean diffusivity value.
    sphere : `dipy.core.Sphere` instance, optional
        Sphere where the signal will be evaluated.

    Returns
    -------
    MultiShellResponse
        MultiShellResponse object.
    """

    bvals = np.array(bvals, copy=True)
    evecs = np.zeros((3, 3))
    z = np.array([0, 0, 1.])
    evecs[:, 0] = z
    evecs[:2, 1:] = np.eye(2)

    n = np.arange(0, sh_order + 1, 2)
    m = np.zeros_like(n)

    if sphere is None:
        sphere = default_sphere

    big_sphere = sphere.subdivide()
    theta, phi = big_sphere.theta, big_sphere.phi

    B = shm.real_sph_harm(m, n, theta[:, None], phi[:, None])
    A = shm.real_sph_harm(0, 0, 0, 0)

    response = np.empty([len(bvals), len(n) + 2])
    for i, bvalue in enumerate(bvals):
        gtab = GradientTable(big_sphere.vertices * bvalue)
        wm_response = single_tensor(gtab, 1., evals, evecs, snr=None)
        response[i, 2:] = np.linalg.lstsq(B, wm_response)[0]

        response[i, 0] = np.exp(-bvalue * csf_md) / A
        response[i, 1] = np.exp(-bvalue * gm_md) / A

    return MultiShellResponse(response, sh_order, bvals)
예제 #3
0
def shore_psi_matrix(radial_order, mu, rgrad):
    """Computes the K matrix without optimization.
    """

    r, theta, phi = cart2sphere(rgrad[:, 0], rgrad[:, 1], rgrad[:, 2])
    theta[np.isnan(theta)] = 0

    ind_mat = shore_index_matrix(radial_order)

    n_elem = ind_mat.shape[0]
    n_rgrad = rgrad.shape[0]
    K = np.zeros((n_rgrad, n_elem))

    counter = 0
    for n in range(0, radial_order + 1, 2):
        for j in range(1, 2 + n / 2):
            l = n + 2 - 2 * j
            const = (-1) ** (j - 1) * \
                    (np.sqrt(2) * np.pi * mu ** 3) ** (-1) *\
                    (r ** 2 / (2 * mu ** 2)) ** (l / 2) *\
                np.exp(- r ** 2 / (2 * mu ** 2)) *\
                genlaguerre(j - 1, l + 0.5)(r ** 2 / mu ** 2)
            for m in range(-l, l + 1):
                K[:, counter] = const * real_sph_harm(m, l, theta, phi)
                counter += 1

    return K
예제 #4
0
def test_mcsd_model_delta():
    sh_order = 8
    gtab = get_3shell_gtab()
    shells = np.unique(gtab.bvals // 100.) * 100.
    response = sim_response(sh_order, shells, evals_d)
    model = MultiShellDeconvModel(gtab, response)
    iso = response.iso

    theta, phi = default_sphere.theta, default_sphere.phi
    B = shm.real_sph_harm(response.m, response.n, theta[:, None], phi[:, None])

    wm_delta = model.delta.copy()
    # set isotropic components to zero
    wm_delta[:iso] = 0.
    wm_delta = _expand(model.m, iso, wm_delta)

    for i, s in enumerate(shells):
        g = GradientTable(default_sphere.vertices * s)
        signal = model.predict(wm_delta, g)
        expected = np.dot(response.response[i, iso:], B.T)
        npt.assert_array_almost_equal(signal, expected)

    signal = model.predict(wm_delta, gtab)
    fit = model.fit(signal)
    m = model.m
    npt.assert_array_almost_equal(fit.shm_coeff[m != 0], 0., 2)
예제 #5
0
def shore_phi_matrix(radial_order, mu, q):
    '''Computed the Q matrix completely without separation into
    mu-depenent / -independent. See shore_Q_mu_independent for help.
    '''
    qval, theta, phi = cart2sphere(q[:, 0], q[:, 1], q[:, 2])
    theta[np.isnan(theta)] = 0

    ind_mat = shore_index_matrix(radial_order)

    n_elem = ind_mat.shape[0]
    n_qgrad = q.shape[0]
    M = np.zeros((n_qgrad, n_elem))

    counter = 0
    for n in range(0, radial_order + 1, 2):
        for j in range(1, 2 + n / 2):
            l = n + 2 - 2 * j
            const = (-1) ** (l / 2) * np.sqrt(4.0 * np.pi) *\
                (2 * np.pi ** 2 * mu ** 2 * qval ** 2) ** (l / 2) *\
                np.exp(-2 * np.pi ** 2 * mu ** 2 * qval ** 2) *\
                genlaguerre(j - 1, l + 0.5)(4 * np.pi ** 2 * mu ** 2 *
                                            qval ** 2)
            for m in range(-l, l + 1):
                M[:, counter] = const * real_sph_harm(m, l, theta, phi)
                counter += 1
    return M
예제 #6
0
파일: shore.py 프로젝트: ssheybani/dipy
def shore_matrix_odf(radial_order, zeta, sphere_vertices):
    r"""Compute the SHORE ODF matrix [1]_"

    Parameters
    ----------
    radial_order : unsigned int,
        an even integer that represent the order of the basis
    zeta : unsigned int,
        scale factor
    sphere_vertices : array, shape (N,3)
        vertices of the odf sphere

    References
    ----------
    .. [1] Merlet S. et. al, "Continuous diffusion signal, EAP and
    ODF estimation via Compressive Sensing in diffusion MRI", Medical
    Image Analysis, 2013.
    """

    r, theta, phi = cart2sphere(sphere_vertices[:, 0], sphere_vertices[:, 1],
                                sphere_vertices[:, 2])
    theta[np.isnan(theta)] = 0
    F = radial_order / 2
    n_c = int(np.round(1 / 6.0 * (F + 1) * (F + 2) * (4 * F + 3)))
    upsilon = np.zeros((len(sphere_vertices), n_c))
    counter = 0
    for l in range(0, radial_order + 1, 2):
        for n in range(l, int((radial_order + l) / 2) + 1):
            for m in range(-l, l + 1):
                upsilon[:, counter] = (-1) ** (n - l / 2.0) * _kappa_odf(zeta, n, l) * \
                    hyp2f1(l - n, l / 2.0 + 1.5, l + 1.5, 2.0) * \
                    real_sph_harm(m, l, theta, phi)
                counter += 1

    return upsilon
예제 #7
0
    def rtap(self, direction=None):
        """ Recovers the directional Return To Axis Probability (RTAP) [1,2].
        Its value is only valid along the direction of the fiber. If no
        direction is given the biggest eigenvector of the tensor is used.
        Its value is defined as:
        ..math::
          :nowrap:
              \begin{equation}
              RTAP=\int_{\mathbb{R}}P(\textbf{r}_{\parallel})
              d\textbf{r}_{\parallel}=\frac{1}{(2\pi) u_0^2}
              \sum_{N=0}^{N_{max}}\sum_{\{j,l,m\}}\textbf{c}_{\{j,l,m\}}
              (-1)^{j-1}2^{-l/2}\kappa(j,l)Y_l^m(\textbf{u}_{fiber})
        The vector is precomputed to speed up the computation.
        """
        if direction is None:
            if self.R is None:
                warn('Tensor linearity too low to use main tensor eigenvalue '
                     'as fiber direction. Returning 0.')
                return 0.
            else:
                direction = np.array(self.R[:, 0], ndmin=2)
                r, theta, phi = cart2sphere(direction[:, 0], direction[:, 1],
                                            direction[:, 2])
        else:
            r, theta, phi = cart2sphere(direction[0], direction[1],
                                        direction[2])

        inx = self.model.ind_mat
        rtap_vec = self.model.rtap_vec
        rtap = self._shore_coef * (1 / self.mu ** 2) *\
            rtap_vec * real_sph_harm(inx[:, 2], inx[:, 1], theta, phi)
        return rtap.sum()
예제 #8
0
def multi_tissue_basis(gtab, sh_order, iso_comp):
    """
    Builds a basis for multi-shell multi-tissue CSD model.

    Parameters
    ----------
    gtab : GradientTable
    sh_order : int
    iso_comp: int
        Number of tissue compartments for running the MSMT-CSD. Minimum
        number of compartments required is 2.

    Returns
    -------
    B : ndarray
        Matrix of the spherical harmonics model used to fit the data
    m : int ``|m| <= n``
        The order of the harmonic.
    n : int ``>= 0``
        The degree of the harmonic.
    """
    if iso_comp < 2:
        msg = ("Multi-tissue CSD requires at least 2 tissue compartments")
        raise ValueError(msg)
    r, theta, phi = geo.cart2sphere(*gtab.gradients.T)
    m, n = shm.sph_harm_ind_list(sh_order)
    B = shm.real_sph_harm(m, n, theta[:, None], phi[:, None])
    B[np.ix_(gtab.b0s_mask, n > 0)] = 0.

    iso = np.empty([B.shape[0], iso_comp])
    iso[:] = SH_CONST

    B = np.concatenate([iso, B], axis=1)
    return B, m, n
예제 #9
0
    def __init__(self, gtab, response, sh_order, lambda_=1, tau=0.1):
        super(NumberSmallfODF, self).__init__()

        m, n = sph_harm_ind_list(sh_order)

        # x, y, z = gtab.gradients[~gtab.b0s_mask].T
        # r, theta, phi = cart2sphere(x, y, z)
        # self.B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])
        self.B_dwi = shm.get_B_matrix(gtab, sh_order)

        self.sphere = get_sphere('symmetric362')

        r, theta, phi = cart2sphere(self.sphere.x, self.sphere.y,
                                    self.sphere.z)
        self.B_reg = real_sph_harm(m, n, theta[:, None], phi[:, None])

        S_r = shm.estimate_response(gtab, response[0:3], response[3])
        r_sh = np.linalg.lstsq(self.B_dwi, S_r[~gtab.b0s_mask], rcond=-1)[0]
        n_response = n
        m_response = m
        r_rh = sh_to_rh(r_sh, m_response, n_response)
        R = forward_sdeconv_mat(r_rh, n)

        # scale lambda_ to account for differences in the number of
        # SH coefficients and number of mapped directions
        # This is exactly what is done in [4]_
        lambda_ = (lambda_ * R.shape[0] * r_rh[0] /
                   (np.sqrt(self.B_reg.shape[0]) * np.sqrt(362.)))
        self.B_reg = self.B_reg * lambda_
        self.B_reg = nn.Parameter(torch.FloatTensor(self.B_reg),
                                  requires_grad=False)

        self.tau = tau
예제 #10
0
파일: shore.py 프로젝트: deflavio/dipy
def shore_matrix_odf(radial_order, zeta, sphere_vertices):
    r"""Compute the SHORE ODF matrix [1]_"

    Parameters
    ----------
    radial_order : unsigned int,
        an even integer that represent the order of the basis
    zeta : unsigned int,
        scale factor
    sphere_vertices : array, shape (N,3)
        vertices of the odf sphere

    References
    ----------
    .. [1] Merlet S. et. al, "Continuous diffusion signal, EAP and
    ODF estimation via Compressive Sensing in diffusion MRI", Medical
    Image Analysis, 2013.
    """

    r, theta, phi = cart2sphere(sphere_vertices[:, 0], sphere_vertices[:, 1],
                                sphere_vertices[:, 2])
    theta[np.isnan(theta)] = 0
    F = radial_order / 2
    n_c = np.round(1 / 6.0 * (F + 1) * (F + 2) * (4 * F + 3))
    upsilon = np.zeros((len(sphere_vertices), n_c))
    counter = 0
    for l in range(0, radial_order + 1, 2):
        for n in range(l, int((radial_order + l) / 2) + 1):
            for m in range(-l, l + 1):
                upsilon[:, counter] = (-1) ** (n - l / 2.0) * _kappa_odf(zeta, n, l) * \
                    hyp2f1(l - n, l / 2.0 + 1.5, l + 1.5, 2.0) * \
                    real_sph_harm(m, l, theta, phi)
                counter += 1

    return upsilon
예제 #11
0
파일: csdeconv.py 프로젝트: swederik/dipy
def gen_dirac(pol, azi, sh_order):
    """ Generate Dirac delta function orientated in (theta, phi) = (azi, pol)
    on the sphere. The spherical harmonics (SH) representation of this Dirac is
    returned. 

    Parameters
    ----------
    pol : float [0, pi]
        The polar (colatitudinal) coordinate (phi)
    az : float [0, 2*pi]
        The azimuthal (longitudinal) coordinate (theta)
    sh_order : int
        maximal SH order of the SH representation

    Returns
    -------
    dirac : ndarray (``(sh_order + 1)(sh_order + 2)/2``,)
        SH coefficients representing the Dirac function
    """
    m, n = sph_harm_ind_list(sh_order)
    dirac = np.zeros(m.shape)
    i = 0
    for l in np.arange(0, sh_order + 1, 2):
        for m in np.arange(-l, l + 1):
            if m == 0:
                dirac[i] = real_sph_harm(0, l, azi, pol)

            i = i + 1

    return dirac
예제 #12
0
파일: csdeconv.py 프로젝트: gsangui/dipy
def gen_dirac(m, n, theta, phi):
    """ Generate Dirac delta function orientated in (theta, phi) on the sphere

    The spherical harmonics (SH) representation of this Dirac is returned as
    coefficients to spherical harmonic functions produced by
    `shm.real_sph_harm`.

    Parameters
    ----------
    m : ndarray (N,)
        The order of the spherical harmonic function associated with each
        coefficient.
    n : ndarray (N,)
        The degree of the spherical harmonic function associated with each
        coefficient.
    theta : float [0, 2*pi]
        The azimuthal (longitudinal) coordinate.
    phi : float [0, pi]
        The polar (colatitudinal) coordinate.

    See Also
    --------
    shm.real_sph_harm, shm.real_sym_sh_basis

    Returns
    -------
    dirac : ndarray
        SH coefficients representing the Dirac function

    """
    return real_sph_harm(m, n, theta, phi)
예제 #13
0
파일: shore.py 프로젝트: gsangui/dipy
def SHOREmatrix_pdf(radial_order, zeta, rtab):
    """Compute the SHORE matrix"

    Parameters
    ----------
    radial_order : unsigned int,
        Radial Order
    zeta : unsigned int,
        scale factor
    rtab : array, shape (N,3)
        r-space points in which calculates the pdf
    """

    r, theta, phi = cart2sphere(rtab[:, 0], rtab[:, 1], rtab[:, 2])
    theta[np.isnan(theta)] = 0

    psi = np.zeros(
        (r.shape[0], (radial_order + 1) * ((radial_order + 1) / 2) * (2 * radial_order + 1)))
    counter = 0
    for n in range(radial_order + 1):
        for l in range(0, n + 1, 2):
            for m in range(-l, l + 1):
                psi[:, counter] = real_sph_harm(m, l, theta, phi) * \
                    genlaguerre(n - l, l + 0.5)(4 * np.pi ** 2 * zeta * r ** 2 ) *\
                    np.exp(-2 * np.pi ** 2 * zeta * r ** 2) *\
                    __kappa_pdf(zeta, n, l) *\
                    (4 * np.pi ** 2 * zeta * r ** 2) ** (l / 2) * \
                    (-1) ** (n - l / 2)
                counter += 1
    return psi[:, 0:counter]
예제 #14
0
def shore_Q_mu_independent(radial_order, q):
    r'''Computed the u0 independent part of the design matrix.
    ..math::
        :nowrap:
            \begin{align}
                \Xi_{jlm(i)}(u_0,\textbf{q}_k)=&\overbrace{u_0^{l(i)}
                e^{-2\pi^2u_0^2q_k^2}L_{j(i)-1}^{l(i)+1/2}
                (4\pi^2u_0^2q_k^2)}^{u_0\,dependent}
                \overbrace{\sqrt{4\pi}i^{-l(i)}(2\pi^2q_k^2)^{l(i)/2}
                Y_{l(i)}^{m(i)}(\textbf{u}_{q_k})}^{u_0\,independent}
                =&A_{jl(i)}(q_k)B_{lm(i)}(\textbf{q}_k)
            \end{align}
    '''
    ind_mat = shore_index_matrix(radial_order)

    qval, theta, phi = cart2sphere(q[:, 0], q[:, 1], q[:, 2])
    theta[np.isnan(theta)] = 0

    n_elem = ind_mat.shape[0]
    n_rgrad = theta.shape[0]
    Q_mu_independent = np.zeros((n_rgrad, n_elem))

    counter = 0
    for n in range(0, radial_order + 1, 2):
        for j in range(1, 2 + n / 2):
            l = n + 2 - 2 * j
            const = np.sqrt(4 * np.pi) * (-1) ** (-l / 2) * \
                (2 * np.pi ** 2 * qval ** 2) ** (l / 2)
            for m in range(-1 * (n + 2 - 2 * j), (n + 3 - 2 * j)):
                Q_mu_independent[:, counter] = const * \
                    real_sph_harm(m, l, theta, phi)
                counter += 1
    return Q_mu_independent
예제 #15
0
def real_sym_rh_basis(sh_order, theta, phi):
    r"""Samples a real symmetric rotational harmonic basis at point on the sphere

    Samples the basis functions up to order `sh_order` at points on the sphere
    given by `theta` and `phi`. The basis functions are defined here the same
    way as in fibernavigator, where the real harmonic $Y^m_n$ is defined to
    be:

        $Y^0_n$                     if m = 0

    Parameters
    -----------
    sh_order : int
        even int > 0, max spherical harmonic degree
    theta : float [0, 2*pi]
        The azimuthal (longitudinal) coordinate.
    phi : float [0, pi]
        The polar (colatitudinal) coordinate.

    Returns
    --------
    real_rh_matrix : array of shape ()
        The real harmonic $Y^0_n$ sampled at `theta` and `phi`
    """
    n = np.arange(0, sh_order + 1, 2)
    m = np.zeros(sh_order // 2 + 1)

    phi = np.reshape(phi, [-1, 1])
    theta = np.reshape(theta, [-1, 1])

    real_rh_matrix = real_sph_harm(m, n, theta, phi)
    return real_rh_matrix
예제 #16
0
    def sh_convolution_matrix(self, kernel="rank1"):
        """

        Parameters
        ----------
        kernel

        Returns
        -------

        """
        if self.kernel_type != kernel:
            self.set_kernel(kernel)

        # Build matrix that maps ODF to signal
        M = np.zeros((self.gtab.bvals.shape[0], esh.LENGTH[self.order]))
        r, theta, phi = cart2sphere(self.gtab.bvecs[:, 0],
                                    self.gtab.bvecs[:, 1], self.gtab.bvecs[:,
                                                                           2])
        theta[np.isnan(theta)] = 0
        counter = 0
        for l in range(0, self.order + 1, 2):
            for m in range(-l, l + 1):
                M[:, counter] = (real_sph_harm(m, l, theta, phi) *
                                 self.kernel_wm[l // 2])
                counter += 1

        return M
예제 #17
0
파일: shore.py 프로젝트: gsangui/dipy
def SHOREmatrix_odf(radial_order, zeta, sphere_vertices):
    """Compute the SHORE matrix"

    Parameters
    ----------
    radial_order : unsigned int,
        Radial Order
    zeta : unsigned int,
        scale factor
    sphere_vertices : array, shape (N,3)
        vertices of the odf sphere
    """

    r, theta, phi = cart2sphere(sphere_vertices[:, 0], sphere_vertices[:, 1], sphere_vertices[:, 2])
    theta[np.isnan(theta)] = 0
    counter = 0
    upsilon = np.zeros(
        (len(sphere_vertices), (radial_order + 1) * ((radial_order + 1) / 2) * (2 * radial_order + 1)))
    for n in range(radial_order + 1):
        for l in range(0, n + 1, 2):
            for m in range(-l, l + 1):
                upsilon[:, counter] = (-1) ** (n - l / 2.0) * __kappa_odf(zeta, n, l) * \
                    hyp2f1(l - n, l / 2.0 + 1.5, l + 1.5, 2.0) * \
                    real_sph_harm(m, l, theta, phi)
                counter += 1

    return upsilon[:, 0:counter]
예제 #18
0
def gen_dirac(pol, azi, sh_order):
    """ Generate Dirac delta function orientated in (theta, phi) = (azi, pol)
    on the sphere. The spherical harmonics (SH) representation of this Dirac is
    returned. 

    Parameters
    ----------
    pol : float [0, pi]
        The polar (colatitudinal) coordinate (phi)
    az : float [0, 2*pi]
        The azimuthal (longitudinal) coordinate (theta)
    sh_order : int
        maximal SH order of the SH representation

    Returns
    -------
    dirac : ndarray (``(sh_order + 1)(sh_order + 2)/2``,)
        SH coefficients representing the Dirac function
    """
    m, n = sph_harm_ind_list(sh_order)
    dirac = np.zeros(m.shape)
    i = 0
    for l in np.arange(0, sh_order + 1, 2):
        for m in np.arange(-l, l + 1):
            if m == 0:
                dirac[i] = real_sph_harm(0, l, azi, pol)

            i = i + 1

    return dirac
예제 #19
0
def sh_smooth(data, bvals, bvecs, sh_order=4, similarity_threshold=50, regul=0.006):
    """Smooth the raw diffusion signal with spherical harmonics.
    data : ndarray
        The diffusion data to smooth.
    gtab : gradient table object
        Corresponding gradients table object to data.
    sh_order : int, default 8
        Order of the spherical harmonics to fit.
    similarity_threshold : int, default 50
        All b-values such that |b_1 - b_2| < similarity_threshold
        will be considered as identical for smoothing purpose.
        Must be lower than 200.
    regul : float, default 0.006
        Amount of regularization to apply to sh coefficients computation.
    Return
    ---------
    pred_sig : ndarray
        The smoothed diffusion data, fitted through spherical harmonics.
    """

    if similarity_threshold > 200:
        raise ValueError("similarity_threshold = {}, which is higher than 200,"
                         " please use a lower value".format(similarity_threshold))

    m, n = sph_harm_ind_list(sh_order)
    L = -n * (n + 1)
    where_b0s = bvals == 0
    pred_sig = np.zeros_like(data, dtype=np.float32)

    # Round similar bvals together for identifying similar shells
    rounded_bvals = np.zeros_like(bvals)

    for unique_bval in np.unique(bvals):
        idx = np.abs(unique_bval - bvals) < similarity_threshold
        rounded_bvals[idx] = unique_bval

    # process each b-value separately
    for unique_bval in np.unique(rounded_bvals):
        idx = rounded_bvals == unique_bval

        # Just give back the signal for the b0s since we can't really do anything about it
        if np.all(idx == where_b0s):
            if np.sum(where_b0s) > 1:
                pred_sig[..., idx] = np.mean(data[..., idx], axis=-1, keepdims=True)
            else:
                pred_sig[..., idx] = data[..., idx]
            continue

        x, y, z = bvecs[:, idx]
        r, theta, phi = cart2sphere(x, y, z)

        # Find the sh coefficients to smooth the signal
        B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])
        invB = smooth_pinv(B_dwi, np.sqrt(regul) * L)
        sh_coeff = np.dot(data[..., idx], invB.T)

        # Find the smoothed signal from the sh fit for the given gtab
        pred_sig[..., idx] = np.dot(sh_coeff, B_dwi.T)

    return pred_sig
예제 #20
0
파일: csdeconv.py 프로젝트: bevlin510/dipy
    def odf(self, sphere):

        sampling_matrix = self.model.cache_get("sampling_matrix", sphere)
        if sampling_matrix is None:
            phi = sphere.phi[:, np.newaxis] #sphere.phi.reshape((-1, 1))
            theta = sphere.theta.reshape((-1, 1))
            sampling_matrix = real_sph_harm(self.model.m, self.model.n, theta, phi)
            self.model.cache_set("sampling_matrix", sphere, sampling_matrix)

        return np.dot(self.shm_coeff, sampling_matrix.T)
예제 #21
0
def shore_odf_matrix(radial_order, mu, s, vertices):
    r"""The ODF in terms of SHORE coefficients for arbitrary radial moment
    can be given as [2]:
    ..math::
        :nowrap:
            \begin{equation}
                ODF_s(u_0,\textbf{v})=\sum_{N=0}^{N_{max}}
                \sum_{\{j,l,m\}}\textbf{c}_{\{j,l,m\}}
                \Omega_s^{jlm}(u_0,\textbf{v})
            \end{equation}

    with $\textbf{v}$ an orientation on the unit sphere and the ODF
    basis function:
    ..math::
        :nowrap:
            \begin{equation}
                \Omega_s^{jlm}(u_0,\textbf{v})=\frac{u_0^s}{\pi}(-1)^{j-1}
                2^{-l/2}\kappa(j,l,s)Y^l_m(\textbf{v})
            \end{equation}
    with
    ..math::
        :nowrap:
            \begin{equation}
                \kappa(j,l,s)=\sum_{k=0}^{j-1}\frac{(-1)^k}{k!}
                \binom{j+l-1/2}{j-k-1}
                \frac{\Gamma((l+s+3)/2+k)}{2^{-((l+s)/2+k)}}.
            \end{equation}
    """
    r, theta, phi = cart2sphere(vertices[:, 0], vertices[:, 1], vertices[:, 2])

    theta[np.isnan(theta)] = 0
    ind_mat = shore_index_matrix(radial_order)
    n_vert = vertices.shape[0]
    n_elem = ind_mat.shape[0]
    odf_mat = np.zeros((n_vert, n_elem))

    counter = 0
    for n in range(0, radial_order + 1, 2):
        for j in range(1, 2 + n / 2):
            l = n + 2 - 2 * j
            kappa = ((-1)**(j - 1) * 2**(-(l + 3) / 2.0) * mu**s) / np.pi
            matsum = 0
            for k in range(0, j):
                matsum += ((-1) ** k * binomialfloat(j + l - 0.5, j - k - 1) *
                           gamma((l + s + 3) / 2.0 + k)) /\
                    (factorial(k) * 0.5 ** ((l + s + 3) / 2.0 + k))
            for m in range(-l, l + 1):
                odf_mat[:, counter] = kappa * matsum *\
                    real_sph_harm(m, l, theta, phi)
                counter += 1

    return odf_mat
예제 #22
0
파일: forecast.py 프로젝트: mbeyeler/dipy
def rho_matrix(sh_order, vecs):
    r"""Compute the SH matrix $\rho$
    """

    r, theta, phi = cart2sphere(vecs[:, 0], vecs[:, 1], vecs[:, 2])
    theta[np.isnan(theta)] = 0

    n_c = int((sh_order + 1) * (sh_order + 2) / 2)
    rho = np.zeros((vecs.shape[0], n_c))
    counter = 0
    for l in range(0, sh_order + 1, 2):
        for m in range(-l, l + 1):
            rho[:, counter] = real_sph_harm(m, l, theta, phi)
            counter += 1
    return rho
예제 #23
0
def rho_matrix(sh_order, vecs):
    r"""Compute the SH matrix $\rho$
    """

    r, theta, phi = cart2sphere(vecs[:, 0], vecs[:, 1], vecs[:, 2])
    theta[np.isnan(theta)] = 0

    n_c = int((sh_order + 1) * (sh_order + 2) / 2)
    rho = np.zeros((vecs.shape[0], n_c))
    counter = 0
    for l in range(0, sh_order + 1, 2):
        for m in range(-l, l + 1):
            rho[:, counter] = real_sph_harm(m, l, theta, phi)
            counter += 1
    return rho
예제 #24
0
파일: smoothing.py 프로젝트: BIG-S2/PSC
def sh_smooth(data, gtab, sh_order=4):
    """Smooth the raw diffusion signal with spherical harmonics

    data : ndarray
        The diffusion data to smooth.

    gtab : gradient table object
        Corresponding gradients table object to data.

    sh_order : int, default 4
        Order of the spherical harmonics to fit.

    Return
    ---------
    pred_sig : ndarray
        The smoothed diffusion data, fitted through spherical harmonics.
    """

    m, n = sph_harm_ind_list(sh_order)
    where_b0s = lazy_index(gtab.b0s_mask)
    where_dwi = lazy_index(~gtab.b0s_mask)

    x, y, z = gtab.gradients[where_dwi].T
    r, theta, phi = cart2sphere(x, y, z)

    # Find the sh coefficients to smooth the signal
    B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])
    sh_shape = (np.prod(data.shape[:-1]), -1)
    sh_coeff = np.linalg.lstsq(B_dwi, data[...,
                                           where_dwi].reshape(sh_shape).T)[0]

    # Find the smoothed signal from the sh fit for the given gtab
    smoothed_signal = np.dot(B_dwi,
                             sh_coeff).T.reshape(data.shape[:-1] + (-1, ))
    pred_sig = np.zeros(smoothed_signal.shape[:-1] + (gtab.bvals.shape[0], ))
    pred_sig[..., ~gtab.b0s_mask] = smoothed_signal

    # Just give back the signal for the b0s since we can't really do anything about it
    if np.sum(gtab.b0s_mask) > 1:
        pred_sig[..., where_b0s] = np.mean(data[..., where_b0s], axis=-1)
    else:
        pred_sig[..., where_b0s] = data[..., where_b0s]

    return pred_sig
예제 #25
0
파일: test_shm.py 프로젝트: arokem/dipy
def test_hat_and_lcr():
    hemi = hemi_icosahedron.subdivide(3)
    m, n = sph_harm_ind_list(8)
    B = real_sph_harm(m, n, hemi.theta[:, None], hemi.phi[:, None])
    H = hat(B)
    B_hat = np.dot(H, B)
    assert_array_almost_equal(B, B_hat)

    R = lcr_matrix(H)
    d = np.arange(len(hemi.theta))
    r = d - np.dot(H, d)
    lev = np.sqrt(1 - H.diagonal())
    r /= lev
    r -= r.mean()

    r2 = np.dot(R, d)
    assert_array_almost_equal(r, r2)

    r3 = np.dot(d, R.T)
    assert_array_almost_equal(r, r3)
예제 #26
0
def test_hat_and_lcr():
    hemi = hemi_icosahedron.subdivide(3)
    m, n = sph_harm_ind_list(8)
    B = real_sph_harm(m, n, hemi.theta[:, None], hemi.phi[:, None])
    H = hat(B)
    B_hat = np.dot(H, B)
    assert_array_almost_equal(B, B_hat)

    R = lcr_matrix(H)
    d = np.arange(len(hemi.theta))
    r = d - np.dot(H, d)
    lev = np.sqrt(1 - H.diagonal())
    r /= lev
    r -= r.mean()

    r2 = np.dot(R, d)
    assert_array_almost_equal(r, r2)

    r3 = np.dot(d, R.T)
    assert_array_almost_equal(r, r3)
예제 #27
0
def test_hat_and_lcr():
    v, e, f = create_half_unit_sphere(6)
    m, n = sph_harm_ind_list(8)
    r, pol, azi = cart2sphere(*v.T)
    B = real_sph_harm(m, n, azi[:, None], pol[:, None])
    H = hat(B)
    B_hat = np.dot(H, B)
    assert_array_almost_equal(B, B_hat)

    R = lcr_matrix(H)
    d = np.arange(len(azi))
    r = d - np.dot(H, d)
    lev = np.sqrt(1 - H.diagonal())
    r /= lev
    r -= r.mean()

    r2 = np.dot(R, d)
    assert_array_almost_equal(r, r2)

    r3 = np.dot(d, R.T)
    assert_array_almost_equal(r, r3)
예제 #28
0
def test_hat_and_lcr():
    v, e, f = create_half_unit_sphere(6)
    m, n = sph_harm_ind_list(8)
    r, pol, azi = cart2sphere(*v.T)
    B = real_sph_harm(m, n, azi[:, None], pol[:, None])
    H = hat(B)
    B_hat = np.dot(H, B)
    assert_array_almost_equal(B, B_hat)

    R = lcr_matrix(H)
    d = np.arange(len(azi))
    r = d - np.dot(H, d)
    lev = np.sqrt(1 - H.diagonal())
    r /= lev
    r -= r.mean()

    r2 = np.dot(R, d)
    assert_array_almost_equal(r, r2)

    r3 = np.dot(d, R.T)
    assert_array_almost_equal(r, r3)
예제 #29
0
def esh_matrix(order, gtab):
    """ Matrix that evaluates SH coeffs in the given directions

    Parameters
    ----------
    order
    gtab

    Returns
    -------

    """
    bvecs = gtab.bvecs
    r, theta, phi = cart2sphere(bvecs[:, 0], bvecs[:, 1], bvecs[:, 2])
    theta[np.isnan(theta)] = 0
    M = np.zeros((bvecs.shape[0], esh.LENGTH[order]))
    counter = 0
    for l in range(0, order + 1, 2):
        for m in range(-l, l + 1):
            M[:, counter] = real_sph_harm(m, l, theta, phi)
            counter += 1
    return M
예제 #30
0
파일: shore.py 프로젝트: gsangui/dipy
def SHOREmatrix(radial_order, zeta, gtab, tau=1 / (4 * np.pi ** 2)):
    """Compute the SHORE matrix"

    Parameters
    ----------
    radial_order : unsigned int,
        Radial Order
    zeta : unsigned int,
        scale factor
    gtab : GradientTable,
        Gradient directions and bvalues container class
    tau : float,
        diffusion time. By default the value that makes q=sqrt(b).

    """

    qvals = np.sqrt(gtab.bvals / (4 * np.pi ** 2 * tau))
    bvecs = gtab.bvecs

    qgradients = qvals[:, None] * bvecs

    r, theta, phi = cart2sphere(
        qgradients[:, 0], qgradients[:, 1], qgradients[:, 2])
    theta[np.isnan(theta)] = 0

    M = np.zeros(
        (r.shape[0], (radial_order + 1) * ((radial_order + 1) / 2) * (2 * radial_order + 1)))

    counter = 0
    for n in range(radial_order + 1):
        for l in range(0, n + 1, 2):
            for m in range(-l, l + 1):
                M[:, counter] = real_sph_harm(m, l, theta, phi) * \
                    genlaguerre(n - l, l + 0.5)(r ** 2 / float(zeta)) * \
                    np.exp(- r ** 2 / (2.0 * zeta)) * \
                    __kappa(zeta, n, l) * \
                    (r ** 2 / float(zeta)) ** (l / 2)
                counter += 1
    return M[:, 0:counter]
예제 #31
0
파일: test_shm.py 프로젝트: arokem/dipy
def test_smooth_pinv():
    hemi = hemi_icosahedron.subdivide(2)
    m, n = sph_harm_ind_list(4)
    B = real_sph_harm(m, n, hemi.theta[:, None], hemi.phi[:, None])

    L = np.zeros(len(m))
    C = smooth_pinv(B, L)
    D = np.dot(npl.inv(np.dot(B.T, B)), B.T)
    assert_array_almost_equal(C, D)

    L = n * (n + 1) * .05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)

    assert_array_almost_equal(C, D)

    L = np.arange(len(n)) * .05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)
    assert_array_almost_equal(C, D)
예제 #32
0
def test_smooth_pinv():
    hemi = hemi_icosahedron.subdivide(2)
    m, n = sph_harm_ind_list(4)
    B = real_sph_harm(m, n, hemi.theta[:, None], hemi.phi[:, None])

    L = np.zeros(len(m))
    C = smooth_pinv(B, L)
    D = np.dot(npl.inv(np.dot(B.T, B)), B.T)
    assert_array_almost_equal(C, D)

    L = n * (n + 1) * .05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)

    assert_array_almost_equal(C, D)

    L = np.arange(len(n)) * .05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)
    assert_array_almost_equal(C, D)
예제 #33
0
def test_smooth_pinv():
    v, e, f = create_half_unit_sphere(3)
    m, n = sph_harm_ind_list(4)
    r, pol, azi = cart2sphere(*v.T)
    B = real_sph_harm(m, n, azi[:, None], pol[:, None])

    L = np.zeros(len(m))
    C = smooth_pinv(B, L)
    D = np.dot(npl.inv(np.dot(B.T, B)), B.T)
    assert_array_almost_equal(C, D)

    L = n * (n + 1) * 0.05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)

    assert_array_almost_equal(C, D)

    L = np.arange(len(n)) * 0.05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)
    assert_array_almost_equal(C, D)
예제 #34
0
def test_smooth_pinv():
    v, e, f = create_half_unit_sphere(3)
    m, n = sph_harm_ind_list(4)
    r, pol, azi = cart2sphere(*v.T)
    B = real_sph_harm(m, n, azi[:, None], pol[:, None])

    L = np.zeros(len(m))
    C = smooth_pinv(B, L)
    D = np.dot(npl.inv(np.dot(B.T, B)), B.T)
    assert_array_almost_equal(C, D)

    L = n * (n + 1) * .05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)

    assert_array_almost_equal(C, D)

    L = np.arange(len(n)) * .05
    C = smooth_pinv(B, L)
    L = np.diag(L)
    D = np.dot(npl.inv(np.dot(B.T, B) + L * L), B.T)
    assert_array_almost_equal(C, D)
예제 #35
0
def shore_K_mu_independent(radial_order, rgrad):
    '''Computes mu independent part of K [2]. Same trick as with Q.
    '''
    r, theta, phi = cart2sphere(rgrad[:, 0], rgrad[:, 1], rgrad[:, 2])
    theta[np.isnan(theta)] = 0

    ind_mat = shore_index_matrix(radial_order)

    n_elem = ind_mat.shape[0]
    n_rgrad = rgrad.shape[0]
    K = np.zeros((n_rgrad, n_elem))

    counter = 0
    for n in range(0, radial_order + 1, 2):
        for j in range(1, 2 + n / 2):
            l = n + 2 - 2 * j
            const = (-1) ** (j - 1) *\
                (np.sqrt(2) * np.pi) ** (-1) *\
                (r ** 2 / 2) ** (l / 2)
            for m in range(-l, l + 1):
                K[:, counter] = const * real_sph_harm(m, l, theta, phi)
                counter += 1
    return K
예제 #36
0
파일: shore.py 프로젝트: conorkcorbin/dipy
def shore_matrix_pdf(radial_order, zeta, rtab):
    r"""Compute the SHORE propagator matrix [1]_"

    Parameters
    ----------
    radial_order : unsigned int,
        an even integer that represent the order of the basis
    zeta : unsigned int,
        scale factor
    rtab : array, shape (N,3)
        real space points in which calculates the pdf

    References
    ----------
    .. [1] Merlet S. et. al, "Continuous diffusion signal, EAP and
    ODF estimation via Compressive Sensing in diffusion MRI", Medical
    Image Analysis, 2013.
    """

    r, theta, phi = cart2sphere(rtab[:, 0], rtab[:, 1], rtab[:, 2])
    theta[np.isnan(theta)] = 0
    F = radial_order / 2
    n_c = int(np.round(1 / 6.0 * (F + 1) * (F + 2) * (4 * F + 3)))
    psi = np.zeros((r.shape[0], n_c))
    counter = 0
    for l in range(0, radial_order + 1, 2):
        for n in range(l, int((radial_order + l) / 2) + 1):
            for m in range(-l, l + 1):
                psi[:, counter] = real_sph_harm(m, l, theta, phi) * \
                    genlaguerre(n - l, l + 0.5)(4 * np.pi ** 2 *
                                                zeta * r ** 2) *\
                    np.exp(-2 * np.pi ** 2 * zeta * r ** 2) *\
                    _kappa_pdf(zeta, n, l) *\
                    (4 * np.pi ** 2 * zeta * r ** 2) ** (l / 2) * \
                    (-1) ** (n - l / 2)
                counter += 1
    return psi
예제 #37
0
파일: shore.py 프로젝트: neurodata/aloftus
def shore_matrix_pdf(radial_order, zeta, rtab):
    r"""Compute the SHORE propagator matrix [1]_"

    Parameters
    ----------
    radial_order : unsigned int,
        an even integer that represent the order of the basis
    zeta : unsigned int,
        scale factor
    rtab : array, shape (N,3)
        real space points in which calculates the pdf

    References
    ----------
    .. [1] Merlet S. et al., "Continuous diffusion signal, EAP and
    ODF estimation via Compressive Sensing in diffusion MRI", Medical
    Image Analysis, 2013.
    """

    r, theta, phi = cart2sphere(rtab[:, 0], rtab[:, 1], rtab[:, 2])
    theta[np.isnan(theta)] = 0
    F = radial_order / 2
    n_c = int(np.round(1 / 6.0 * (F + 1) * (F + 2) * (4 * F + 3)))
    psi = np.zeros((r.shape[0], n_c))
    counter = 0
    for l in range(0, radial_order + 1, 2):
        for n in range(l, int((radial_order + l) / 2) + 1):
            for m in range(-l, l + 1):
                psi[:, counter] = real_sph_harm(m, l, theta, phi) * \
                    genlaguerre(n - l, l + 0.5)(4 * np.pi ** 2 *
                                                zeta * r ** 2) *\
                    np.exp(-2 * np.pi ** 2 * zeta * r ** 2) *\
                    _kappa_pdf(zeta, n, l) *\
                    (4 * np.pi ** 2 * zeta * r ** 2) ** (l / 2) * \
                    (-1) ** (n - l / 2)
                counter += 1
    return psi
예제 #38
0
def recursive_response(gtab, data, mask=None, sh_order=8, peak_thr=0.01,
                       init_fa=0.08, init_trace=0.0021, iter=8,
                       convergence=0.001, parallel=True, nbr_processes=None,
                       sphere=default_sphere):
    """ Recursive calibration of response function using peak threshold

    Parameters
    ----------
    gtab : GradientTable
    data : ndarray
        diffusion data
    mask : ndarray, optional
        mask for recursive calibration, for example a white matter mask. It has
        shape `data.shape[0:3]` and dtype=bool. Default: use the entire data
        array.
    sh_order : int, optional
        maximal spherical harmonics order. Default: 8
    peak_thr : float, optional
        peak threshold, how large the second peak can be relative to the first
        peak in order to call it a single fiber population [1]. Default: 0.01
    init_fa : float, optional
        FA of the initial 'fat' response function (tensor). Default: 0.08
    init_trace : float, optional
        trace of the initial 'fat' response function (tensor). Default: 0.0021
    iter : int, optional
        maximum number of iterations for calibration. Default: 8.
    convergence : float, optional
        convergence criterion, maximum relative change of SH
        coefficients. Default: 0.001.
    parallel : bool, optional
        Whether to use parallelization in peak-finding during the calibration
        procedure. Default: True
    nbr_processes: int
        If `parallel` is True, the number of subprocesses to use
        (default multiprocessing.cpu_count()).
    sphere : Sphere, optional.
        The sphere used for peak finding. Default: default_sphere.

    Returns
    -------
    response : ndarray
        response function in SH coefficients

    Notes
    -----
    In CSD there is an important pre-processing step: the estimation of the
    fiber response function. Using an FA threshold is not a very robust method.
    It is dependent on the dataset (non-informed used subjectivity), and still
    depends on the diffusion tensor (FA and first eigenvector),
    which has low accuracy at high b-value. This function recursively
    calibrates the response function, for more information see [1].

    References
    ----------
    .. [1] Tax, C.M.W., et al. NeuroImage 2014. Recursive calibration of
           the fiber response function for spherical deconvolution of
           diffusion MRI data.
    """
    S0 = 1.
    evals = fa_trace_to_lambdas(init_fa, init_trace)
    res_obj = (evals, S0)

    if mask is None:
        data = data.reshape(-1, data.shape[-1])
    else:
        data = data[mask]

    n = np.arange(0, sh_order + 1, 2)
    where_dwi = lazy_index(~gtab.b0s_mask)
    response_p = np.ones(len(n))

    for num_it in range(iter):
        r_sh_all = np.zeros(len(n))
        csd_model = ConstrainedSphericalDeconvModel(gtab, res_obj,
                                                    sh_order=sh_order)

        csd_peaks = peaks_from_model(model=csd_model,
                                     data=data,
                                     sphere=sphere,
                                     relative_peak_threshold=peak_thr,
                                     min_separation_angle=25,
                                     parallel=parallel,
                                     nbr_processes=nbr_processes)

        dirs = csd_peaks.peak_dirs
        vals = csd_peaks.peak_values
        single_peak_mask = (vals[:, 1] / vals[:, 0]) < peak_thr
        data = data[single_peak_mask]
        dirs = dirs[single_peak_mask]

        for num_vox in range(data.shape[0]):
            rotmat = vec2vec_rotmat(dirs[num_vox, 0], np.array([0, 0, 1]))

            rot_gradients = np.dot(rotmat, gtab.gradients.T).T

            x, y, z = rot_gradients[where_dwi].T
            r, theta, phi = cart2sphere(x, y, z)
            # for the gradient sphere
            B_dwi = real_sph_harm(0, n, theta[:, None], phi[:, None])
            r_sh_all += np.linalg.lstsq(B_dwi, data[num_vox, where_dwi])[0]

        response = r_sh_all / data.shape[0]
        res_obj = AxSymShResponse(data[:, gtab.b0s_mask].mean(), response)

        change = abs((response_p - response) / response_p)
        if all(change < convergence):
            break

        response_p = response

    return res_obj
예제 #39
0
파일: csdeconv.py 프로젝트: mbeyeler/dipy
def recursive_response(gtab,
                       data,
                       mask=None,
                       sh_order=8,
                       peak_thr=0.01,
                       init_fa=0.08,
                       init_trace=0.0021,
                       iter=8,
                       convergence=0.001,
                       parallel=True,
                       nbr_processes=None,
                       sphere=default_sphere):
    """ Recursive calibration of response function using peak threshold

    Parameters
    ----------
    gtab : GradientTable
    data : ndarray
        diffusion data
    mask : ndarray, optional
        mask for recursive calibration, for example a white matter mask. It has
        shape `data.shape[0:3]` and dtype=bool. Default: use the entire data
        array.
    sh_order : int, optional
        maximal spherical harmonics order. Default: 8
    peak_thr : float, optional
        peak threshold, how large the second peak can be relative to the first
        peak in order to call it a single fiber population [1]. Default: 0.01
    init_fa : float, optional
        FA of the initial 'fat' response function (tensor). Default: 0.08
    init_trace : float, optional
        trace of the initial 'fat' response function (tensor). Default: 0.0021
    iter : int, optional
        maximum number of iterations for calibration. Default: 8.
    convergence : float, optional
        convergence criterion, maximum relative change of SH
        coefficients. Default: 0.001.
    parallel : bool, optional
        Whether to use parallelization in peak-finding during the calibration
        procedure. Default: True
    nbr_processes: int
        If `parallel` is True, the number of subprocesses to use
        (default multiprocessing.cpu_count()).
    sphere : Sphere, optional.
        The sphere used for peak finding. Default: default_sphere.

    Returns
    -------
    response : ndarray
        response function in SH coefficients

    Notes
    -----
    In CSD there is an important pre-processing step: the estimation of the
    fiber response function. Using an FA threshold is not a very robust method.
    It is dependent on the dataset (non-informed used subjectivity), and still
    depends on the diffusion tensor (FA and first eigenvector),
    which has low accuracy at high b-value. This function recursively
    calibrates the response function, for more information see [1].

    References
    ----------
    .. [1] Tax, C.M.W., et al. NeuroImage 2014. Recursive calibration of
           the fiber response function for spherical deconvolution of
           diffusion MRI data.
    """
    S0 = 1.
    evals = fa_trace_to_lambdas(init_fa, init_trace)
    res_obj = (evals, S0)

    if mask is None:
        data = data.reshape(-1, data.shape[-1])
    else:
        data = data[mask]

    n = np.arange(0, sh_order + 1, 2)
    where_dwi = lazy_index(~gtab.b0s_mask)
    response_p = np.ones(len(n))

    for _ in range(iter):
        r_sh_all = np.zeros(len(n))
        csd_model = ConstrainedSphericalDeconvModel(gtab,
                                                    res_obj,
                                                    sh_order=sh_order)

        csd_peaks = peaks_from_model(model=csd_model,
                                     data=data,
                                     sphere=sphere,
                                     relative_peak_threshold=peak_thr,
                                     min_separation_angle=25,
                                     parallel=parallel,
                                     nbr_processes=nbr_processes)

        dirs = csd_peaks.peak_dirs
        vals = csd_peaks.peak_values
        single_peak_mask = (vals[:, 1] / vals[:, 0]) < peak_thr
        data = data[single_peak_mask]
        dirs = dirs[single_peak_mask]

        for num_vox in range(data.shape[0]):
            rotmat = vec2vec_rotmat(dirs[num_vox, 0], np.array([0, 0, 1]))

            rot_gradients = np.dot(rotmat, gtab.gradients.T).T

            x, y, z = rot_gradients[where_dwi].T
            r, theta, phi = cart2sphere(x, y, z)
            # for the gradient sphere
            B_dwi = real_sph_harm(0, n, theta[:, None], phi[:, None])
            r_sh_all += np.linalg.lstsq(B_dwi,
                                        data[num_vox, where_dwi],
                                        rcond=-1)[0]

        response = r_sh_all / data.shape[0]
        res_obj = AxSymShResponse(data[:, gtab.b0s_mask].mean(), response)

        change = abs((response_p - response) / response_p)
        if all(change < convergence):
            break

        response_p = response

    return res_obj
예제 #40
0
파일: csdeconv.py 프로젝트: mbeyeler/dipy
    def __init__(self,
                 gtab,
                 response,
                 reg_sphere=None,
                 sh_order=8,
                 lambda_=1,
                 tau=0.1,
                 convergence=50):
        r""" Constrained Spherical Deconvolution (CSD) [1]_.

        Spherical deconvolution computes a fiber orientation distribution
        (FOD), also called fiber ODF (fODF) [2]_, as opposed to a diffusion ODF
        as the QballModel or the CsaOdfModel. This results in a sharper angular
        profile with better angular resolution that is the best object to be
        used for later deterministic and probabilistic tractography [3]_.

        A sharp fODF is obtained because a single fiber *response* function is
        injected as *a priori* knowledge. The response function is often
        data-driven and is thus provided as input to the
        ConstrainedSphericalDeconvModel. It will be used as deconvolution
        kernel, as described in [1]_.

        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'.
        sh_order : int (optional)
            maximal spherical harmonics order. Default: 8
        lambda_ : float (optional)
            weight given to the constrained-positivity regularization part of
            the deconvolution equation (see [1]_). Default: 1
        tau : float (optional)
            threshold controlling the amplitude below which the corresponding
            fODF is assumed to be zero.  Ideally, tau should be set to
            zero. However, to improve the stability of the algorithm, tau is
            set to tau*100 % of the mean fODF amplitude (here, 10% by default)
            (see [1]_). Default: 0.1
        convergence : int
            Maximum number of iterations to allow the deconvolution to converge.

        References
        ----------
        .. [1] Tournier, J.D., et al. NeuroImage 2007. Robust determination of
               the fibre orientation distribution in diffusion MRI:
               Non-negativity constrained super-resolved spherical
               deconvolution
        .. [2] Descoteaux, M., et al. IEEE TMI 2009. Deterministic and
               Probabilistic Tractography Based on Complex Fibre Orientation
               Distributions
        .. [3] Côté, M-A., et al. Medical Image Analysis 2013. Tractometer:
               Towards validation of tractography pipelines
        .. [4] Tournier, J.D, et al. Imaging Systems and Technology
               2012. MRtrix: Diffusion Tractography in Crossing Fiber Regions
        """
        # Initialize the parent class:
        SphHarmModel.__init__(self, gtab)
        m, n = sph_harm_ind_list(sh_order)
        self.m, self.n = m, n
        self._where_b0s = lazy_index(gtab.b0s_mask)
        self._where_dwi = lazy_index(~gtab.b0s_mask)

        no_params = ((sh_order + 1) * (sh_order + 2)) / 2

        if no_params > np.sum(~gtab.b0s_mask):
            msg = "Number of parameters required for the fit are more "
            msg += "than the actual data points"
            warnings.warn(msg, UserWarning)

        x, y, z = gtab.gradients[self._where_dwi].T
        r, theta, phi = cart2sphere(x, y, z)
        # for the gradient sphere
        self.B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])

        # for the sphere used in the regularization positivity constraint
        if reg_sphere is None:
            self.sphere = small_sphere
        else:
            self.sphere = reg_sphere

        r, theta, phi = cart2sphere(self.sphere.x, self.sphere.y,
                                    self.sphere.z)
        self.B_reg = real_sph_harm(m, n, theta[:, None], phi[:, None])

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

        self.response = response
        if isinstance(response, AxSymShResponse):
            r_sh = response.dwi_response
            self.response_scaling = response.S0
            n_response = response.n
            m_response = response.m
        else:
            self.S_r = estimate_response(gtab, self.response[0],
                                         self.response[1])
            r_sh = np.linalg.lstsq(self.B_dwi,
                                   self.S_r[self._where_dwi],
                                   rcond=-1)[0]
            n_response = n
            m_response = m
            self.response_scaling = response[1]
        r_rh = sh_to_rh(r_sh, m_response, n_response)
        self.R = forward_sdeconv_mat(r_rh, n)

        # scale lambda_ to account for differences in the number of
        # SH coefficients and number of mapped directions
        # This is exactly what is done in [4]_
        lambda_ = (lambda_ * self.R.shape[0] * r_rh[0] /
                   (np.sqrt(self.B_reg.shape[0]) * np.sqrt(362.)))
        self.B_reg *= lambda_
        self.sh_order = sh_order
        self.tau = tau
        self.convergence = convergence
        self._X = X = self.R.diagonal() * self.B_dwi
        self._P = np.dot(X.T, X)
예제 #41
0
파일: csdeconv.py 프로젝트: mbeyeler/dipy
 def basis(self, sphere):
     """A basis that maps the response coefficients onto a sphere."""
     theta = sphere.theta[:, None]
     phi = sphere.phi[:, None]
     return real_sph_harm(self.m, self.n, theta, phi)
예제 #42
0
파일: csdeconv.py 프로젝트: mbeyeler/dipy
    def __init__(self,
                 gtab,
                 ratio,
                 reg_sphere=None,
                 sh_order=8,
                 lambda_=1.,
                 tau=0.1):
        r""" Spherical Deconvolution Transform (SDT) [1]_.

        The SDT computes a fiber orientation distribution (FOD) as opposed to a
        diffusion ODF as the QballModel or the CsaOdfModel. This results in a
        sharper angular profile with better angular resolution. The Constrained
        SDTModel is similar to the Constrained CSDModel but mathematically it
        deconvolves the q-ball ODF as oppposed to the HARDI signal (see [1]_
        for a comparison and a through discussion).

        A sharp fODF is obtained because a single fiber *response* function is
        injected as *a priori* knowledge. In the SDTModel, this response is a
        single fiber q-ball ODF as opposed to a single fiber signal function
        for the CSDModel. The response function will be used as deconvolution
        kernel.

        Parameters
        ----------
        gtab : GradientTable
        ratio : float
            ratio of the smallest vs the largest eigenvalue of the single
            prolate tensor response function
        reg_sphere : Sphere
            sphere used to build the regularization B matrix
        sh_order : int
            maximal spherical harmonics order
        lambda_ : float
            weight given to the constrained-positivity regularization part of
            the deconvolution equation
        tau : float
            threshold (tau *mean(fODF)) controlling the amplitude below
            which the corresponding fODF is assumed to be zero.

        References
        ----------
        .. [1] Descoteaux, M., et al. IEEE TMI 2009. Deterministic and
               Probabilistic Tractography Based on Complex Fibre Orientation
               Distributions.

        """
        SphHarmModel.__init__(self, gtab)
        m, n = sph_harm_ind_list(sh_order)
        self.m, self.n = m, n
        self._where_b0s = lazy_index(gtab.b0s_mask)
        self._where_dwi = lazy_index(~gtab.b0s_mask)

        no_params = ((sh_order + 1) * (sh_order + 2)) / 2

        if no_params > np.sum(~gtab.b0s_mask):
            msg = "Number of parameters required for the fit are more "
            msg += "than the actual data points"
            warnings.warn(msg, UserWarning)

        x, y, z = gtab.gradients[self._where_dwi].T
        r, theta, phi = cart2sphere(x, y, z)
        # for the gradient sphere
        self.B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])

        # for the odf sphere
        if reg_sphere is None:
            self.sphere = get_sphere('symmetric362')
        else:
            self.sphere = reg_sphere

        r, theta, phi = cart2sphere(self.sphere.x, self.sphere.y,
                                    self.sphere.z)
        self.B_reg = real_sph_harm(m, n, theta[:, None], phi[:, None])

        self.R, self.P = forward_sdt_deconv_mat(ratio, n)

        # scale lambda_ to account for differences in the number of
        # SH coefficients and number of mapped directions
        self.lambda_ = (lambda_ * self.R.shape[0] * self.R[0, 0] /
                        self.B_reg.shape[0])
        self.tau = tau
        self.sh_order = sh_order
예제 #43
0
파일: shore.py 프로젝트: neurodata/aloftus
def shore_matrix(radial_order, zeta, gtab, tau=1 / (4 * np.pi**2)):
    r"""Compute the SHORE matrix for modified Merlet's 3D-SHORE [1]_

    ..math::
            :nowrap:
                \begin{equation}
                    \textbf{E}(q\textbf{u})=\sum_{l=0, even}^{N_{max}}
                                            \sum_{n=l}^{(N_{max}+l)/2}
                                            \sum_{m=-l}^l c_{nlm}
                                            \phi_{nlm}(q\textbf{u})
                \end{equation}

    where $\phi_{nlm}$ is
    ..math::
            :nowrap:
                \begin{equation}
                    \phi_{nlm}^{SHORE}(q\textbf{u})=\Biggl[\dfrac{2(n-l)!}
                        {\zeta^{3/2} \Gamma(n+3/2)} \Biggr]^{1/2}
                        \Biggl(\dfrac{q^2}{\zeta}\Biggr)^{l/2}
                        exp\Biggl(\dfrac{-q^2}{2\zeta}\Biggr)
                        L^{l+1/2}_{n-l} \Biggl(\dfrac{q^2}{\zeta}\Biggr)
                        Y_l^m(\textbf{u}).
                \end{equation}

    Parameters
    ----------
    radial_order : unsigned int,
        an even integer that represent the order of the basis
    zeta : unsigned int,
        scale factor
    gtab : GradientTable,
        gradient directions and bvalues container class
    tau : float,
        diffusion time. By default the value that makes q=sqrt(b).

    References
    ----------
    .. [1] Merlet S. et al., "Continuous diffusion signal, EAP and
    ODF estimation via Compressive Sensing in diffusion MRI", Medical
    Image Analysis, 2013.

    """

    qvals = np.sqrt(gtab.bvals / (4 * np.pi**2 * tau))
    qvals[gtab.b0s_mask] = 0
    bvecs = gtab.bvecs

    qgradients = qvals[:, None] * bvecs

    r, theta, phi = cart2sphere(qgradients[:, 0], qgradients[:, 1],
                                qgradients[:, 2])
    theta[np.isnan(theta)] = 0
    F = radial_order / 2
    n_c = int(np.round(1 / 6.0 * (F + 1) * (F + 2) * (4 * F + 3)))
    M = np.zeros((r.shape[0], n_c))

    counter = 0
    for l in range(0, radial_order + 1, 2):
        for n in range(l, int((radial_order + l) / 2) + 1):
            for m in range(-l, l + 1):
                M[:, counter] = real_sph_harm(m, l, theta, phi) * \
                    genlaguerre(n - l, l + 0.5)(r ** 2 / zeta) * \
                    np.exp(- r ** 2 / (2.0 * zeta)) * \
                    _kappa(zeta, n, l) * \
                    (r ** 2 / zeta) ** (l / 2)
                counter += 1
    return M
예제 #44
0
def multi_shell_fiber_response(sh_order,
                               bvals,
                               wm_rf,
                               gm_rf,
                               csf_rf,
                               sphere=None,
                               tol=20):
    """Fiber response function estimation for multi-shell data.

    Parameters
    ----------
    sh_order : int
         Maximum spherical harmonics order.
    bvals : ndarray
        Array containing the b-values. Must be unique b-values, like outputed
        by `dipy.core.gradients.unique_bvals_tolerance`.
    wm_rf : (4, len(bvals)) ndarray
        Response function of the WM tissue, for each bvals.
    gm_rf : (4, len(bvals)) ndarray
        Response function of the GM tissue, for each bvals.
    csf_rf : (4, len(bvals)) ndarray
        Response function of the CSF tissue, for each bvals.
    sphere : `dipy.core.Sphere` instance, optional
        Sphere where the signal will be evaluated.

    Returns
    -------
    MultiShellResponse
        MultiShellResponse object.
    """
    NUMPY_1_14_PLUS = LooseVersion(np.__version__) >= LooseVersion('1.14.0')
    rcond_value = None if NUMPY_1_14_PLUS else -1

    bvals = np.array(bvals, copy=True)
    evecs = np.zeros((3, 3))
    z = np.array([0, 0, 1.])
    evecs[:, 0] = z
    evecs[:2, 1:] = np.eye(2)

    n = np.arange(0, sh_order + 1, 2)
    m = np.zeros_like(n)

    if sphere is None:
        sphere = default_sphere

    big_sphere = sphere.subdivide()
    theta, phi = big_sphere.theta, big_sphere.phi

    B = shm.real_sph_harm(m, n, theta[:, None], phi[:, None])
    A = shm.real_sph_harm(0, 0, 0, 0)

    response = np.empty([len(bvals), len(n) + 2])

    if bvals[0] < tol:
        gtab = GradientTable(big_sphere.vertices * 0)
        wm_response = single_tensor(gtab,
                                    wm_rf[0, 3],
                                    wm_rf[0, :3],
                                    evecs,
                                    snr=None)
        response[0, 2:] = np.linalg.lstsq(B, wm_response, rcond=rcond_value)[0]

        response[0, 1] = gm_rf[0, 3] / A
        response[0, 0] = csf_rf[0, 3] / A

        for i, bvalue in enumerate(bvals[1:]):
            gtab = GradientTable(big_sphere.vertices * bvalue)
            wm_response = single_tensor(gtab,
                                        wm_rf[i, 3],
                                        wm_rf[i, :3],
                                        evecs,
                                        snr=None)
            response[i + 1, 2:] = np.linalg.lstsq(B,
                                                  wm_response,
                                                  rcond=rcond_value)[0]

            response[i + 1,
                     1] = gm_rf[i, 3] * np.exp(-bvalue * gm_rf[i, 0]) / A
            response[i + 1,
                     0] = csf_rf[i, 3] * np.exp(-bvalue * csf_rf[i, 0]) / A

        S0 = [csf_rf[0, 3], gm_rf[0, 3], wm_rf[0, 3]]

    else:
        warnings.warn("""No b0 given. Proceeding either way.""", UserWarning)
        for i, bvalue in enumerate(bvals):
            gtab = GradientTable(big_sphere.vertices * bvalue)
            wm_response = single_tensor(gtab,
                                        wm_rf[i, 3],
                                        wm_rf[i, :3],
                                        evecs,
                                        snr=None)
            response[i, 2:] = np.linalg.lstsq(B,
                                              wm_response,
                                              rcond=rcond_value)[0]

            response[i, 1] = gm_rf[i, 3] * np.exp(-bvalue * gm_rf[i, 0]) / A
            response[i, 0] = csf_rf[i, 3] * np.exp(-bvalue * csf_rf[i, 0]) / A

        S0 = [csf_rf[0, 3], gm_rf[0, 3], wm_rf[0, 3]]

    return MultiShellResponse(response, sh_order, bvals, S0=S0)
예제 #45
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
예제 #46
0
파일: csdeconv.py 프로젝트: swederik/dipy
    def __init__(self, gtab, ratio, reg_sphere=None, sh_order=8, lambda_=1., tau=0.1):
        r""" Spherical Deconvolution Transform (SDT) [1]_.
        
        The SDT computes a fiber orientation distribution (FOD) as opposed to a diffusion
        ODF as the QballModel or the CsaOdfModel. This results in a sharper angular
        profile with better angular resolution. The Contrained SDTModel is similar
        to the Constrained CSDModel but mathematically it deconvolves the q-ball ODF
        as oppposed to the HARDI signal (see [1]_ for a comparison and a through discussion).
        
        A sharp fODF is obtained because a single fiber *response* function is injected
        as *a priori* knowledge. In the SDTModel, this response is a single fiber q-ball
        ODF as opposed to a single fiber signal function for the CSDModel. The response function
        will be used as deconvolution kernel.

        Parameters
        ----------
        gtab : GradientTable
        ratio : float
            ratio of the smallest vs the largest eigenvalue of the single prolate tensor response function
        reg_sphere : Sphere
            sphere used to build the regularization B matrix
        sh_order : int
            maximal spherical harmonics order
        lambda_ : float
            weight given to the constrained-positivity regularization part of the
            deconvolution equation 
        tau : float
            threshold (tau *mean(fODF)) controlling the amplitude below
            which the corresponding fODF is assumed to be zero.

        References
        ----------
        .. [1] Descoteaux, M., et al. IEEE TMI 2009. Deterministic and Probabilistic Tractography Based
               on Complex Fibre Orientation Distributions.
        """

        m, n = sph_harm_ind_list(sh_order)
        self.m, self.n = m, n
        self._where_b0s = lazy_index(gtab.b0s_mask)
        self._where_dwi = lazy_index(~gtab.b0s_mask)

        no_params = ((sh_order + 1) * (sh_order + 2)) / 2

        if no_params > np.sum(gtab.b0s_mask == False):
            msg = "Number of parameters required for the fit are more "
            msg += "than the actual data points"
            warnings.warn(msg, UserWarning)

        x, y, z = gtab.gradients[self._where_dwi].T
        r, theta, phi = cart2sphere(x, y, z)
        # for the gradient sphere
        self.B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])

        # for the odf sphere
        if reg_sphere is None:
            self.sphere = get_sphere('symmetric362')
        else:
            self.sphere = reg_sphere

        r, theta, phi = cart2sphere(self.sphere.x, self.sphere.y, self.sphere.z)
        self.B_reg = real_sph_harm(m, n, theta[:, None], phi[:, None])

        self.R, self.P = forward_sdt_deconv_mat(ratio, sh_order)

        # scale lambda_ to account for differences in the number of
        # SH coefficients and number of mapped directions
        self.lambda_ = lambda_ * self.R.shape[0] * self.R[0, 0] / self.B_reg.shape[0]
        self.tau = tau
        self.sh_order = sh_order
예제 #47
0
 def basis(self, sphere):
     """A basis that maps the response coefficients onto a sphere."""
     theta = sphere.theta[:, None]
     phi = sphere.phi[:, None]
     return real_sph_harm(self.m, self.n, theta, phi)
예제 #48
0
파일: shore.py 프로젝트: conorkcorbin/dipy
def shore_matrix(radial_order, zeta, gtab, tau=1 / (4 * np.pi ** 2)):
    r"""Compute the SHORE matrix for modified Merlet's 3D-SHORE [1]_

    ..math::
            :nowrap:
                \begin{equation}
                    \textbf{E}(q\textbf{u})=\sum_{l=0, even}^{N_{max}}
                                            \sum_{n=l}^{(N_{max}+l)/2}
                                            \sum_{m=-l}^l c_{nlm}
                                            \phi_{nlm}(q\textbf{u})
                \end{equation}

    where $\phi_{nlm}$ is
    ..math::
            :nowrap:
                \begin{equation}
                    \phi_{nlm}^{SHORE}(q\textbf{u})=\Biggl[\dfrac{2(n-l)!}
                        {\zeta^{3/2} \Gamma(n+3/2)} \Biggr]^{1/2}
                        \Biggl(\dfrac{q^2}{\zeta}\Biggr)^{l/2}
                        exp\Biggl(\dfrac{-q^2}{2\zeta}\Biggr)
                        L^{l+1/2}_{n-l} \Biggl(\dfrac{q^2}{\zeta}\Biggr)
                        Y_l^m(\textbf{u}).
                \end{equation}

    Parameters
    ----------
    radial_order : unsigned int,
        an even integer that represent the order of the basis
    zeta : unsigned int,
        scale factor
    gtab : GradientTable,
        gradient directions and bvalues container class
    tau : float,
        diffusion time. By default the value that makes q=sqrt(b).

    References
    ----------
    .. [1] Merlet S. et. al, "Continuous diffusion signal, EAP and
    ODF estimation via Compressive Sensing in diffusion MRI", Medical
    Image Analysis, 2013.

    """

    qvals = np.sqrt(gtab.bvals / (4 * np.pi ** 2 * tau))
    qvals[gtab.b0s_mask] = 0
    bvecs = gtab.bvecs

    qgradients = qvals[:, None] * bvecs

    r, theta, phi = cart2sphere(qgradients[:, 0], qgradients[:, 1],
                                qgradients[:, 2])
    theta[np.isnan(theta)] = 0
    F = radial_order / 2
    n_c = int(np.round(1 / 6.0 * (F + 1) * (F + 2) * (4 * F + 3)))
    M = np.zeros((r.shape[0], n_c))

    counter = 0
    for l in range(0, radial_order + 1, 2):
        for n in range(l, int((radial_order + l) / 2) + 1):
            for m in range(-l, l + 1):
                M[:, counter] = real_sph_harm(m, l, theta, phi) * \
                    genlaguerre(n - l, l + 0.5)(r ** 2 / zeta) * \
                    np.exp(- r ** 2 / (2.0 * zeta)) * \
                    _kappa(zeta, n, l) * \
                    (r ** 2 / zeta) ** (l / 2)
                counter += 1
    return M
예제 #49
0
파일: csdeconv.py 프로젝트: swederik/dipy
    def __init__(self, gtab, response, reg_sphere=None, sh_order=8, lambda_=1, tau=0.1):
        r""" Constrained Spherical Deconvolution (CSD) [1]_.

        Spherical deconvolution computes a fiber orientation distribution (FOD), also
        called fiber ODF (fODF) [2]_, as opposed to a diffusion ODF as the QballModel
        or the CsaOdfModel. This results in a sharper angular profile with better
        angular resolution that is the best object to be used for later deterministic
        and probabilistic tractography [3]_.

        A sharp fODF is obtained because a single fiber *response* function is injected
        as *a priori* knowledge. The response function is often data-driven and thus,
        comes as input to the ConstrainedSphericalDeconvModel. It will be used as deconvolution
        kernel, as described in [1]_.
    
        Parameters
        ----------
        gtab : GradientTable
        response : tuple or callable
            If tuple, then it should have 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. If callable then the function
            should return an ndarray with the all the signal values for the response function. The response
            function will be used as deconvolution kernel ([1]_)
        reg_sphere : Sphere
            sphere used to build the regularization B matrix
        sh_order : int
            maximal spherical harmonics order
        lambda_ : float
            weight given to the constrained-positivity regularization part of the
            deconvolution equation (see [1]_)
        tau : float
            threshold controlling the amplitude below which the corresponding fODF is assumed to be zero.
            Ideally, tau should be set to zero. However, to improve the stability of the algorithm, tau
            is set to tau*100 % of the mean fODF amplitude (here, 10% by default) (see [1]_)

        References
        ----------
        .. [1] Tournier, J.D., et al. NeuroImage 2007. Robust determination of the fibre orientation
               distribution in diffusion MRI: Non-negativity constrained super-resolved spherical
               deconvolution
        .. [2] Descoteaux, M., et al. IEEE TMI 2009. Deterministic and Probabilistic Tractography Based
               on Complex Fibre Orientation Distributions
        .. [3] C\^ot\'e, M-A., et al. Medical Image Analysis 2013. Tractometer: Towards validation
               of tractography pipelines
        .. [4] Tournier, J.D, et al. Imaging Systems and Technology 2012. MRtrix: Diffusion
               Tractography in Crossing Fiber Regions
        """

        m, n = sph_harm_ind_list(sh_order)
        self.m, self.n = m, n
        self._where_b0s = lazy_index(gtab.b0s_mask)
        self._where_dwi = lazy_index(~gtab.b0s_mask)

        no_params = ((sh_order + 1) * (sh_order + 2)) / 2

        if no_params > np.sum(gtab.b0s_mask == False):
            msg = "Number of parameters required for the fit are more "
            msg += "than the actual data points"
            warnings.warn(msg, UserWarning)

        x, y, z = gtab.gradients[self._where_dwi].T
        r, theta, phi = cart2sphere(x, y, z)
        # for the gradient sphere
        self.B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])

        # for the sphere used in the regularization positivity constraint
        if reg_sphere is None:
            self.sphere = get_sphere('symmetric362')
        else:
            self.sphere = reg_sphere

        r, theta, phi = cart2sphere(self.sphere.x, self.sphere.y, self.sphere.z)
        self.B_reg = real_sph_harm(m, n, theta[:, None], phi[:, None])

        if callable(response):
            S_r = response
        else:
            if response is None:
                S_r = estimate_response(gtab, np.array([0.0015, 0.0003, 0.0003]), 1)
            else:
                S_r = estimate_response(gtab, response[0], response[1])

        r_sh = np.linalg.lstsq(self.B_dwi, S_r[self._where_dwi])[0]
        r_rh = sh_to_rh(r_sh, sh_order)

        self.R = forward_sdeconv_mat(r_rh, sh_order)

        # scale lambda_ to account for differences in the number of
        # SH coefficients and number of mapped directions
        # This is exactly what is done in [4]_ 
        self.lambda_ = lambda_ * self.R.shape[0] * r_rh[0] / self.B_reg.shape[0]
        self.sh_order = sh_order
        self.tau = tau
예제 #50
0
파일: smoothing.py 프로젝트: luzpaz/nlsam
def sh_smooth(data,
              bvals,
              bvecs,
              sh_order=4,
              similarity_threshold=50,
              regul=0.006):
    """Smooth the raw diffusion signal with spherical harmonics.
    data : ndarray
        The diffusion data to smooth.
    gtab : gradient table object
        Corresponding gradients table object to data.
    sh_order : int, default 8
        Order of the spherical harmonics to fit.
    similarity_threshold : int, default 50
        All b-values such that |b_1 - b_2| < similarity_threshold
        will be considered as identical for smoothing purpose.
        Must be lower than 200.
    regul : float, default 0.006
        Amount of regularization to apply to sh coefficients computation.
    Return
    ---------
    pred_sig : ndarray
        The smoothed diffusion data, fitted through spherical harmonics.
    """

    if similarity_threshold > 200:
        raise ValueError(
            "similarity_threshold = {}, which is higher than 200,"
            " please use a lower value".format(similarity_threshold))

    m, n = sph_harm_ind_list(sh_order)
    L = -n * (n + 1)
    where_b0s = bvals == 0
    pred_sig = np.zeros_like(data, dtype=np.float32)

    # Round similar bvals together for identifying similar shells
    rounded_bvals = np.zeros_like(bvals)

    for unique_bval in np.unique(bvals):
        idx = np.abs(unique_bval - bvals) < similarity_threshold
        rounded_bvals[idx] = unique_bval

    # process each b-value separately
    for unique_bval in np.unique(rounded_bvals):
        idx = rounded_bvals == unique_bval

        # Just give back the signal for the b0s since we can't really do anything about it
        if np.all(idx == where_b0s):
            if np.sum(where_b0s) > 1:
                pred_sig[..., idx] = np.mean(data[..., idx],
                                             axis=-1,
                                             keepdims=True)
            else:
                pred_sig[..., idx] = data[..., idx]
            continue

        x, y, z = bvecs[:, idx]
        r, theta, phi = cart2sphere(x, y, z)

        # Find the sh coefficients to smooth the signal
        B_dwi = real_sph_harm(m, n, theta[:, None], phi[:, None])
        invB = smooth_pinv(B_dwi, np.sqrt(regul) * L)
        sh_coeff = np.dot(data[..., idx], invB.T)

        # Find the smoothed signal from the sh fit for the given gtab
        pred_sig[..., idx] = np.dot(sh_coeff, B_dwi.T)

    return pred_sig