[1]:
# Install in Google Colab
try:
    import google.colab
    !pip install -q -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ quantem-widget
except ImportError:
    pass  # Not in Colab, skip
[2]:
try:
    %load_ext autoreload
    %autoreload 2
    %env ANYWIDGET_HMR=1
except Exception:
    pass  # autoreload unavailable (Colab Python 3.12+)
env: ANYWIDGET_HMR=1

Open In Colab # Show3D — Quick Demo

[3]:
import numpy as np
import torch
device = torch.device("mps" if torch.backends.mps.is_available() else "cuda" if torch.cuda.is_available() else "cpu")
def make_focal_series(n_frames=30, size=256):
    """Through-focus series: nanoparticles with Fresnel fringes at edges."""
    y, x = torch.meshgrid(torch.arange(size, device=device, dtype=torch.float32),
                           torch.arange(size, device=device, dtype=torch.float32), indexing="ij")
    particles = [
        (size * 0.35, size * 0.4, 18, 1.0),  # (cx, cy, radius, Z-contrast)
        (size * 0.65, size * 0.55, 25, 0.7),
        (size * 0.45, size * 0.7, 12, 1.2),
        (size * 0.7, size * 0.3, 15, 0.9),
    ]
    defocus = torch.linspace(-60, 60, n_frames, device=device)
    frames = torch.zeros((n_frames, size, size), device=device, dtype=torch.float32)
    for f_idx, df in enumerate(defocus):
        df_val = df.item()
        frame = torch.full((size, size), 0.5, device=device, dtype=torch.float32)
        for cx, cy, r, z in particles:
            dist = torch.sqrt((x - cx) ** 2 + (y - cy) ** 2)
            # In focus: sharp edge
            edge = 1.0 / (1 + torch.exp((dist - r) * 2))
            if abs(df_val) > 3:
                # Defocused: Fresnel fringes at particle edges
                fresnel = torch.cos(0.005 * df * (dist - r) ** 2) * torch.exp(
                    -((dist - r) ** 2) / (2 * (3 + abs(df_val) * 0.15) ** 2)
                )
                frame += z * (edge * 0.3 + fresnel * 0.2 * (1.0 if df_val > 0 else -1.0))
            else:
                frame += z * edge * 0.4
        # Poisson noise: use NumPy (torch.poisson may not work on MPS)
        noise = np.random.normal(0, 0.03, (size, size)).astype(np.float32)
        frame += torch.from_numpy(noise).to(device)
        frames[f_idx] = frame
    return frames.cpu().numpy()
focal_stack = make_focal_series()
print(f"Shape: {focal_stack.shape}, range: [{focal_stack.min():.2f}, {focal_stack.max():.2f}]")
print(f"Generators ready (device={device})")
Shape: (30, 256, 256), range: [0.19, 1.17]
Generators ready (device=mps)
[4]:
import quantem.widget
from quantem.widget import Show3D
defocus_values = torch.linspace(-60, 60, 30)
labels = [f"C10={df:.0f} nm" for df in defocus_values]
Show3D(
    focal_stack,
    labels=labels,
    title="Through-Focus Series: Nanoparticles with Fringes",
    cmap="gray",
    pixel_size=0.25,
    fps=8,
)
print(f"quantem.widget {quantem.widget.__version__}")
quantem.widget 0.4.0a3

Circle ROI centered on a nanoparticle#

w_circle = Show3D( focal_stack, title=”Circle ROI on Particle”, cmap=”gray”, pixel_size=0.25, ) w_circle

[5]:
# Square ROI on another particle
w_square = Show3D(
    focal_stack,
    title="Square ROI on Particle",
    cmap="inferno",
    pixel_size=0.25,
)
w_square.set_roi(row=102, col=90, radius=20)
w_square.roi_square(20)
w_square
[5]:
[6]:
# Rectangle ROI spanning a region between two particles
w_rect = Show3D(
    focal_stack,
    title="Rectangle ROI Between Particles",
    cmap="viridis",
    pixel_size=0.25,
)
w_rect.set_roi(row=128, col=128)
w_rect.roi_rectangle(width=80, height=40)
w_rect
[6]:

Inspect Widget State#

[7]:
w_rect.summary()
Rectangle ROI Between Particles
════════════════════════════════
Stack:    30×256×256 (0.25 Å/px)
Frame:    15/29 [15]
Data:     min=0.1906  max=1.167  mean=0.5168
Display:  viridis | manual contrast | linear
Playback: 5.0 fps | loop=on | reverse=off | boomerang=off
ROI:      1 region(s)
[ ]: