Rendering Planes through 3D Data#

Controls: * 1 - toggle between volume rendering methods * 2 - toggle between volume rendering modes (‘volume’, ‘plane’) * [] - shift plane along plane normal * {} - decrease/increase plane thickness * Spacebar - stop/start animation

  • x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis

volume plane
import sys

import numpy as np

from vispy import app, scene, io
from vispy.visuals.transforms import STTransform

# Read volume
vol = np.load(io.load_data_file('volume/stent.npz'))['arr_0']

# Prepare canvas
canvas = scene.SceneCanvas(keys='interactive', show=True)
view = canvas.central_widget.add_view()

# Create the volume visual for plane rendering
plane = scene.visuals.Volume(
    vol,
    parent=view.scene,
    raycasting_mode='plane',
    method='mip',
    plane_thickness=3.0,
    plane_position=(128, 60, 64),
    plane_normal=(1, 0, 0),
)

volume = scene.visuals.Volume(
    vol,
    parent=view.scene,
    raycasting_mode='volume',
    method='mip',
)
volume.set_gl_state('additive')
volume.opacity = 0.25

# Create a camera
cam = scene.cameras.TurntableCamera(
    parent=view.scene, fov=60.0, azimuth=-42.0, elevation=30.0
)
view.camera = cam

# 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


def update_axis_visual():
    """Sync XYZAxis visual with camera angles"""
    axis.transform.reset()

    axis.transform.rotate(cam.roll, (0, 0, 1))
    axis.transform.rotate(cam.elevation, (1, 0, 0))
    axis.transform.rotate(cam.azimuth, (0, 1, 0))
    axis.transform.scale((50, 50, 0.001))
    axis.transform.translate((50., 50.))

    axis.update()


update_axis_visual()


@canvas.events.mouse_move.connect
def on_mouse_move(event):
    if event.button == 1 and event.is_dragging:
        update_axis_visual()


# Implement key presses
@canvas.events.key_press.connect
def on_key_press(event):
    if event.text == '1':
        methods = ['mip', 'average']
        method = methods[(methods.index(plane.method) + 1) % 2]
        print("Volume render method: %s" % method)
        plane.method = method
    elif event.text == '2':
        modes = ['volume', 'plane']
        if plane.raycasting_mode == modes[0]:
            plane.raycasting_mode = modes[1]
            print(modes[1])
        else:
            plane.raycasting_mode = modes[0]
            print(modes[0])
    elif event.text != '' and event.text in '{}':
        t = -1 if event.text == '{' else 1
        plane.plane_thickness += t
        plane.plane_thickness += t
        print(f"plane thickness: {plane.plane_thickness}")
    elif event.text != '' and event.text in '[]':
        shift = plane.plane_normal / np.linalg.norm(plane.plane_normal)
        if event.text == '[':
            plane.plane_position -= 2 * shift
        elif event.text == ']':
            plane.plane_position += 2 * shift
        print(f"plane position: {plane.plane_position}")
    elif event.text == 'x':
        plane.plane_normal = [0, 0, 1]
    elif event.text == 'y':
        plane.plane_normal = [0, 1, 0]
    elif event.text == 'z':
        plane.plane_normal = [1, 0, 0]
    elif event.text == 'o':
        plane.plane_normal = [1, 1, 1]
    elif event.text == ' ':
        if timer.running:
            timer.stop()
        else:
            timer.start()


def move_plane(event):
    z_pos = plane.plane_position[0]
    if z_pos < 32:
        plane.plane_position = plane.plane_position + [1, 0, 0]
    elif 32 < z_pos <= 220:
        plane.plane_position = plane.plane_position - [1, 0, 0]
    else:
        plane.plane_position = (220, 64, 64)


timer = app.Timer('auto', connect=move_plane, start=True)

if __name__ == '__main__':
    canvas.show()
    print(__doc__)
    if sys.flags.interactive == 0:
        plane.plane_position = (220, 64, 64)
        app.run()

Total running time of the script: (0 minutes 27.731 seconds)

Gallery generated by Sphinx-Gallery