|
7 | 7 | import numpy as np
|
8 | 8 |
|
9 | 9 | from fury import layout as lyt
|
| 10 | +from fury.actors.odf import sh_odf |
10 | 11 | from fury.actors.odf_slicer import OdfSlicerActor
|
11 | 12 | from fury.actors.peak import PeakActor
|
12 | 13 | from fury.actors.tensor import (
|
@@ -4019,3 +4020,67 @@ def uncertainty_cone(
|
4019 | 4020 | angles = main_dir_uncertainty(evals, evecs, signal, sigma, b_matrix)
|
4020 | 4021 |
|
4021 | 4022 | return double_cone(centers, evecs, angles, colors, scales, opacity)
|
| 4023 | + |
| 4024 | + |
| 4025 | +def odf(centers, coeffs, sh_basis="descoteaux", scales=1.0, opacity=1.0): |
| 4026 | + """ |
| 4027 | + FURY actor for visualizing Orientation Distribution Functions (ODFs) given |
| 4028 | + an array of Spherical Harmonics (SH) coefficients. |
| 4029 | +
|
| 4030 | + Parameters |
| 4031 | + ---------- |
| 4032 | + centers : ndarray(N, 3) |
| 4033 | + ODFs positions. |
| 4034 | + coeffs : (N, M) or (N, 6) or (N, 15) or (N, 28) or (N, 45) or (N, 66) or |
| 4035 | + (N, 91) ndarray. |
| 4036 | + Corresponding SH coefficients for the ODFs. |
| 4037 | + sh_basis: str, optional |
| 4038 | + Type of basis (descoteaux, tournier) |
| 4039 | + 'descoteaux' for the default ``descoteaux07`` DIPY basis. |
| 4040 | + 'tournier' for the default ``tournier07`` DIPY basis. |
| 4041 | + scales : float or ndarray (N, ), optional |
| 4042 | + ODFs size. |
| 4043 | + opacity : float, optional |
| 4044 | + Takes values from 0 (fully transparent) to 1 (opaque). |
| 4045 | +
|
| 4046 | + Returns |
| 4047 | + ------- |
| 4048 | + odf: Actor |
| 4049 | +
|
| 4050 | + """ |
| 4051 | + |
| 4052 | + if not isinstance(centers, np.ndarray): |
| 4053 | + centers = np.array(centers) |
| 4054 | + if centers.ndim == 1: |
| 4055 | + centers = np.array([centers]) |
| 4056 | + |
| 4057 | + if not isinstance(coeffs, np.ndarray): |
| 4058 | + coeffs = np.array(coeffs) |
| 4059 | + if coeffs.ndim != 2: |
| 4060 | + if coeffs.ndim == 1: |
| 4061 | + coeffs = np.array([coeffs]) |
| 4062 | + else: |
| 4063 | + raise ValueError("coeffs should be a 2D array.") |
| 4064 | + if coeffs.shape[0] != centers.shape[0]: |
| 4065 | + raise ValueError( |
| 4066 | + "number of odf glyphs defined does not match with number of centers" |
| 4067 | + ) |
| 4068 | + |
| 4069 | + coeffs_given = coeffs.shape[-1] |
| 4070 | + degree = int((np.sqrt(8 * coeffs_given + 1) - 3) / 2) |
| 4071 | + if degree % 2 != 0: |
| 4072 | + degree -= 1 |
| 4073 | + coeffs = coeffs[:, : int(((degree + 1) * (degree + 2)) / 2)] |
| 4074 | + if not isinstance(scales, np.ndarray): |
| 4075 | + scales = np.array(scales) |
| 4076 | + if scales.size == 1: |
| 4077 | + scales = np.repeat(scales, centers.shape[0]) |
| 4078 | + elif scales.size != centers.shape[0]: |
| 4079 | + scales = np.concatenate( |
| 4080 | + (scales, np.ones(centers.shape[0] - scales.shape[0])), axis=None |
| 4081 | + ) |
| 4082 | + |
| 4083 | + total = np.sum(abs(coeffs), axis=1) |
| 4084 | + coeffs = np.dot(np.diag(1 / total * scales), coeffs) * 1.7 |
| 4085 | + |
| 4086 | + return sh_odf(centers, coeffs, degree, sh_basis, scales, opacity) |
0 commit comments