Skip to content

Instantly share code, notes, and snippets.

@MarilynKeller
Last active January 17, 2024 14:48
Show Gist options
  • Save MarilynKeller/2835d1d2f13a3e113b011f7b1f7a9e88 to your computer and use it in GitHub Desktop.
Save MarilynKeller/2835d1d2f13a3e113b011f7b1f7a9e88 to your computer and use it in GitHub Desktop.
Pyvista wrapper functions
import pyvista as pv
from pyvista import examples
import data_mri as dd
import numpy as np
def plot_3D_image(arr, resolution, center, p=None, interactive_slice=False, orthogonal_slices=True):
''' Plot of a 3D volume with orthogonal slices slices.'''
values = arr
values.shape
# Create the spatial reference
grid = pv.UniformGrid()
# Set the grid dimensions: shape + 1 because we want to inject our values on
# the CELL data
grid.dimensions = np.array(values.shape) + 1
# Edit the spatial reference
# The bottom left corner of the data set
print(f'WARNING: a MRI can have different resolutions per slice. Here we plot with the same resolution for all slices \
otherwise we cant plot the a grid of voxels. See point clouds for correct visualisation')
origin = np.array(resolution[0]) * np.array(arr.shape) * 0.5
grid.origin = origin
print(f'Scan size in meter: {origin*2}')
grid.spacing = resolution[0] # These are the cell sizes along each axis
# Add the data values to the cell data
grid.cell_arrays["values"] = values.flatten(order="F") # Flatten the array!
if p is None:
p = pv.Plotter()
p.show_grid()
if orthogonal_slices:
slices = grid.slice_orthogonal()
p.add_mesh(slices)
if interactive_slice:
p.add_mesh_clip_plane(grid)
return p
def plot_mesh(mesh_path, is_pc=False, p=None, scale_factor=1e3):
'''
:param mesh_path: string giving the path to the mesh to load. Most common formats are supported
:param is_pc: If True, show the mesh as a point cloud
:param p: Existing pyvista plotter to plot to. One will be created if set to None.
:param scale_factor: To scale the mesh
:return: p Plotter containing the mesh
'''
mesh = pv.read(mesh_path)
if p==None:
p = pv.Plotter()
if is_pc:
mesh = pv.PolyData(mesh.points/scale_factor)
# mesh.plot(render_points_as_spheres=True)
p.add_mesh(mesh, render_points_as_spheres=True, color='red')
else:
p.add_mesh(mesh, render_points_as_spheres=True)
p.show_grid()
return
def plotly_volume(arr):
""" Not sure what this does anymore. Feel free to try X) """
import time
import numpy as np
from skimage import io
# vol = io.imread("https://s3.amazonaws.com/assets.datacamp.com/blog_assets/attention-mri.tif")
vol = arr
volume = vol.T
r, c = volume[0].shape
# Define frames
import plotly.graph_objects as go
nb_frames = 68
fig = go.Figure(frames=[go.Frame(data=go.Surface(
z=(6.7 - k * 0.1) * np.ones((r, c)),
surfacecolor=np.flipud(volume[67 - k]),
cmin=0, cmax=1
),
name=str(k) # you need to name the frame for the animation to behave properly
)
for k in range(nb_frames)])
# Add data to be displayed before animation starts
fig.add_trace(go.Surface(
z=6.7 * np.ones((r, c)),
surfacecolor=np.flipud(volume[67]),
colorscale='Gray',
cmin=0, cmax=1,
colorbar=dict(thickness=20, ticklen=4)
))
def frame_args(duration):
return {
"frame": {"duration": duration},
"mode": "immediate",
"fromcurrent": True,
"transition": {"duration": duration, "easing": "linear"},
}
sliders = [
{
"pad": {"b": 10, "t": 60},
"len": 0.9,
"x": 0.1,
"y": 0,
"steps": [
{
"args": [[f.name], frame_args(0)],
"label": str(k),
"method": "animate",
}
for k, f in enumerate(fig.frames)
],
}
]
# Layout
fig.update_layout(
title='Slices in volumetric data',
width=600,
height=600,
scene=dict(
zaxis=dict(range=[-0.1, 6.8], autorange=False),
aspectratio=dict(x=1, y=1, z=1),
),
updatemenus=[
{
"buttons": [
{
"args": [None, frame_args(50)],
"label": "▶", # play symbol
"method": "animate",
},
{
"args": [[None], frame_args(0)],
"label": "◼", # pause symbol
"method": "animate",
},
],
"direction": "left",
"pad": {"r": 10, "t": 70},
"type": "buttons",
"x": 0.1,
"y": 0,
}
],
sliders=sliders
)
fig.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment