Ejemplo n.º 1
0
def test_multi_tensor():
    sphere = get_sphere('symmetric724')
    vertices = sphere.vertices
    mevals = np.array(([0.0015, 0.0003, 0.0003],
                       [0.0015, 0.0003, 0.0003]))
    e0 = np.array([np.sqrt(2) / 2., np.sqrt(2) / 2., 0])
    e1 = np.array([0, np.sqrt(2) / 2., np.sqrt(2) / 2.])
    mevecs = [all_tensor_evecs(e0), all_tensor_evecs(e1)]
    # odf = multi_tensor_odf(vertices, [0.5, 0.5], mevals, mevecs)
    # assert_(odf.shape == (len(vertices),))
    # assert_(np.all(odf <= 1) & np.all(odf >= 0))

    fimg, fbvals, fbvecs = get_data('small_101D')
    bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs)
    gtab = gradient_table(bvals, bvecs)

    s1 = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None)
    s2 = single_tensor(gtab, 100, mevals[1], mevecs[1], snr=None)

    Ssingle = 0.5*s1 + 0.5*s2

    S, sticks = MultiTensor(gtab, mevals, S0=100, angles=[(90, 45), (45, 90)],
                            fractions=[50, 50], snr=None)

    assert_array_almost_equal(S, Ssingle)
Ejemplo n.º 2
0
def test_r2_term_odf_sharp():
    SNR = None
    S0 = 1
    angle = 75

    _, fbvals, fbvecs = get_data('small_64D')  #get_data('small_64D')

    bvals = np.load(fbvals)
    bvecs = np.load(fbvecs)

    sphere = get_sphere('symmetric724')
    gtab = gradient_table(bvals, bvecs)
    mevals = np.array(([0.0015, 0.0003, 0.0003],
                       [0.0015, 0.0003, 0.0003]))

    S, sticks = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (angle, 0)],
                             fractions=[50, 50], snr=SNR)
    
    
    mevecs = [all_tensor_evecs(sticks[0]).T,
              all_tensor_evecs(sticks[1]).T]

    odf_gt = multi_tensor_odf(sphere.vertices, [0.5, 0.5], mevals, mevecs)
    odfs_sh = sf_to_sh(odf_gt, sphere, sh_order=8, basis_type=None)
    fodf_sh = odf_sh_to_sharp(odfs_sh, sphere, basis=None, ratio=3 / 15.,
                              sh_order=8, lambda_=1., tau=0.1, r2_term=True)
    fodf = sh_to_sf(fodf_sh, sphere, sh_order=8, basis_type=None)

    directions_gt, _, _ = peak_directions(odf_gt, sphere)
    directions, _, _ = peak_directions(fodf, sphere)

    ang_sim = angular_similarity(directions_gt, directions)
    assert_equal(ang_sim > 1.9, True)
    assert_equal(directions.shape[0], 2)
Ejemplo n.º 3
0
def test_peak_finding():

    vertices, faces=get_sphere('symmetric724')
    odf=np.zeros(len(vertices))
    odf = np.abs(vertices.sum(-1))

    odf[1] = 10.
    odf[505] = 505.
    odf[143] = 143.

    peaks, inds=peak_finding(odf.astype('f8'), faces.astype('uint16'))
    print peaks, inds
    edges = unique_edges(faces)
    peaks, inds = local_maxima(odf, edges)
    print peaks, inds
    vertices_half, edges_half, faces_half = reduce_antipodal(vertices, faces)
    n = len(vertices_half)
    peaks, inds = local_maxima(odf[:n], edges_half)
    print peaks, inds
    mevals=np.array(([0.0015,0.0003,0.0003],
                    [0.0015,0.0003,0.0003]))
    e0=np.array([1,0,0.])
    e1=np.array([0.,1,0])
    mevecs=[all_tensor_evecs(e0),all_tensor_evecs(e1)]
    odf = multi_tensor_odf(vertices, [0.5,0.5], mevals, mevecs)
    peaks, inds=peak_finding(odf, faces)
    print peaks, inds
    peaks2, inds2 = local_maxima(odf[:n], edges_half)
    print peaks2, inds2
    assert_equal(len(peaks), 2)
    assert_equal(len(peaks2), 2)
Ejemplo n.º 4
0
def test_multi_tensor():
    vertices, faces = get_sphere('symmetric724')
    mevals=np.array(([0.0015, 0.0003, 0.0003],
                     [0.0015, 0.0003, 0.0003]))
    e0 = np.array([1, 0, 0.])
    e1 = np.array([0., 1, 0])
    mevecs=[all_tensor_evecs(e0), all_tensor_evecs(e1)]
    odf = multi_tensor_odf(vertices, [0.5,0.5], mevals, mevecs)

    assert odf.shape == (len(vertices),)
    assert np.all(odf <= 1) & np.all(odf >= 0)
Ejemplo n.º 5
0
def test_odfdeconv():
    SNR = 100
    S0 = 1

    _, fbvals, fbvecs = get_data('small_64D')

    bvals = np.load(fbvals)
    bvecs = np.load(fbvecs)

    gtab = gradient_table(bvals, bvecs)
    mevals = np.array(([0.0015, 0.0003, 0.0003],
                       [0.0015, 0.0003, 0.0003]))

    S, sticks = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (90, 0)],
                             fractions=[50, 50], snr=SNR)

    sphere = get_sphere('symmetric724')

    mevecs = [all_tensor_evecs(sticks[0]).T,
              all_tensor_evecs(sticks[1]).T]

    odf_gt = multi_tensor_odf(sphere.vertices, [0.5, 0.5], mevals, mevecs)

    e1 = 15.0
    e2 = 3.0
    ratio = e2 / e1

    csd = ConstrainedSDTModel(gtab, ratio, None)

    csd_fit = csd.fit(S)
    fodf = csd_fit.odf(sphere)

    directions, _, _ = peak_directions(odf_gt, sphere)
    directions2, _, _ = peak_directions(fodf, sphere)

    ang_sim = angular_similarity(directions, directions2)

    assert_equal(ang_sim > 1.9, True)

    assert_equal(directions.shape[0], 2)
    assert_equal(directions2.shape[0], 2)

    with warnings.catch_warnings(record=True) as w:

        ConstrainedSDTModel(gtab, ratio, sh_order=10)
        assert_equal(len(w) > 0, True)

    with warnings.catch_warnings(record=True) as w:

        ConstrainedSDTModel(gtab, ratio, sh_order=8)
        assert_equal(len(w) > 0, False)
Ejemplo n.º 6
0
def sim_tensor_2x(gtab, angle=90, sphere=None, S0=1., snr=None):

    mevals = np.array(([0.0015, 0.0003, 0.0003],
                       [0.0015, 0.0003, 0.0003]))

    data, sticks = multi_tensor(gtab, mevals, S0,
                                angles=[(90 , 0), (90, angle)],
                                fractions=[50, 50], snr=snr)

    mevecs = [all_tensor_evecs(sticks[0]).T,
              all_tensor_evecs(sticks[1]).T]

    odf_gt = multi_tensor_odf(sphere.vertices, [0.5, 0.5], mevals, mevecs)

    return data, sticks, odf_gt
Ejemplo n.º 7
0
def test_fwdti_predictions():
    # single voxel case
    gtf = 0.50  # ground truth volume fraction
    angles = [(90, 0), (90, 0)]
    mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]])
    S_conta, peaks = multi_tensor(gtab_2s, mevals, S0=100,
                                  angles=angles,
                                  fractions=[(1-gtf) * 100, gtf*100], snr=None)
    R = all_tensor_evecs(peaks[0])
    R = R.reshape((9))
    model_params = np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]),
                                  axis=0)
    S_pred1 = fwdti_prediction(model_params, gtab_2s, S0=100)
    assert_array_almost_equal(S_pred1, S_conta)

    # Testing in model class
    fwdm = fwdti.FreeWaterTensorModel(gtab_2s)
    S_pred2 = fwdm.predict(model_params, S0=100)
    assert_array_almost_equal(S_pred2, S_conta)

    # Testing in fit class
    fwefit = fwdm.fit(S_conta)
    S_pred3 = fwefit.predict(gtab_2s, S0=100)
    assert_array_almost_equal(S_pred3, S_conta, decimal=5)

    # Multi voxel simulation
    S_pred1 = fwdti_prediction(model_params_mv, gtab_2s, S0=100)  # function
    assert_array_almost_equal(S_pred1, DWI)
    S_pred2 = fwdm.predict(model_params_mv, S0=100)  # Model class
    assert_array_almost_equal(S_pred2, DWI)
    fwefit = fwdm.fit(DWI)  # Fit class
    S_pred3 = fwefit.predict(gtab_2s, S0=100)
    assert_array_almost_equal(S_pred3, DWI)
Ejemplo n.º 8
0
def test_all_tensor_evecs():
    e0 = np.array([1/np.sqrt(2), 1/np.sqrt(2), 0])

    desired = np.array([[1/np.sqrt(2), 1/np.sqrt(2), 0],
                        [-1/np.sqrt(2), 1/np.sqrt(2), 0],
                        [0, 0, 1]]).T

    assert_array_almost_equal(all_tensor_evecs(e0), desired)
Ejemplo n.º 9
0
def test_all_tensor_evecs():
    e0 = np.array([1/np.sqrt(2), 1/np.sqrt(2), 0])

    # Vectors are returned column-wise!
    desired = np.array([[1/np.sqrt(2), 1/np.sqrt(2), 0],
                        [-1/np.sqrt(2), 1/np.sqrt(2), 0],
                        [0, 0, 1]]).T

    assert_array_almost_equal(all_tensor_evecs(e0), desired)
Ejemplo n.º 10
0
def setup_module():
    """Module-level setup"""
    global gtab, gtab_2s, mevals, model_params_mv
    global DWI, FAref, GTF, MDref, FAdti, MDdti
    _, fbvals, fbvecs = get_fnames('small_64D')
    bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs)
    gtab = gradient_table(bvals, bvecs)

    # FW model requires multishell data
    bvals_2s = np.concatenate((bvals, bvals * 1.5), axis=0)
    bvecs_2s = np.concatenate((bvecs, bvecs), axis=0)
    gtab_2s = gradient_table(bvals_2s, bvecs_2s)

    # Simulation a typical DT and DW signal for no water contamination
    S0 = np.array(100)
    dt = np.array([0.0017, 0, 0.0003, 0, 0, 0.0003])
    evals, evecs = decompose_tensor(from_lower_triangular(dt))
    S_tissue = single_tensor(gtab_2s, S0=100, evals=evals, evecs=evecs,
                             snr=None)
    dm = dti.TensorModel(gtab_2s, 'WLS')
    dtifit = dm.fit(S_tissue)
    FAdti = dtifit.fa
    MDdti = dtifit.md
    dtiparams = dtifit.model_params

    # Simulation of 8 voxels tested
    DWI = np.zeros((2, 2, 2, len(gtab_2s.bvals)))
    FAref = np.zeros((2, 2, 2))
    MDref = np.zeros((2, 2, 2))
    # Diffusion of tissue and water compartments are constant for all voxel
    mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]])
    # volume fractions
    GTF = np.array([[[0.06, 0.71], [0.33, 0.91]],
                    [[0., 0.], [0., 0.]]])
    # S0 multivoxel
    S0m = 100 * np.ones((2, 2, 2))
    # model_params ground truth (to be fill)
    model_params_mv = np.zeros((2, 2, 2, 13))
    for i in range(2):
        for j in range(2):
            gtf = GTF[0, i, j]
            S, p = multi_tensor(gtab_2s, mevals, S0=100,
                                angles=[(90, 0), (90, 0)],
                                fractions=[(1-gtf) * 100, gtf*100], snr=None)
            DWI[0, i, j] = S
            FAref[0, i, j] = FAdti
            MDref[0, i, j] = MDdti
            R = all_tensor_evecs(p[0])
            R = R.reshape((9))
            model_params_mv[0, i, j] = \
                np.concatenate(([0.0017, 0.0003, 0.0003], R, [gtf]), axis=0)
Ejemplo n.º 11
0
def test_shore_metrics():
    fetch_taiwan_ntu_dsi()
    img, gtab = read_taiwan_ntu_dsi()

    mevals = np.array(([0.0015, 0.0003, 0.0003],
                       [0.0015, 0.0003, 0.0003]))
    angl = [(0, 0), (60, 0)]
    S, sticks = MultiTensor(gtab, mevals, S0=100, angles=angl,
                            fractions=[50, 50], snr=None)
    S = S / S[0, None].astype(np.float)

    radial_order = 8
    zeta = 800
    lambdaN = 1e-12
    lambdaL = 1e-12
    asm = ShoreModel(gtab, radial_order=radial_order, zeta=zeta, lambdaN=lambdaN, lambdaL=lambdaL)
    asmfit = asm.fit(S)
    c_shore= asmfit.shore_coeff

    cmat = SHOREmatrix(radial_order, zeta, gtab)
    S_reconst = np.dot(cmat, c_shore)
    nmse_signal = np.sqrt(np.sum((S - S_reconst) ** 2)) / (S.sum())
    assert_almost_equal(nmse_signal, 0.0, 4)

    mevecs2 = np.zeros((2, 3, 3))
    angl = np.array(angl)
    for i in range(2):
        mevecs2[i] = all_tensor_evecs(sticks[i]).T

    sphere = get_sphere('symmetric724')
    v = sphere.vertices
    radius = 10e-3
    pdf_shore = asmfit.pdf(v * radius)
    pdf_mt = multi_tensor_pdf(v * radius, [.5, .5], mevals=mevals, mevecs=mevecs2)
    nmse_pdf = np.sqrt(np.sum((pdf_mt - pdf_shore) ** 2)) / (pdf_mt.sum())
    assert_almost_equal(nmse_pdf, 0.0, 2)

    rtop_shore_signal = asmfit.rtop_signal()
    rtop_shore_pdf = asmfit.rtop_pdf()
    assert_almost_equal(rtop_shore_signal, rtop_shore_pdf, 9)
    rtop_mt = multi_tensor_rtop([.5, .5], mevals=mevals)
    assert_equal(rtop_mt/rtop_shore_signal < 1.12 and rtop_mt/rtop_shore_signal > 0.9 , True)
    
    msd_mt = multi_tensor_msd([.5, .5], mevals=mevals)
    msd_shore = asmfit.msd()
    assert_equal(msd_mt/msd_shore < 1.05 and msd_mt/msd_shore > 0.95 , True)
Ejemplo n.º 12
0
def test_csdeconv():
    SNR = 100
    S0 = 1

    _, fbvals, fbvecs = get_data('small_64D')

    bvals = np.load(fbvals)
    bvecs = np.load(fbvecs)

    gtab = gradient_table(bvals, bvecs)
    mevals = np.array(([0.0015, 0.0003, 0.0003],
                       [0.0015, 0.0003, 0.0003]))

    angles = [(0, 0), (60, 0)]

    S, sticks = multi_tensor(gtab, mevals, S0, angles=angles,
                             fractions=[50, 50], snr=SNR)

    sphere = get_sphere('symmetric362')

    odf_gt = multi_tensor_odf(sphere.vertices, mevals, angles, [50, 50])

    response = (np.array([0.0015, 0.0003, 0.0003]), S0)

    csd = ConstrainedSphericalDeconvModel(gtab, response)

    csd_fit = csd.fit(S)

    assert_equal(csd_fit.shm_coeff[0] > 0, True)

    fodf = csd_fit.odf(sphere)

    directions, _, _ = peak_directions(odf_gt, sphere)
    directions2, _, _ = peak_directions(fodf, sphere)

    ang_sim = angular_similarity(directions, directions2)

    assert_equal(ang_sim > 1.9, True)
    assert_equal(directions.shape[0], 2)
    assert_equal(directions2.shape[0], 2)

    with warnings.catch_warnings(record=True) as w:

        ConstrainedSphericalDeconvModel(gtab, response, sh_order=10)
        assert_equal(len(w) > 0, True)

    with warnings.catch_warnings(record=True) as w:

        ConstrainedSphericalDeconvModel(gtab, response, sh_order=8)
        assert_equal(len(w) > 0, False)

    mevecs = []
    for s in sticks:
        mevecs += [all_tensor_evecs(s).T]

    S2 = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None)
    big_S = np.zeros((10, 10, 10, len(S2)))
    big_S[:] = S2

    aresponse, aratio = auto_response(gtab, big_S, roi_center=(5, 5, 4), roi_radius=3, fa_thr=0.5)
    assert_array_almost_equal(aresponse[0], response[0])
    assert_almost_equal(aresponse[1], 100)
    assert_almost_equal(aratio, response[0][1]/response[0][0])

    aresponse2, aratio2 = auto_response(gtab, big_S, roi_radius=3, fa_thr=0.5)
    assert_array_almost_equal(aresponse[0], response[0])
Ejemplo n.º 13
0
GTF = np.array([[[0.06, 0.71], [0.33, 0.91]],
                [[0., 0.], [0., 0.]]])
# S0 multivoxel
S0m = 100 * np.ones((2, 2, 2))
# model_params ground truth (to be fill)
model_params_mv = np.zeros((2, 2, 2, 13))
for i in range(2):
    for j in range(2):
        gtf = GTF[0, i, j]
        S, p = multi_tensor(gtab_2s, mevals, S0=100,
                            angles=[(90, 0), (90, 0)],
                            fractions=[(1-gtf) * 100, gtf*100], snr=None)
        DWI[0, i, j] = S
        FAref[0, i, j] = FAdti
        MDref[0, i, j] = MDdti
        R = all_tensor_evecs(p[0])
        R = R.reshape((9))
        model_params_mv[0, i, j] = np.concatenate(([0.0017, 0.0003, 0.0003],
                                                   R, [gtf]), axis=0)


def test_fwdti_singlevoxel():
    # Simulation when water contamination is added
    gtf = 0.44444  # ground truth volume fraction
    mevals = np.array([[0.0017, 0.0003, 0.0003], [0.003, 0.003, 0.003]])
    S_conta, peaks = multi_tensor(gtab_2s, mevals, S0=100,
                                  angles=[(90, 0), (90, 0)],
                                  fractions=[(1-gtf) * 100, gtf*100], snr=None)
    fwdm = fwdti.FreeWaterTensorModel(gtab_2s, 'WLS')
    fwefit = fwdm.fit(S_conta)
    FAfwe = fwefit.fa
Ejemplo n.º 14
0
def test_kurtosis_elements():
    """ Testing symmetry of the elements of the KT

    As an 4th order tensor, KT has 81 elements. However, due to diffusion
    symmetry the KT is fully characterized by 15 independent elements. This
    test checks for this property.
    """
    # two fiber not aligned to planes x = 0, y = 0, or z = 0
    mevals = np.array([[0.00099, 0, 0], [0.00226, 0.00087, 0.00087],
                       [0.00099, 0, 0], [0.00226, 0.00087, 0.00087]])
    angles = [(80, 10), (80, 10), (20, 30), (20, 30)]
    fie = 0.49  # intra axonal water fraction
    frac = [fie * 50, (1-fie) * 50, fie * 50, (1-fie) * 50]
    sticks = _check_directions(angles)
    mD = np.zeros((len(frac), 3, 3))
    for i in range(len(frac)):
        R = all_tensor_evecs(sticks[i])
        mD[i] = np.dot(np.dot(R, np.diag(mevals[i])), R.T)

    # compute global DT
    D = np.zeros((3, 3))
    for i in range(len(frac)):
        D = D + frac[i]*mD[i]

    # compute voxel's MD
    MD = (D[0][0] + D[1][1] + D[2][2]) / 3

    # Reference dictionary with the 15 independent elements.
    # Note: The multiplication of the indexes (i+1) * (j+1) * (k+1) * (l+1)
    # for of an elements is only equal to this multiplication for another
    # element if an only if the element corresponds to an symmetry element.
    # Thus indexes multiplication is used as key of the reference dictionary
    kt_ref = {1: kurtosis_element(mD, frac, 0, 0, 0, 0),
              16: kurtosis_element(mD, frac, 1, 1, 1, 1),
              81: kurtosis_element(mD, frac, 2, 2, 2, 2),
              2: kurtosis_element(mD, frac, 0, 0, 0, 1),
              3: kurtosis_element(mD, frac, 0, 0, 0, 2),
              8: kurtosis_element(mD, frac, 0, 1, 1, 1),
              24: kurtosis_element(mD, frac, 1, 1, 1, 2),
              27: kurtosis_element(mD, frac, 0, 2, 2, 2),
              54: kurtosis_element(mD, frac, 1, 2, 2, 2),
              4: kurtosis_element(mD, frac, 0, 0, 1, 1),
              9: kurtosis_element(mD, frac, 0, 0, 2, 2),
              36: kurtosis_element(mD, frac, 1, 1, 2, 2),
              6: kurtosis_element(mD, frac, 0, 0, 1, 2),
              12: kurtosis_element(mD, frac, 0, 1, 1, 2),
              18: kurtosis_element(mD, frac, 0, 1, 2, 2)}

    # Testing all 81 possible elements
    xyz = [0, 1, 2]
    for i in xyz:
        for j in xyz:
            for k in xyz:
                for l in xyz:
                    key = (i+1) * (j+1) * (k+1) * (l+1)
                    assert_almost_equal(kurtosis_element(mD, frac, i, k, j, l),
                                        kt_ref[key])
                    # Testing optional funtion inputs
                    assert_almost_equal(kurtosis_element(mD, frac, i, k, j, l),
                                        kurtosis_element(mD, frac, i, k, j, l,
                                                         D, MD))
Ejemplo n.º 15
0
def sfm_design_matrix(gtab, sphere, response, mode='signal'):
    """
    Construct the SFM design matrix

    Parameters
    ----------
    gtab : GradientTable or Sphere
        Sets the rows of the matrix, if the mode is 'signal', this should be a
        GradientTable. If mode is 'odf' this should be a Sphere
    sphere : Sphere
        Sets the columns of the matrix
    response : list of 3 elements
        The eigenvalues of a tensor which will serve as a kernel
        function.
    mode : str {'signal' | 'odf'}, optional
        Choose the (default) 'signal' for a design matrix containing predicted
        signal in the measurements defined by the gradient table for putative
        fascicles oriented along the vertices of the sphere. Otherwise, choose
        'odf' for an odf convolution matrix, with values of the odf calculated
        from a tensor with the provided response eigenvalues, evaluated at the
        b-vectors in the gradient table, for the tensors with prinicipal
        diffusion directions along the vertices of the sphere.

    Returns
    -------
    mat : ndarray
        A design matrix that can be used for one of the following operations:
        when the 'signal' mode is used, each column contains the putative
        signal in each of the bvectors of the `gtab` if a fascicle is oriented
        in the direction encoded by the sphere vertex corresponding to this
        column. This is used for deconvolution with a measured DWI signal. If
        the 'odf' mode is chosen, each column instead contains the values of
        the tensor ODF for a tensor with a principal diffusion direction
        corresponding to this vertex. This is used to generate odfs from the
        fits of the SFM for the purpose of tracking.

    Examples
    --------
    >>> import dipy.data as dpd
    >>> data, gtab = dpd.dsi_voxels()
    >>> sphere = dpd.get_sphere()
    >>> from dipy.reconst.sfm import sfm_design_matrix

    A canonical tensor approximating corpus-callosum voxels [Rokem2014]_:

    >>> tensor_matrix = sfm_design_matrix(gtab, sphere, [0.0015, 0.0005, 0.0005])

    A 'stick' function ([Behrens2007]_):

    >>> stick_matrix = sfm_design_matrix(gtab, sphere, [0.001, 0, 0])

    Notes
    -----
    .. [Rokem2014a] Ariel Rokem, Jason D. Yeatman, Franco Pestilli, Kendrick
       N. Kay, Aviv Mezer, Stefan van der Walt, Brian A. Wandell
       (2014). Evaluating the accuracy of diffusion MRI models in white
       matter. http://arxiv.org/abs/1411.0721

    .. [Rokem2014b] Ariel Rokem, Kimberly L. Chan, Jason D. Yeatman, Franco
       Pestilli,  Brian A. Wandell (2014). Evaluating the accuracy of diffusion
       models at multiple b-values with cross-validation. ISMRM 2014.

    .. [Behrens2007] Behrens TEJ, Berg HJ, Jbabdi S, Rushworth MFS, Woolrich MW
       (2007): Probabilistic diffusion tractography with multiple fibre
       orientations: What can we gain? Neuroimage 34:144-55.
    """
    if mode == 'signal':
        mat_gtab = grad.gradient_table(gtab.bvals[~gtab.b0s_mask],
                                       gtab.bvecs[~gtab.b0s_mask])
        # Preallocate:
        mat = np.empty((np.sum(~gtab.b0s_mask),
                        sphere.vertices.shape[0]))
    elif mode == 'odf':
        mat = np.empty((gtab.x.shape[0], sphere.vertices.shape[0]))

    # Calculate column-wise:
    for ii, this_dir in enumerate(sphere.vertices):
        # Rotate the canonical tensor towards this vertex and calculate the
        # signal you would have gotten in the direction
        evecs = sims.all_tensor_evecs(this_dir)
        if mode == 'signal':
            sig = sims.single_tensor(mat_gtab, evals=response, evecs=evecs)
            # For regressors based on the single tensor, remove $e^{-bD}$
            iso_sig = np.exp(-mat_gtab.bvals * np.mean(response))
            mat[:, ii] = sig - iso_sig
        elif mode == 'odf':
            # Stick function
            if response[1] == 0 or response[2] == 0:
                jj = sphere.find_closest(evecs[0])
                mat[jj, ii] = 1
            else:
                odf = sims.single_tensor_odf(gtab.vertices,
                                             evals=response, evecs=evecs)
                mat[:, ii] = odf
    return mat