Show1D#

Open in Colab

Show1D is the iteration dashboard for traces, optimizer losses, image line profiles, and reconstruction snapshots. It is useful when a ptychography or denoising run produces scalar metrics every iteration and object/probe images at lower-rate checkpoints.

This tutorial uses the real ducky joint-time ptychography lambda sweep hosted in the public bobleesj/quantem-data Hugging Face dataset. The files are grouped under widget-tutorials/show1d/ducky/small, and the public API hides those storage details behind Show1D.from_example("ducky") and show1d_ducky(size="small").

import pathlib
import tempfile

import numpy as np

from quantem.widget import Show1D
from quantem.widget.datasets import show1d_ducky

Compare loss traces with reconstruction snapshots#

A joint reconstruction sweep often has many related loss curves: a frame-by-frame baseline plus several regularization strengths. The ducky example below loads a file-backed show1d_monitor.jsonl run with 12 frame checkpoints and a full lambda sweep.

Each snapshot group contains a reference image and the reconstruction candidates for the selected frame. The selected frame in the loss plot and the image grid stay linked. Use the star beside playback to bookmark an important iteration; the same action is available as star_snapshot_group(...) from Python.

ducky_run = show1d_ducky(size="small", verbose=False)

widget = Show1D.from_example(
    "ducky",
    size="small",
    snapshot_columns=4,
    snapshot_panel_width_px=720,
    snapshot_histogram_width=360,
    snapshot_histogram_height=52,
)
widget.goto_snapshot(5)
widget.star_snapshot_group(5)
widget

Open the selected snapshot group in Show2D#

Use to_show2d() when a snapshot group needs deeper image inspection. The handoff preserves labels, colormap, scale-bar units, hidden/starred trial state, and linked contrast defaults.

widget.to_show2d(
    group=5,
    images=["reference", "lambda 0.3", "lambda 1", "lambda 10"],
    title="Frame 5 reconstruction candidates",
)

Profile a single image#

Show1D.from_image samples a line profile from a 2D image and keeps the image context beside the trace. Coordinates use the scientific (row, col) convention. Here we reuse the reference snapshot downloaded by show1d_ducky().

reference = np.load(ducky_run / "snapshots" / "reference.npy")

Show1D.from_image(
    reference,
    line=((70, 45), (170, 190)),
    profile_width=3,
    title="Ducky reference line profile",
    image_cmap="viridis",
)

Reopen a file-backed live monitor#

For overnight reconstructions, write one JSON object per line. Each event can contain scalar losses, metrics, warnings, review state, and snapshot paths relative to the monitor file. Show1D.from_monitor_file(...) rebuilds the dashboard after the run, while Show1D.watch_run(..., refresh_s=5) polls the same file during a live notebook session.

Hide synthetic data generation code

rng = np.random.default_rng(7)


def lambda_label(value):
    return "lambda_" + str(value).replace(".", "p")


def ducky_object(size=128, frame=0, lam=1, noise=0.02):
    grid = np.linspace(-1.0, 1.0, size, dtype=np.float32)
    row, col = np.meshgrid(grid, grid, indexing="ij")
    drift = 0.08 * np.sin(2 * np.pi * frame / 11)
    regularization = np.log10(float(lam) + 1.3)

    body = (((col + 0.16 + drift) / 0.55) ** 2 + ((row + 0.02) / 0.50) ** 2) < 1.0
    head = (((col - 0.30 + drift) / 0.27) ** 2 + ((row - 0.12) / 0.30) ** 2) < 1.0
    beak = np.exp(-((col - 0.62 + drift) ** 2 / 0.010 + (row - 0.12) ** 2 / 0.006))
    wing = np.exp(-((col + 0.10 + drift) ** 2 / 0.070 + (row + 0.10) ** 2 / 0.020))
    lattice = 0.08 * np.sin(42 * col + 0.6 * frame) * np.sin(38 * row)
    background = 0.10 * np.sin(8 * col + 0.4 * frame) + 0.08 * np.cos(7 * row)

    image = 0.20 * body + 0.28 * head + 0.16 * beak + 0.12 * wing + lattice + background
    image = image - 0.08 * abs(regularization - 1.1) * (row**2 + col**2)
    image += noise * rng.standard_normal((size, size)).astype(np.float32)
    return image.astype(np.float32)


run_dir = pathlib.Path(tempfile.mkdtemp(prefix="quantem_show1d_monitor_"))
snapshot_dir = run_dir / "snapshots"
snapshot_dir.mkdir(parents=True)
monitor_path = run_dir / "show1d_monitor.jsonl"

monitor_lambdas = [0.3, 1, 3, 10]
for i in range(12):
    losses = {}
    metrics = {}
    snapshots = {}
    for lam in monitor_lambdas:
        label = lambda_label(lam)
        loss = 112.0 + 0.9 * np.exp(-i / (2.0 + 0.3 * lam)) + 0.04 * np.sin(i + lam)
        losses[f"lambda {lam:g}"] = float(loss)
        metrics[label] = {
            "final_loss": float(loss),
            "flicker": float(0.04 + 0.015 * abs(np.log10(lam + 0.3) - 0.7)),
        }
        if i in (0, 4, 8, 11):
            image_path = snapshot_dir / f"{label}_i{i:03d}.npy"
            np.save(image_path, ducky_object(frame=i, lam=lam, noise=0.04))
            snapshots[label] = str(image_path.relative_to(run_dir))

    event = {"iteration": i, "losses": losses, "metrics": metrics}
    if snapshots:
        event["label"] = f"iter {i}"
        event["snapshots"] = snapshots
    if i == 8:
        event["warnings"] = ["lambda 0.3 has visible frame-to-frame flicker"]
    if i == 11:
        event["starred"] = ["lambda_3"]
        event["hidden"] = ["lambda_0p3"]
        event["notes"] = {"lambda_3": "best visual/loss balance"}
        event["tags"] = {"lambda_3": ["best lambda"]}

    Show1D.append_monitor_event(monitor_path, event)
monitor = Show1D.from_monitor_file(
    monitor_path,
    title="Reopened Show1D monitor",
    snapshot_columns=4,
    snapshot_panel_width_px=640,
    snapshot_histogram_width=320,
    show_review=False,
    log_scale=False,
)
monitor.goto_snapshot(3)
monitor

During a real run, display the polling widget once, then let the reconstruction keep appending JSONL events:

live = Show1D.watch_run("/path/to/run/show1d_monitor.jsonl", refresh_s=5)
live

Use live.stop_monitor() when the notebook no longer needs to poll. If the notebook disconnects overnight, rerun Show1D.from_monitor_file(...) on the same file to rebuild the view from disk.