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

volume
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)

Gallery generated by Sphinx-Gallery