Show4DSTEM#
Compare diffraction patterns side by side#
For a live notebook with several datasets, use
Show4DSTEM(stack, view_mode="multiple", compare_dp_mode="all").
Each visible dataset shows its native diffraction pattern at the same scan
position. Circle, Square or Rect scan selection with Mean compares each
dataset over identical scan positions, never an average across datasets.
Dragging the detector in any diffraction tile updates the shared virtual
detector and all virtual images before release.
The diffraction grid shares the virtual-image grid’s zoom, pan, reset,
scale bars and column layout. Playback is available in Single view only.
The all mode requires a live kernel; standalone export is not qualified for
this layout. Existing selected and average modes remain available.
Packed sources require their own reduction support and are not established
by this dense-array feature. See the
interaction contract.
Public import:
from quantem.gpu.io import load
from quantem.widget import Show4DSTEM
Show4DSTEM is one operator-facing factory. It chooses the viewer from its
input:
an acquisition returned by
quantem.gpu.io.loadon CUDA or Apple Silicon MPS opens as a live view over its encoded GPU storage;a list of acquisitions with one scan and detector shape opens as a comparison grid labelled by source file;
a NumPy array, Torch tensor, or
Dataset5dstemseries opens in the base viewer, which also supports browser WebGPU compute and offline export.
Canonical forms:
# One acquisition; CUDA or MPS is selected automatically.
viewer = Show4DSTEM(load(path))
# Part of the scan: (row_start, row_stop, col_start, col_stop), exclusive stops.
viewer = Show4DSTEM(load(path), scan_region=(128, 384, 128, 384))
# Several acquisitions: one shared diffraction ROI, one virtual image per file.
viewer = Show4DSTEM(load([path1, path2, path3]), compare_cols=3)
# Every ready master in a folder; masters completed later are appended.
viewer = Show4DSTEM.from_folder("/data/session")
# An array in the Python session: browser-owned compute and standalone export.
viewer = Show4DSTEM(array, backend="webgpu")
viewer.export_html("show4dstem.html")
Use the CLI quantem show4dstem ... --backend webgpu --html folder export for
a standalone browser viewer over large HDF5 acquisitions.
Encoded acquisitions#
load(path) keeps the acquisition ANS encoded on the GPU at native detector
sampling and count dtype. A 512 x 512 x 192 x 192 uint16 Arina scan, 18 GiB as
a dense array, occupies about 0.1 to 2 GiB depending on its counts. The viewer
never expands it: virtual images are summed on the encoded storage, and each
diffraction pattern comes from a bounded read. CUDA and MPS acquisitions use
the same viewer.
These views need a live kernel. offline=True, data_url= and
backend="webgpu" raise for an encoded acquisition. export_html still works
from a live view: an interactive export reads the acquisition in small scan
windows into the embedded array (a host copy of the full data at the chosen
dtype and binning; the GPU never holds the dense cube), and a report export
(export_kind="report", static PNG pages) embeds no raw 4D data. For a
standalone browser viewer over the source files, use the CLI
--backend webgpu --html folder export.
The viewer borrows the acquisition. Keep a handle when you plan to release GPU memory, and close it after the viewer:
loaded = load(path)
viewer = Show4DSTEM(loaded)
viewer
# later, when you are done with this dataset
viewer.close()
loaded.close()
Backend ownership#
Fit the diffraction disk once and supply the same geometry to the viewer and virtual detectors:
from quantem.gpu import detector
loaded = load(path)
center, radius = detector.fit_probe(detector.mean(loaded))
Show4DSTEM(loaded, center=center, bf_radius=radius)
The center is (row, col) and the radius is in detector pixels. Supplying
both skips the viewer’s automatic disk estimation. fit_probe estimates disk
geometry, not the complex probe or its aberrations. detector.mean and the
other quantem.gpu.detector products (bf, adf, masked_sum, or a
detector.prepare(loaded) session for repeated queries) read the encoded
storage directly.
For a bounded Torch tensor on the same GPU, read a scan region:
patterns_t = loaded.read(scan_region=(100, 164, 100, 164)) # (64, 64, det_row, det_col)
Show4DSTEM(patterns_t, center=center, bf_radius=radius)
Show4DSTEM has two acceleration surfaces:
Live Python-backed viewers compute in the kernel on CUDA or MPS, over an encoded acquisition, a tensor, or an array.
Exported browser viewers use the packed HTML or folder payload and browser WebGPU. After export, interaction does not depend on Python, Torch, CUDA, or MPS.
Routing lives in quantem.widget.show4dstem_factory: acquisitions from
io.load open through quantem.widget.show4dstem_bounded, and every other
input opens in the base viewer.
Live scope folders#
For real-time processing on a microscope or acquisition workstation, open the acquisition folder directly:
from quantem.widget import Show4DSTEM
widget = Show4DSTEM.from_folder(
"/data/live-scope-session",
scan_size=512, # keep only 512 x 512 scans in a mixed folder
columns=5, # grid width
page_size=10, # datasets per page
watch_interval=2.0, # seconds between folder polls
)
widget
from_folder(...) loads every ready *_master.h5 (pattern=, recursive=)
with quantem.gpu.io.load into encoded storage on one CUDA device or the Apple
GPU (backend=, device=), at full detector resolution. At about 0.1 to 2 GiB
per 512 x 512 x 192 x 192 scan, the whole folder stays resident without
detector binning or paging. The viewer opens once the first master is loaded;
the others join the comparison grid in the background, and
widget.wait_for_folder() blocks until they have.
Masters that are not completely written are skipped (ready_only=True). When
the folder mixes geometries, the largest group sharing one scan and detector
shape is shown; verbose=True reports the skipped count. Use scan_size= or a
narrower pattern= to choose another group, and max_masters= or
min_masters= to bound the count. Other keyword arguments, such as
compare_dp_mode="selected" or title=, go to the viewer.
With watch=True (default), a master that completes while the viewer is open
is appended once its header signature is unchanged on two consecutive polls, so
a file still being written is never read. A compact title-area badge
distinguishes a live Watching worker, Updating, Waiting for file completion, a corrective Watch error, and Stopped; watch=False opens a
fixed snapshot with no badge.
The folder lifecycle matches Show2D and Show3D:
widget.wait_for_folder() # block until the opening masters are loaded
new_datasets = widget.poll_folder() # append newly completed masters now
widget.stop_folder_watch() # pause background discovery
widget.watch_folder(interval=1.0) # resume discovery
widget.free() # close the acquisitions from_folder loaded
widget.close() # stop folder work, release them, close the widget
Folder watching is append-only. Known masters are not duplicated, incomplete or externally linked masters wait until they are readable, and removing a file does not delete a dataset from an active scientific view.
Maintainer real-time signoff follows S4D-14: introduce genuine master/chunk files while one Jupyter widget is mounted and measure both discovery/control paint and requested virtual-image/diffraction paint.
This path reads the original master data, not cached thumbnails.
On Apple Silicon the same call loads onto the Apple GPU; pass backend="mps"
to require it:
widget = Show4DSTEM.from_folder(
"/data/live-scope-session",
backend="mps",
scan_size=512,
title="Live 4D-STEM",
)
widget
GPU memory belongs to the loaded acquisitions and the Python session, not to
the visual widget alone. free() and close() release the acquisitions that
from_folder loaded; acquisitions you pass to Show4DSTEM(...) stay open until
you call their close(). The live widget shows a compact GPU memory label in
its title row when CUDA or MPS memory is visible. Exported HTML has no live
Python GPU allocation, so it does not expose a “free GPU memory” control.
Compute SSB#
With a live kernel on CUDA or MPS, the Compute SSB control, or
viewer.compute_ssb(), runs quantem.gpu.SSB(...).find_aberrations(...) on the
current 4D frame. It attaches the SSB phase and the aligned DPC row/col maps as
virtual-image sources and switches the virtual image to the phase. Supply the
microscope calibration when you open the viewer:
loaded = load(path)
viewer = Show4DSTEM(
loaded,
ssb_voltage_kV=300,
ssb_semiangle_mrad=30,
ssb_scan_sampling_A=0.5,
)
phase = viewer.compute_ssb() # 200 trials plus Nelder-Mead refinement by default
The beam energy comes from ssb_voltage_kV, and the fit uses every detected
bright-field pixel (ssb_bf_intensity_threshold, ssb_bf_radius).
ssb_n_trials, ssb_refine, and ssb_seed control the search. Compute SSB
needs one 4D frame on a square 128, 256, or 512 scan grid. An encoded
acquisition goes to SSB without a dense copy; SSB decodes only the
bright-field disk.
Multiple grid#
Use view_mode="multiple" when the extra frame axis represents multiple
acquisitions that should be inspected side by side. The viewer keeps the
standard diffraction-panel workflow: one shared detector ROI, one shared scan
cursor, and one Dataset slider. The virtual-image side becomes a grid of ready
frames or datasets. Use view_mode="single" for one-at-a-time browsing. A list
of acquisitions from load and Show4DSTEM.from_folder(...) open in multiple
mode by default.
from quantem.gpu.io import load
from quantem.widget import Show4DSTEM
widget = Show4DSTEM(
load([path1, path2, path3, path4]),
view_mode="multiple",
compare_cols=2,
compare_panel_gap_px=0,
compare_max_panels=4,
compare_dp_mode="selected",
)
widget
compare_cols=0 lets the frontend pick a responsive layout. compare_layout
accepts "side" and "top" for placing the shared diffraction panel next to
or above the multiple grid. Positive compare_cols values are treated as the
maximum grid columns on desktop; narrow/mobile viewports cap the grid at two
columns so the tiles remain touch-friendly. A comparison of encoded
acquisitions never stacks them into one 5D array; each tile reads its own
acquisition.
When the visible set is larger than compare_max_panels, the multiple grid is
paged like Show2D/Show3D galleries. compare_page_idx is zero-based and
compare_page_count is synced widget state, so notebooks can drive pages
programmatically or save/restore the current page with the rest of the widget
state.
Set compare_group_mode="all" or call widget.show_compare_all_groups() to
collapse all visible pages into one dense comparison grid. Call
widget.show_compare_paged_groups() to restore page-by-page browsing. The
shared diffraction panel keeps using the active page for compare_dp_mode="average"
so scan-position drags stay responsive even when the virtual-image grid is
showing every reduced panel.
compare_panel_gap_px=0 renders the virtual-image grid edge-to-edge for dense
screening. Increase it when a report or presentation needs visible gutters
between panels. Mouse-wheel or trackpad scroll over a multiple tile zooms the
shared virtual-image grid instead of scrolling the page; double-click a tile to
reset the compare zoom. The single-panel diffraction and virtual-image canvases
use the same scroll-to-zoom behavior.
The constructor default is compare_dp_mode="average", which shows the mean
diffraction pattern at the current scan position across visible ready multiple
panels. For tilt-series and dataset-review demos, prefer
compare_dp_mode="selected" so the diffraction panel follows the clicked or
active dataset. Use "average" only when the mean diffraction pattern across
the current visible page is the intended measurement.
Multiple panel curation is stored on the widget, so a notebook can reuse the same state in a later cell or saved HTML export:
widget.set_compare_panel_order(["scan-3", "scan-0", "scan-1", "scan-2"])
widget.hide_compare_panel("scan-4")
widget.star_compare_panel("scan-3")
state = widget.state_dict()
another_widget.load_state_dict(state)
The GUI exposes the same state: the star and hide icons live on each multiple tile, the reorder button enables drag-and-drop or click-then-click ordering, and the multiple toolbar can restore hidden panels or reset the saved panel state.
Exporting reports and raw 4D viewers#
Show4DSTEM has two HTML export modes with different goals:
Export kind |
Use when |
Data included |
Memory behavior |
|---|---|---|---|
|
Sharing a curated folder/multiple-grid result or saving a compact screening report |
Static PNG virtual-image pages plus a representative diffraction pattern |
Page-aware; folder data is rendered page by page and raw 4D tensors are not embedded |
|
The recipient must drive the actual 4D dataset offline in the browser |
Raw 4D payload, explicitly encoded as |
Can be large; use dtype and binning deliberately before sending. Needs the 4D array in the Python session, so it applies to viewers opened from an array or tensor |
Quick decision rule:
Use
reportfirst for large folders, many masters, starred/hidden panel curation, and collaborator screening.Use
interactiveonly when the exported browser page must still recompute new detector ROIs from raw 4D data.Use the CLI
quantem show4dstem ... --backend webgpu --html --bin 1 --dtype uint8for a terminal-made full-detector browser artifact that reads source H5 files.Use
--dtype uint16when a no-notebook user needs the wider detector-count range and accepts the larger browser/GPU memory footprint.Keep the original notebook or Python script when the recipient needs to keep doing analysis, not just view an export.
Report exports are the safe default for large folders:
widget.export_html(
"show4dstem_report.html",
export_kind="report",
dataset_scope="unhidden", # "current_page", "starred", or "all" also work
scan_bin=2, # mean-bin real space for smaller PNG pages
det_bin=8, # mean-bin the representative DP thumbnail
dtype="uint8",
)
Interactive raw exports remain available when the exported HTML needs the
backendless Show4DSTEM widget, not just a report. They embed the viewer’s 4D
array; a viewer over io.load acquisitions reads it in small scan windows, so
the export copies the whole acquisition to host memory. Open the viewer on a
bounded loaded.read(scan_region=...) to export part of a scan:
widget = Show4DSTEM(array)
widget.export_html(
"show4dstem_interactive.html",
export_kind="interactive",
dtype="uint8", # "uint16" keeps the exact integer range but is larger
scan_bin=2, # real-space mean bin before embedding raw 4D
det_bin=4, # detector mean bin before embedding raw 4D
)
Full native interactive export, without detector or scan binning:
widget.export_html(
"show4dstem_full_interactive.html",
export_kind="interactive",
dtype="uint16",
scan_bin=1,
det_bin=1,
)
Equivalent CLI for users who do not want a notebook:
quantem show4dstem /data/session --backend webgpu --html --count 7 --bin 1 --dtype uint8 --out ~/Downloads
Both scan_bin and det_bin use mean binning, not summing. This keeps display
exports from saturating uint8 and makes the file-size estimate in the GUI
match the binned payload shape. The GUI export menu labels the same distinction
as HTML report: static PNG, no raw 4D and HTML interactive raw 4D.
The interactive section offers a size-sorted ladder of uint8/uint16,
real-space-bin, and detector-bin presets so users can choose between a quick
preview, a practical offline browser file, and exact raw 4D HTML deliberately.
Choose export dtype deliberately:
Dtype |
Use for |
Do not use for |
|---|---|---|
|
Compact browser payloads, first-pass screening, tutorials, and audited low-count data. |
Claims that need high detector counts unless values above 255 are known not to matter. |
|
Full/native interactive exports, detector-detail review, and count-range-preserving browser payloads. |
Small public demos or reports where the recipient only needs rendered virtual-image pages. |
uint8 uses one byte per exported detector pixel and may clip/narrow detector
values above 255. uint16 uses two bytes per exported detector pixel and can
produce much larger artifacts, but preserves the wider 0-65535 integer range.
The dtype choice matters for export_kind="interactive" because that path sends
raw 4D data to the browser. A report export remains a rendered PNG review
artifact even if dtype="uint16" is passed.
For LLM agents and scripted docs, prefer these explicit parameter names:
widget.export_html(
path="show4dstem_report.html",
export_kind="report",
dataset_scope="unhidden",
dtype="uint8",
scan_bin=2,
det_bin=8,
)
Do not describe a report export as “raw” or “exact”; it is a rendered review
artifact. Do not describe a uint8 interactive export as exact unless the
detector count range was audited. Always mention the scan_bin and det_bin
values in a figure caption, notebook markdown cell, or handoff note.
See the copyable Show4DSTEM export recipes for terminal commands, report settings, and interactive raw-4D settings.
WebGPU HDF5 Folder#
For large HDF5 acquisitions, prefer the CLI folder export. It keeps the
compressed *_master.h5 family on disk and lets the browser read local HDF5
chunks through a folder grant or local range server. Startup should not wait on
a CLI-time full-stack conversion. Do not make normal CLI launches depend on
precomputed profile.bin/com.bin sidecars; those are generated products, not
the fast click-to-open path.
quantem show4dstem /path/to/h5_family --backend webgpu --html --bin 1 --count 1
quantem show4dstem /path/to/h5_family --backend webgpu --html --bin 1 --count 7
The folder writes Show4DSTEM.command, a hidden .viewer/ server/viewer
folder, and linked tilt_NN_master.h5/tilt_NN_data_*.h5 files. On macOS,
double-clicking the command starts a local range-capable server and opens the
vendored viewer page in Chrome. The exported page ships the required browser
widget-manager assets, so the handoff does not depend on public CDNs. Rerunning
the same CLI into the same --out replaces the generated
*_show4dstem_webgpu viewer folder, which prevents stale HTML or stale links
from surviving a regeneration.
Double-clicking index.html directly is also supported in Chromium browsers
with the File System Access API: click Open data folder and grant the
export folder that contains index.html, .viewer/, and the anonymous H5
links. Use Show4DSTEM.command when you want the no-prompt local-server path.
Use h5_uint8_lossless=True only after auditing the detector counts. It enables
the low8 WebGPU decode path and is lossless only when every corrected good-pixel
count fits in 8 bits. Leave it off for exact native uint16 browsing; the bundle
then injects __QT_H5_DECODE_DTYPE="u2" and keeps the high bitplanes.
The browser loader also honors these optional globals when injected before the widget bundle:
Global |
Purpose |
|---|---|
|
|
|
Detector mean-binning factor for local HDF5 loads |
|
|
|
Source scan shape when loading a cropped region |
|
HTTP fetch/decode queue depth |
|
Browser local-file read/decode grouping |
For performance signoff, inspect window.__loadprof after load and
window.__sh4dLiveViStats while dragging the virtual detector. The maintainer
performance notes record the current seven-panel WebGPU compare-grid signoff.
Reference#
- class quantem.widget.show4dstem.Show4DSTEM(*args: t.Any, **kwargs: t.Any)#
Bases:
StaticFallbackMixin,AnyWidgetFast interactive 4D-STEM viewer with advanced features.
Optimized for speed with binary transfer and pre-normalization. Works with NumPy and PyTorch arrays.
- Parameters:
data (Dataset4dstem or array_like) – Dataset4dstem object (calibration auto-extracted), 4D array of shape (scan_rows, scan_cols, det_rows, det_cols), or 5D array of shape (n_frames, scan_rows, scan_cols, det_rows, det_cols) for time-series or tilt-series data.
scan_shape (tuple, optional) – If data is flattened (N, det_rows, det_cols), provide scan dimensions.
sampling (tuple of 4 floats, optional) – Pixel size per axis
(scan_row, scan_col, k_row, k_col). Scalar broadcasts to all four axes. Defaults to(1, 1, 1, 1). Auto-extracted fromDataset4dstemif not provided.units (list of 4 str, optional) – Unit string per axis. Common:
["A", "A", "mrad", "mrad"]. Defaults to["pixels"] * 4. Auto-extracted fromDataset4dstemif not provided.center (tuple[float, float], optional) – (center_row, center_col) of the diffraction pattern in pixels. If not provided, defaults to detector center.
bf_radius (float, optional) – Bright field disk radius in pixels. If not provided, estimated as 1/8 of detector size.
precompute_virtual_images (bool, default True) – Precompute BF/ABF/LAADF/HAADF virtual images for preset switching.
DPC_row (array_like, optional) – Precomputed real-space maps to expose in the image panel alongside the ROI-derived virtual image. Each map must be real-valued with shape
(scan_rows, scan_cols)or(n_frames, scan_rows, scan_cols).SSBis the phase map; complex SSB inputs are rejected so amplitude is not selected silently.DPC_col (array_like, optional) – Precomputed real-space maps to expose in the image panel alongside the ROI-derived virtual image. Each map must be real-valued with shape
(scan_rows, scan_cols)or(n_frames, scan_rows, scan_cols).SSBis the phase map; complex SSB inputs are rejected so amplitude is not selected silently.SSB (array_like, optional) – Precomputed real-space maps to expose in the image panel alongside the ROI-derived virtual image. Each map must be real-valued with shape
(scan_rows, scan_cols)or(n_frames, scan_rows, scan_cols).SSBis the phase map; complex SSB inputs are rejected so amplitude is not selected silently.vi_source ({"roi", "DPC_row", "DPC_col", "SSB"}, optional) – Initial image-panel source. Defaults to the ROI-derived virtual image.
frame_dim_label (str, optional) – Label for the frame dimension when 5D data is provided. Defaults to “Frame”. Common values: “Tilt”, “Time”, “Focus”.
view_mode ({"single", "multiple"}, default "single") – Scientific layout mode.
"single"shows one selected frame/dataset."multiple"shows a grid of virtual images for the first ready frames/datasets while sharing the detector ROI and scan cursor with the existing diffraction panel.compare_layout ({"side", "top"}, default "side") – Frontend layout hint for
view_mode="multiple". The current widget renders"side"as the default shared-DP plus virtual-image grid.compare_cols (int, default 0) – Number of columns in the compare virtual-image grid.
0selects a responsive automatic layout.compare_grid_width_px (int, default 0) – Desktop width of the compare virtual-image grid in CSS pixels.
0uses the default responsive width. The frontend resize handle updates this value without changing the diffraction-panel size.compare_panel_gap_px (int, default 0) – Gap between compare virtual-image panels in CSS pixels.
0gives a dense, edge-to-edge grid for browsing many datasets; larger values can be useful in reports.compare_max_panels (int, default 12) – Maximum ready frames/datasets included in the compare grid.
compare_group_mode ({"paged", "all"}, default "paged") – Compare-grid grouping behavior.
"paged"shows one group of up tocompare_max_panelspanels at a time."all"collapses all visible groups into one dense grid while still computing lazy datasets in page-sized batches.compare_dp_mode ({"average", "selected", "all"}, default "average") – Diffraction panel source in compare mode.
"average"displays the mean diffraction pattern at the current scan position across visible ready compare panels."selected"displays the active frame/dataset."all"adds a native pattern for each visible comparison dataset, with shared absolute contrast and the same scan position. This mode currently requires a live kernel and hardware WebGPU rendering; it is not a kernel-free export mode. The primary panel retains detector tools.compare_cache_pages (int, default 16) – Number of reduced compare-grid virtual-image pages to keep in host memory. This caches BF/ABF/ADF/HAADF thumbnails across page changes and is separate from raw 4D GPU residency.
compare_cache_max_bytes (int, optional) – Host-memory cap for the reduced compare-grid page cache. Defaults to 512 MiB. Set to
0orcompare_cache_pages=0to disable.ui_mode ({"interactive", "presentation", "report", "minimal"}, default "interactive") – Preset for viewer chrome. Explicit
show_*keyword arguments override the preset.show_title (bool, default True) – Show the top title row.
show_controls (bool, default True) – Show the live control UI. Set
Falsefor a permanently clean display.controls_collapsed (bool, default False) – Start with the live control UI collapsed. Unlike
show_controls=False, Python can callexpand_controls()later.show_stats (bool, default True) – Show mean/min/max/std readout bars under the DP, virtual image, and FFT.
show_scale_bar (bool, default True) – Draw scale bars on the diffraction and virtual-image canvases.
debug (bool, default False) – Show a compact frontend FPS/debug badge in the widget title row.
save_state (bool, default False) – When False, saved notebooks omit heavy 4D buffers and keep a compact static preview for cold reopen. Set True only for small widgets that must reopen interactively without rerunning the kernel.
notebook_preview_format ({"jpeg", "webp", "png"} or None, default "jpeg") – Static preview format used when
save_state=False. Set toNonefor live-only notebooks that should publish only the interactive view.notebook_preview_quality (int, default 88) – Lossy preview quality for JPEG/WebP, from 1 to 100. Ignored for PNG.
notebook_preview_max_px (int, default 512) – Longest panel side for the saved-notebook preview.
Examples
>>> import numpy as np >>> from quantem.widget.show4dstem import Show4DSTEM
4D NumPy array
(scan_rows, scan_cols, det_rows, det_cols):>>> Show4DSTEM(np.random.rand(64, 64, 128, 128))
PyTorch CUDA tensor:
>>> import torch >>> Show4DSTEM(torch.rand(64, 64, 128, 128, device="cuda"))
With explicit calibration (real-space Å, k-space mrad):
>>> Show4DSTEM(np.random.rand(64, 64, 128, 128), ... sampling=(2.39, 2.39, 0.46, 0.46), ... units=["A", "A", "mrad", "mrad"])
quantem
Dataset4dstem— calibration + units auto-extracted:>>> from quantem.core.datastructures import Dataset4dstem >>> ds = Dataset4dstem.from_array(np.random.rand(64, 64, 128, 128)) >>> Show4DSTEM(ds)
Flattened scan
(N, det_rows, det_cols)with explicit scan shape:>>> Show4DSTEM(np.random.rand(4096, 128, 128), scan_shape=(64, 64))
Custom BF disk center and radius (overrides auto-detection):
>>> Show4DSTEM(np.random.rand(64, 64, 128, 128), ... center=(64, 64), bf_radius=12)
5D time-series or tilt-series
(n_frames, scan_r, scan_c, det_r, det_c):>>> Show4DSTEM(np.random.rand(20, 64, 64, 128, 128), frame_dim_label="Tilt")
Raster animation (scan path through 4D dataset):
>>> w = Show4DSTEM(np.random.rand(64, 64, 128, 128)) >>> w.raster(step=2, interval_ms=50)
Static export to PDF or PNG (single panel or all four):
>>> w = Show4DSTEM(np.random.rand(64, 64, 128, 128)) >>> w.save_image("dp.pdf", view="diffraction") >>> w.save_image("all.pdf", view="all")
- set_vi_product_map(label: str, value: Any) Self#
Attach or replace a static virtual-image product map.
- set_vi_preset_map(label: str, value: Any) Self#
Attach or replace a static BF/ABF/ADF/HAADF preset map.
- compute_ssb(*, set_source: bool = True, verbose: bool = False, **kwargs) ndarray#
Compute SSB phase in the live kernel and attach it as the SSB map.
- export_html(path: str | Path | None = None, *, title: str | None = None, mode: str = 'single', encoding: str | None = None, downsample: int | None = None, dtype: str = 'uint8', det_bin: int = 1, scan_bin: int = 1, real_space_bin: int | None = None, export_kind: str = 'interactive', dataset_scope: str = 'unhidden') Path#
Write a standalone HTML viewer.
export_kind="interactive"packages raw 4D data into the standalone browser-compute widget.export_kind="report"writes a compact static virtual-image report with no raw 4D payload, which is the safe export path for lazy folder-backed viewers.det_binbins detector pixels by mean overdet_bin x det_binblocks.scan_bin(or aliasreal_space_bin) bins scan pixels by mean overscan_bin x scan_binblocks.dtypemay be"uint8"or"uint16".
- get_state(key=None, drop_defaults=False)#
Trait state for comm sync and notebook embedding.
ipywidgets calls this with
key=Noneto snapshot the FULL state that gets written into the saved notebook’smetadata.widgets. Whensave_stateis False we drop the heavy buffers from that snapshot so a plain Show4DSTEM does not bake the packed 4D stack into the .ipynb. Targeted syncs (keyis a name or set, used by hold_sync / send_state during live rendering - e.g. the deferred virtual_image_bytes / frame_bytes re-send on mount) are untouched, so the frontend still receives every buffer normally.save_state=Trueembeds everything so a reopened notebook restores the interactive offline widget without a kernel.
- collapse_controls() Self#
Collapse the live control UI programmatically.
- expand_controls() Self#
Expand the live control UI.
- toggle_controls() Self#
Toggle the collapsed state of the live control UI.
- free()#
Free GPU memory held by this widget.
Drops EVERY reference to the data tensor and flushes the allocator pools, so the stack actually leaves VRAM (no kernel restart needed). The data is held in FOUR places, not one:
self._dataplus the compute backend’s cached_t/_4d/_flatviews (self._compute_backend) plus the per-opself._compute_forcache - missing any one keeps the whole stack pinned. And the storage is cupy-owned (io.loaddecompresses with cupy; the widget wraps it viafrom_dlpack), so when the torch refs die the memory returns to the CUPY pool, whichtorch.empty_cachecannot release - the cupy pool is freed too. Call before loading a new dataset.Examples
>>> w.free() # release the full stack from VRAM >>> del result # free the source array
- property position: tuple[int, int]#
Current scan position as (row, col) tuple.
- property scan_shape: tuple[int, int]#
Scan dimensions as (rows, cols) tuple.
- property detector_shape: tuple[int, int]#
Detector dimensions as (rows, cols) tuple.
- set_path(points: list[tuple[int, int]], interval_ms: int = 100, loop: bool = True, autoplay: bool = True) Self#
Set a custom path of scan positions to animate through.
- Parameters:
points (list[tuple[int, int]]) – List of (row, col) scan positions to visit.
interval_ms (int, default 100) – Time between frames in milliseconds.
loop (bool, default True) – Whether to loop when reaching end.
autoplay (bool, default True) – Start playing immediately.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> widget.set_path([(0, 0), (10, 10), (20, 20), (30, 30)]) >>> widget.set_path([(i, i) for i in range(48)], interval_ms=50)
- play() Self#
Start playing the path animation.
- pause() Self#
Pause the path animation.
- stop() Self#
Stop and reset path animation to beginning.
- goto(index: int) Self#
Jump to a specific index in the path.
- raster(step: int = 1, bidirectional: bool = False, interval_ms: int = 100, loop: bool = True) Self#
Play a raster scan path (row by row, left to right).
This mimics real STEM scanning: left→right, step down, left→right, etc.
- Parameters:
step (int, default 1) – Step size between positions.
bidirectional (bool, default False) – If True, use snake/boustrophedon pattern (alternating direction). If False (default), always scan left→right like real STEM.
interval_ms (int, default 100) – Time between frames in milliseconds.
loop (bool, default True) – Whether to loop when reaching the end.
- Returns:
Self for method chaining.
- Return type:
- roi_circle(radius: float | None = None) Self#
Switch to circle ROI mode for virtual imaging.
In circle mode, the virtual image integrates over a circular region centered at the current ROI position (like a virtual bright field detector).
- Parameters:
radius (float, optional) – Radius of the circle in pixels. If not provided, uses current value or defaults to half the BF radius.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> widget.roi_circle(20) # 20px radius circle >>> widget.roi_circle() # Use default radius
- roi_point() Self#
Switch to point ROI mode (single-pixel indexing).
In point mode, the virtual image shows intensity at the exact ROI position. This is the default mode.
- Returns:
Self for method chaining.
- Return type:
- roi_square(half_size: float | None = None) Self#
Switch to square ROI mode for virtual imaging.
In square mode, the virtual image integrates over a square region centered at the current ROI position.
- Parameters:
half_size (float, optional) – Half-size of the square in pixels (distance from center to edge). A half_size of 15 creates a 30x30 pixel square. If not provided, uses current roi_radius value.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> widget.roi_square(15) # 30x30 pixel square (half_size=15) >>> widget.roi_square() # Use default size
- roi_annular(inner_radius: float | None = None, outer_radius: float | None = None) Self#
Set ROI mode to annular (donut-shaped) for ADF/HAADF imaging.
- Parameters:
inner_radius (float, optional) – Inner radius in pixels. If not provided, uses current roi_radius_inner.
outer_radius (float, optional) – Outer radius in pixels. If not provided, uses current roi_radius.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> widget.roi_annular(20, 50) # ADF: inner=20px, outer=50px >>> widget.roi_annular(30, 80) # HAADF: larger angles
- roi_rect(width: float | None = None, height: float | None = None) Self#
Set ROI mode to rectangular.
- Parameters:
width (float, optional) – Width in pixels. If not provided, uses current roi_width.
height (float, optional) – Height in pixels. If not provided, uses current roi_height.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> widget.roi_rect(30, 20) # 30px wide, 20px tall >>> widget.roi_rect(40, 40) # 40x40 rectangle
- auto_detect_center(update_roi: bool = True) Self#
Automatically detect BF disk center and radius using centroid.
This method analyzes the summed diffraction pattern to find the bright field disk center and estimate its radius. The detected values are applied to the widget’s calibration (center_row, center_col, bf_radius).
- Parameters:
update_roi (bool, default True) – If True, also update ROI center and recompute cached virtual images. Set to False during __init__ when ROI is not yet initialized.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> widget = Show4DSTEM(data) >>> widget.auto_detect_center() # Auto-detect and apply
- save_image(path: str | Path, view: str | None = None, position: tuple[int, int] | None = None, frame_idx: int | None = None, format: str | None = None, include_metadata: bool = True, metadata_path: str | Path | None = None, include_overlays: bool | None = None, include_scalebar: bool | None = None, restore_state: bool = True, dpi: int | None = None) Path#
Save the current visualization as PNG or PDF.
- Parameters:
path (str or pathlib.Path) – Output image path.
view (str, optional) – One of: “diffraction”, “virtual”, “fft”, “all”.
position (tuple[int, int], optional) – Temporary scan position override as (row, col) for this export.
frame_idx (int, optional) – Temporary frame index override for 5D data.
format (str, optional) – “png” or “pdf”. If omitted, inferred from file extension.
include_metadata (bool, default True) – If True, writes JSON metadata next to the image.
metadata_path (str or pathlib.Path, optional) – Override metadata JSON path.
include_overlays (bool, default True) – Draw ROI/profile/crosshair overlays on exported panels.
include_scalebar (bool, default True) – Draw panel scale bars on exported panels.
restore_state (bool, default True) – If True, temporary position/frame overrides are reverted after export.
dpi (int, optional) – Export DPI metadata.
- Returns:
The written image path.
- Return type:
pathlib.Path
- property compare_ordered_panels: list[int]#
Frame/dataset indices in the current compare display order.
- property compare_visible_panels: list[int]#
Frame/dataset indices currently visible in compare mode.
Replace the hidden compare panel set by index or exact label.
- hide_compare_panel(*panels: int | str) Self#
Hide one or more compare panels by zero-based index or exact label.
- show_compare_panel(*panels: int | str) Self#
Restore one or more hidden compare panels by index or exact label.
- show_all_compare_panels() Self#
Restore every compare panel.
- preload_all_datasets(*, background: bool = True) Self#
Keep every unhidden lazy dataset in VRAM when the full set fits.
The decision uses
Dataset5dstem.residency_plan(), which knows each frame’s shape and dtype without reading it. If memory becomes unavailable after planning, Show4DSTEM retains full-resolution paging and reports the fallback without surfacing a raw backend allocation error.
- stop_dataset_preload(*, wait: bool = False) Self#
Stop automatic all-dataset loading after the current file read.
- wait_for_dataset_preload(timeout: float | None = None) Self#
Wait for an automatic all-dataset preload, primarily for verification.
- poll_folder() list[int]#
Append newly completed folder masters to the comparison.
Only widgets created by
Show4DSTEM.from_folder(...)watch a folder. A new master must report the same complete header signature on two consecutive polls before it is loaded, so a file still being written is never read. Returns the dataset indices this poll appended.
- wait_for_folder(timeout: float | None = None) Self#
Block until the masters found when
from_folderopened are loaded.
- watch_folder(*, interval: float = 2.0) Self#
Poll the attached folder in the background and append ready masters.
- stop_folder_watch() None#
Stop the background folder watcher, if one was started.
- close() None#
Stop background work and close the widget comm.
- set_compare_panel_order(panels: Sequence[int | str]) Self#
Set the compare-grid display order by panel index or exact label.
- reset_compare_panel_order() Self#
Restore the natural compare panel order.
- move_compare_panel(panel: int | str, position: int) Self#
Move one compare panel to a zero-based display position.
- set_compare_page(page: int) Self#
Show a zero-based page of compare-grid panels.
- next_compare_page() Self#
Advance the compare grid by one page.
- previous_compare_page() Self#
Move the compare grid back by one page.
- show_compare_paged_groups() Self#
Show one compare-grid group/page at a time.
- show_compare_all_groups() Self#
Collapse all visible compare-grid groups into one dense grid.
- set_compare_starred_panels(panels: Sequence[int | str] | int | str) Self#
Replace the set of starred compare panels by index or exact label.
- star_compare_panel(panel: int | str) Self#
Mark a compare panel with a star.
- unstar_compare_panel(panel: int | str) Self#
Clear the star on a compare panel.
- warm_compare_cache(presets: Sequence[str] = ('bf', 'abf', 'adf', 'haadf'), *, background: bool = True) Self#
Cache standard detector views without retaining every raw 4D master.
Lazy
Dataset5dstemseries are loaded in memory-aware batches. All requested detector presets are reduced while each batch is resident, then only the small 2D virtual images remain in host memory.
- stop_compare_cache_warm(*, wait: bool = False) None#
Stop background detector-preset caching after the current GPU batch.
Note
The generated reference above is the universal base viewer. The public
quantem.widget.Show4DSTEM factory accepts the same viewer options, plus
scan_region= for an acquisition from io.load; backend="webgpu",
offline_codec, and data_url apply to array input.
- quantem.widget.show4dstem_factory.from_folder(folder, *, pattern: str = '*_master.h5', recursive: bool = True, scan_size: int | None = None, max_masters: int | None = None, min_masters: int | None = None, ready_only: bool = True, backend: str = 'auto', device: int | None = None, view_mode: str = 'multiple', columns: int | None = None, page_size: int | None = None, watch: bool = True, watch_interval: float = 2.0, verbose: bool = False, **viewer_kwargs)#
Open every ready
*_master.h5in a folder as one live comparison viewer.Each acquisition is loaded with
quantem.gpu.io.load()into encoded storage on one CUDA device or the Apple GPU, at full detector resolution; a 512 x 512 x 192 x 192 uint16 Arina scan (18 GiB dense) occupies about 0.1 GiB, so the whole folder stays resident without binning or paging. Masters that are not completely written are skipped (ready_only), and when the folder mixes geometries the largest group sharing one scan and detector shape is shown. The viewer opens once the first master is loaded; the others join in the background (wait_for_folder()blocks until they have), andfree()orclose()releases them.columnssets the grid width andpage_sizethe number of datasets per page. Withwatch=True(default) a master that completes while the viewer is open is appended once its headers are unchanged on two polls, everywatch_intervalseconds. Setwatch=Falsefor a fixed snapshot.Examples
>>> viewer = Show4DSTEM.from_folder("/data/session", scan_size=512)
Interactive controls#
With a running kernel these recompute on the GPU backend (CUDA or MPS). In
backend="webgpu" mode, the same controls run in the browser with no
Python round trip - see Performance.
Control |
Trait |
Expected effect |
|---|---|---|
Detector position (drag on diffraction) |
|
Virtual image recomputes for that probe position |
BF aperture radius |
|
Bright-field disk grows/shrinks; virtual image updates |
Aperture center |
|
Recenters the detector on the unscattered beam |
Detector ROI mode |
|
Switch BF / annular / rectangular detector |
Annular inner / outer |
|
ADF annulus geometry |
Virtual-image ROI |
|
Pick a real-space region to average its diffraction |
FFT toggle |
|
Power spectrum of the virtual image |
Multiple grid |
|
Shows ready frames/datasets as synchronized virtual images sharing the detector ROI and scan cursor; |
Multiple DP source |
|
Shows either the average DP across visible multiple panels or the selected panel’s DP |
Multiple panel state |
|
Saves/reuses panel order, hidden panels, and starred picks across cells, state files, and HTML export |
Viewer chrome preset |
|
Applies shared display presets; see Viewer UI controls |
Control visibility |
|
Permanently remove controls or programmatically collapse/expand them for clean exports |
Title visibility |
|
Top title row shows/hides |
Stats visibility |
|
DP, virtual-image, and FFT stats bars show/hide |
Scale bar visibility |
|
DP and virtual-image scale bars show/hide |
Scan-path playback |
|
Sweeps the probe across the scan |
k-space calibration |
|
Diffraction axes read in mrad when calibrated |