Note
Go to the end to download the full example code.
Volume Rendering#
Example volume rendering
Controls:
- 1 - toggle camera between first person (fly), regular 3D (turntable) and
arcball
2 - toggle between volume rendering methods
3 - toggle between stent-CT / brain-MRI image
4 - toggle between colormaps
5 - toggle between interpolation methods
0 - reset cameras
[] - decrease/increase isosurface threshold
With fly camera:
WASD or arrow keys - move around
SPACE - brake
FC - move up-down
IJKL or mouse - look around
Downloading data from https://raw.githubusercontent.com/vispy/demo-data/main/brain/mri.npz (3.9 MB)
[ ] 1.60843 | downloading
[. ] 3.21685 / downloading
[. ] 4.82528 - downloading
[.. ] 6.43371 \ downloading
[... ] 8.04213 | downloading
[... ] 9.65056 / downloading
[.... ] 11.25899 - downloading
[..... ] 12.86741 \ downloading
[..... ] 14.47584 | downloading
[...... ] 16.08427 / downloading
[....... ] 17.69269 - downloading
[....... ] 19.30112 \ downloading
[........ ] 20.90955 | downloading
[......... ] 22.51797 / downloading
[......... ] 24.12640 - downloading
[.......... ] 25.73483 \ downloading
[.......... ] 27.34325 | downloading
[........... ] 28.95168 / downloading
[............ ] 30.56010 - downloading
[............ ] 32.16853 \ downloading
[............. ] 33.77696 | downloading
[.............. ] 35.38538 / downloading
[.............. ] 36.99381 - downloading
[............... ] 38.60224 \ downloading
[................ ] 40.21066 | downloading
[................ ] 41.81909 / downloading
[................. ] 43.42752 - downloading
[.................. ] 45.03594 \ downloading
[.................. ] 46.64437 | downloading
[................... ] 48.25280 / downloading
[................... ] 49.86122 - downloading
[.................... ] 51.46965 \ downloading
[..................... ] 53.07808 | downloading
[..................... ] 54.68650 / downloading
[...................... ] 56.29493 - downloading
[....................... ] 57.90336 \ downloading
[....................... ] 59.51178 | downloading
[........................ ] 61.12021 / downloading
[......................... ] 62.72864 - downloading
[......................... ] 64.33706 \ downloading
[.......................... ] 65.94549 | downloading
[........................... ] 67.55392 / downloading
[........................... ] 69.16234 - downloading
[............................ ] 70.77077 \ downloading
[............................ ] 72.37920 | downloading
[............................. ] 73.98762 / downloading
[.............................. ] 75.59605 - downloading
[.............................. ] 77.20448 \ downloading
[............................... ] 78.81290 | downloading
[................................ ] 80.42133 / downloading
[................................ ] 82.02976 - downloading
[................................. ] 83.63818 \ downloading
[.................................. ] 85.24661 | downloading
[.................................. ] 86.85503 / downloading
[................................... ] 88.46346 - downloading
[.................................... ] 90.07189 \ downloading
[.................................... ] 91.68031 | downloading
[..................................... ] 93.28874 / downloading
[..................................... ] 94.89717 - downloading
[...................................... ] 96.50559 \ downloading
[....................................... ] 98.11402 | downloading
[....................................... ] 99.72245 / downloading
[........................................] 100.00000 - downloading
File saved as /home/runner/.vispy/data/brain/mri.npz.
from itertools import cycle
import numpy as np
from vispy import app, scene, io
from vispy.color import get_colormaps, BaseColormap
from vispy.visuals.transforms import STTransform
# Read volume
vol1 = np.load(io.load_data_file('volume/stent.npz'))['arr_0']
vol2 = np.load(io.load_data_file('brain/mri.npz'))['data']
vol2 = np.flipud(np.rollaxis(vol2, 1))
# Prepare canvas
canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True)
canvas.measure_fps()
# Set up a viewbox to display the image with interactive pan/zoom
view = canvas.central_widget.add_view()
# Create the volume visuals, only one is visible
volume1 = scene.visuals.Volume(vol1, parent=view.scene, threshold=0.225)
volume1.transform = scene.STTransform(translate=(64, 64, 0))
volume2 = scene.visuals.Volume(vol2, parent=view.scene, threshold=0.2)
volume2.visible = False
# Create three cameras (Fly, Turntable and Arcball)
fov = 60.
cam1 = scene.cameras.FlyCamera(parent=view.scene, fov=fov, name='Fly')
cam2 = scene.cameras.TurntableCamera(parent=view.scene, fov=fov,
name='Turntable')
cam3 = scene.cameras.ArcballCamera(parent=view.scene, fov=fov, name='Arcball')
view.camera = cam2 # Select turntable at first
# Create an XYZAxis visual
axis = scene.visuals.XYZAxis(parent=view)
s = STTransform(translate=(50, 50), scale=(50, 50, 50, 1))
affine = s.as_matrix()
axis.transform = affine
# create colormaps that work well for translucent and additive volume rendering
class TransFire(BaseColormap):
glsl_map = """
vec4 translucent_fire(float t) {
return vec4(pow(t, 0.5), t, t*t, max(0, t*1.05 - 0.05));
}
"""
class TransGrays(BaseColormap):
glsl_map = """
vec4 translucent_grays(float t) {
return vec4(t, t, t, t*0.05);
}
"""
# Setup colormap iterators
opaque_cmaps = cycle(get_colormaps())
translucent_cmaps = cycle([TransFire(), TransGrays()])
opaque_cmap = next(opaque_cmaps)
translucent_cmap = next(translucent_cmaps)
interp_methods = cycle(volume1.interpolation_methods)
interp = next(interp_methods)
# Implement axis connection with cam2
@canvas.events.mouse_move.connect
def on_mouse_move(event):
if event.button == 1 and event.is_dragging:
axis.transform.reset()
axis.transform.rotate(cam2.roll, (0, 0, 1))
axis.transform.rotate(cam2.elevation, (1, 0, 0))
axis.transform.rotate(cam2.azimuth, (0, 1, 0))
axis.transform.scale((50, 50, 0.001))
axis.transform.translate((50., 50.))
axis.update()
# Implement key presses
@canvas.events.key_press.connect
def on_key_press(event):
global opaque_cmap, translucent_cmap
if event.text == '1':
cam_toggle = {cam1: cam2, cam2: cam3, cam3: cam1}
view.camera = cam_toggle.get(view.camera, cam2)
print(view.camera.name + ' camera')
if view.camera is cam2:
axis.visible = True
else:
axis.visible = False
elif event.text == '2':
methods = ['mip', 'translucent', 'iso', 'additive']
method = methods[(methods.index(volume1.method) + 1) % 4]
print("Volume render method: %s" % method)
cmap = opaque_cmap if method in ['mip', 'iso'] else translucent_cmap
volume1.method = method
volume1.cmap = cmap
volume2.method = method
volume2.cmap = cmap
elif event.text == '3':
volume1.visible = not volume1.visible
volume2.visible = not volume1.visible
elif event.text == '4':
if volume1.method in ['mip', 'iso']:
cmap = opaque_cmap = next(opaque_cmaps)
else:
cmap = translucent_cmap = next(translucent_cmaps)
volume1.cmap = cmap
volume2.cmap = cmap
elif event.text == '5':
interp = next(interp_methods)
volume1.interpolation = interp
volume2.interpolation = interp
print(f"Interpolation method: {interp}")
elif event.text == '0':
cam1.set_range()
cam3.set_range()
elif event.text != '' and event.text in '[]':
s = -0.025 if event.text == '[' else 0.025
volume1.threshold += s
volume2.threshold += s
th = volume1.threshold if volume1.visible else volume2.threshold
print("Isosurface threshold: %0.3f" % th)
# for testing performance
# @canvas.connect
# def on_draw(ev):
# canvas.update()
if __name__ == '__main__':
print(__doc__)
app.run()
Total running time of the script: (0 minutes 1.385 seconds)