Show3D#

Open in Colab

Show3D scrubs a 3D stack slice by slice: a focal series, time series, tomographic reconstruction, or a sequence of related images. Drag the slider, press the play controls, or use the arrow keys to move through the stack.

This tutorial uses a real gold HAADF image from bobleesj/quantem-data. show3d_gold(size="small") makes a calibrated moving-crop stack from that image, so the documentation and Colab examples use real microscope data while still loading quickly.

Tip

Run this exact notebook with the Colab badge above, or View or download this notebook on GitHub. For finished results, use HTML and file export to export interactive HTML or share a trusted notebook with widget state.

import quantem.widget as qw
qw.profile()

import numpy as np
import torch

from quantem.widget import Show3D
from quantem.widget.datasets import show3d_gold

volume_dataset = show3d_gold(size="small")
quantem.widget  0.0.1rc39
  install       editable checkout
quantem.gpu     0.0.1rc8
  install       published package
quantem         0.1.9
torch           2.14.1+cu130  device=cpu
python          3.12.15
Source: gold-haadf (widget-tutorials/shared) from Hugging Face
Full image: 4096 x 4096 uint16
Stack: (32, 256, 256), stride 8, pixel size 0.1489 nm

Scrub the real-data stack#

The helper returns a quantem Dataset3d, so depth and lateral calibration travel with the stack. Show3D reads that metadata automatically and draws a physical scale bar without widget-level pixel-size arguments. Here the FFT starts at 2.0×; every FFT tile or inset keeps its live N.N× multiplier visible while you zoom.

stack = Show3D(volume_dataset, show_fft=True, fft_layout="right", fft_overlay_zoom=2.0)
stack
  Display bin 4×: 256×256 → 64×64

Compare one plane with a moving average#

Set Avg to 3 or 4, scrub, and press Play. This is a moving mean, not a depth projection or a reconstruction update. The centered window shifts inward at the ends, without wrapping or shortening: with Avg 3, centers 0 and 1 use frames 0–2; center 2 uses 1–3. Return to Avg 1 to inspect individual planes. The source array remains unchanged.

This demonstration stack contains moving crops, not measured depth slices. Apply the same controls to your own (depth, row, col) volume to inspect depth. For an actual projected phase, first check whether your array stores phase per plane or potential requiring an interaction constant and thickness conversion; averaging is not interchangeable with summing.

stack.avg_window = 3

Trigger a fresh render in an existing widget#

For live stacks, display one Show3D object and update it with set_image() as new frames arrive. set_image() is the render trigger: it writes a fresh current-frame transfer, bumps the frame sequence used by the frontend renderer, invalidates playback buffers, and clamps the current slice to the new stack.

In a real acquisition loop, pass offline=False so frames stream from the kernel instead of being re-embedded on every append:

live3d = Show3D(np.stack(live_frames), offline=False, fps=4)  # streams, nothing embedded
while acquiring:
    live_frames.append(next_frame())
    live3d.set_image(np.stack(live_frames))

The executed example below keeps the default offline embedding instead, so the widget on this documentation page stays scrubbable without a kernel - the set_image() mechanics are identical either way.

live_frames = [frame for frame in volume_dataset.array[:4]]
live3d = Show3D(
    np.stack(live_frames),
    labels=[f"frame {i + 1}" for i in range(len(live_frames))],
    fps=4,
)
live3d
  Display bin 4×: 256×256 → 64×64
live_frames.extend(volume_dataset.array[4:8])
live3d.set_image(
    np.stack(live_frames),
    labels=[f"frame {i + 1}" for i in range(len(live_frames))],
)
live3d.slice_idx = len(live_frames) - 1