Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add white matter fiber tracking to sEEG tutorial #12555

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions doc/changes/devel/12555.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add diffusion imaging to :ref:`tut-working-with-seeg` including adding :meth:`mne.viz.Brain.add_streamline` and :meth:`mne.viz.Brain.remove_streamlines` to visualize the fiber tracts, by `Alex Rockhill`_.
4 changes: 2 additions & 2 deletions mne/datasets/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
# here: ↓↓↓↓↓↓↓↓
RELEASES = dict(
testing="0.152",
misc="0.27",
misc="0.29",
phantom_kit="0.2",
ucl_opm_auditory="0.2",
)
Expand Down Expand Up @@ -131,7 +131,7 @@
)
MNE_DATASETS["misc"] = dict(
archive_name=f"{MISC_VERSIONED}.tar.gz", # 'mne-misc-data',
hash="md5:e343d3a00cb49f8a2f719d14f4758afe",
hash="md5:19535192331d9e4e99d8886028c1f447",
url=(
"https://codeload.github.com/mne-tools/mne-misc-data/tar.gz/"
f'{RELEASES["misc"]}'
Expand Down
64 changes: 64 additions & 0 deletions mne/viz/_brain/_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ class Brain:
+-------------------------------------+--------------+---------------+
| :meth:`add_skull` | | ✓ |
+-------------------------------------+--------------+---------------+
| :meth:`add_streamline` | | ✓ |
+-------------------------------------+--------------+---------------+
| :meth:`add_text` | ✓ | ✓ |
+-------------------------------------+--------------+---------------+
| :meth:`add_volume_labels` | | ✓ |
Expand Down Expand Up @@ -253,6 +255,8 @@ class Brain:
+-------------------------------------+--------------+---------------+
| :meth:`remove_skull` | | ✓ |
+-------------------------------------+--------------+---------------+
| :meth:`remove_streamlines` | | ✓ |
+-------------------------------------+--------------+---------------+
| :meth:`remove_text` | | ✓ |
+-------------------------------------+--------------+---------------+
| :meth:`remove_volume_labels` | | ✓ |
Expand Down Expand Up @@ -2535,6 +2539,66 @@ def remove_skull(self):
"""Remove skull objects from the rendered scene."""
self._remove("skull", render=True)

@fill_doc
def add_streamline(
self,
streamline,
line_width=1,
color="red",
scalars=None,
colormap=None,
vmin=None,
vmax=None,
alpha=1,
):
"""Add a streamlines to render fiber tracts.

Parameters
----------
streamline : array shape=(n_points, 3)
An array with 3D points forming a line in units of m.
line_width : int
The width of the line.
color : list
A list with entries of anything matplotlib accepts:
string, RGB, hex, etc.
scalars : list
A list of scalar values associated with each vertex of
the streamline.
%(colormap)s
vmin : None | float
The minimum value for color scaling.
vmax : None | float
The maximum value for color scaling.
%(alpha)s

Notes
-----
.. versionadded:: 0.24
"""
color = _to_rgb(color)

for _ in self._iter_views("vol"):
actor, _ = self._renderer.line(
streamline * (1e3 if self._units == "mm" else 1),
color=color,
opacity=alpha,
line_width=line_width,
scalars=scalars,
colormap=colormap,
vmin=vmin,
vmax=vmax,
reset_camera=False,
render=False,
)
self._add_actor("streamlines", actor)

self._renderer._update()

def remove_streamlines(self):
"""Remove streamline objects from the rendered scene."""
self._remove("streamlines", render=True)

@fill_doc
def add_volume_labels(
self,
Expand Down
60 changes: 60 additions & 0 deletions mne/viz/backends/_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,66 @@ def mesh(
"""
pass

@abstractclassmethod
def line(
self,
line,
colors,
opacity=1.0,
backface_culling=False,
scalars=None,
colormap=None,
vmin=None,
vmax=None,
interpolate_before_map=True,
line_width=1.0,
polygon_offset=None,
**kwargs,
):
"""Add a mesh in the scene.

Parameters
----------
line : list
A list of array-like points defining the lines.
color : tuple | str
The color of the mesh as a tuple (red, green, blue) of float
values between 0 and 1 or a valid color name (i.e. 'white'
or 'w').
opacity : float
The opacity of the mesh.
shading : bool
If True, enable the mesh shading.
backface_culling : bool
If True, enable backface culling on the mesh.
scalars : ndarray, shape (n_vertices,)
The scalar valued associated to the vertices.
vmin : float | None
vmin is used to scale the colormap.
If None, the min of the data will be used.
vmax : float | None
vmax is used to scale the colormap.
If None, the max of the data will be used.
colormap : str | np.ndarray | matplotlib.colors.Colormap | None
The colormap to use.
interpolate_before_map :
Enabling makes for a smoother scalars display. Default is True.
When False, OpenGL will interpolate the mapped colors which can
result is showing colors that are not present in the color map.
line_width : int
The width of the line.
polygon_offset : float
If not None, the factor used to resolve coincident topology.
kwargs : args
The arguments to pass to triangular_mesh

Returns
-------
line :
Handle of the line in the scene.
"""
pass

@abstractclassmethod
def contour(
self,
Expand Down
31 changes: 31 additions & 0 deletions mne/viz/backends/_pyvista.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,37 @@ def mesh(
**kwargs,
)

def line(
self,
line,
color,
opacity=1.0,
backface_culling=False,
scalars=None,
colormap=None,
vmin=None,
vmax=None,
interpolate_before_map=True,
line_width=1.0,
polygon_offset=None,
**kwargs,
):
return self.polydata(
mesh=pyvista.MultipleLines(line),
color=color,
opacity=opacity,
normals=np.zeros_like(np.array(line)),
backface_culling=backface_culling,
scalars=scalars,
colormap=colormap,
vmin=vmin,
vmax=vmax,
interpolate_before_map=interpolate_before_map,
line_width=line_width,
polygon_offset=polygon_offset,
**kwargs,
)

def contour(
self,
surface,
Expand Down
11 changes: 11 additions & 0 deletions mne/viz/backends/tests/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ def test_3d_backend(renderer):
txt_text = "renderer"
txt_size = 14

line_data = [[0, 0, 0], [1, 1, 1]]
line_color = "red"

cam_distance = 5 * tet_size

# init scene
Expand All @@ -123,6 +126,14 @@ def test_3d_backend(renderer):
)
rend.remove_mesh(mesh_data)

# use lines
line_actor = rend.line(
line_data,
color=line_color,
line_width=5,
)
rend.remove_mesh(line_actor)

# use contour
rend.contour(
surface=ct_surface, scalars=ct_scalars, contours=ct_levels, kind="line"
Expand Down