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.
Public import:
from quantem.widget import Show4DSTEM, read_4dstem
Show4DSTEM is one operator-facing factory. It chooses the viewer from its
input:
a file path (
*_master.h5, HDF5 or.npy) opens asread_4dstem(path)does: aquantem.gpu.io.Dataset4dstemGPUwhenquantem.widget[cuda]or[mps]is installed and a GPU is present, else a quantem coreDataset4dstemread whole into host memory (one printed line with the time taken; a file larger than 80% of the available memory is refused, never binned or cropped) and reduced ondevice=;a
Dataset4dstemGPU(fromread_4dstemon a GPU orquantem.gpu.io.load) opens as a live view over its GPU storage;a quantem core
Dataset4dstem(numpy- or tensor-backed) or the widget’s stand-in opens as its array or tensor, with its name as the title and its calibration on the scale bars;a list of datasets with one scan and detector shape opens as a comparison grid labelled by dataset name (the file name for
read_4dstem);a NumPy array or Torch tensor opens in the base viewer, which also supports browser WebGPU compute and offline export.
The viewer shows the same virtual images, patterns and fitted disk for each
of these inputs. Counts are stored in
the narrowest integer type that holds the largest count, which is known only
after reading, so the refusal states the size for each type the file’s dtype
and detector bit depth allow, for example needs between 9.7 GB and 77.3 GB
with 19.3 GB as uint16 for a 512 x 512 scan of 192 x 192 uint32 frames.
device= places a NumPy array or a dense file read: "auto" (CUDA, then
Apple MPS, then CPU, printed once), "cuda", "cuda:N", "mps" or "cpu".
A tensor stays on its device unless device= names another. Virtual images,
the mean pattern and scan-ROI patterns of a dense tensor are integer sums in
torch on that device, divided once in float64 for a mean; quantem.gpu’s
encoded path gives the same numbers.
Canonical forms:
# One acquisition: on the GPU with quantem.gpu (CUDA or MPS), else read densely.
viewer = Show4DSTEM(read_4dstem(path))
# Part of the scan: (row_start, row_stop, col_start, col_stop), exclusive stops.
viewer = Show4DSTEM(read_4dstem(path), scan_region=(128, 384, 128, 384))
# Several acquisitions: one shared diffraction ROI, one virtual image per file.
viewer = Show4DSTEM(read_4dstem([path1, path2, path3]), compare_cols=3)
# A master by path: the same as Show4DSTEM(read_4dstem(path)).
viewer = Show4DSTEM("/data/session/scan_master.h5")
# Every ready master in a folder; masters completed later are appended.
viewer = Show4DSTEM.from_folder("/data/session")
# An array in the Python session: counts packed for browser compute and standalone export.
viewer = Show4DSTEM(array, offline=True)
viewer.export_html("show4dstem.html")
Use the CLI quantem show4dstem ... --backend webgpu --html folder export for
a standalone browser viewer over large HDF5 acquisitions.
Use the selected scan region in analysis#
Choose Rect in the virtual-image ROI controls, drag the region, then read its bounds from the same live widget:
region = viewer.selected_scan_region()
row_start, row_stop, col_start, col_stop = region
patch = data[row_start:row_stop, col_start:col_stop]
The tuple uses exclusive stops and includes integer pixel centers on the rectangle boundary, exactly as the viewer does. With ROI off it returns the whole displayed scan. A circle cannot be represented by one rectangular crop; select Rect or turn ROI off before requesting bounds.
Coordinates are relative to the input displayed by the viewer. For
scan_region=(100, 164, 200, 264), add row origin 100 and column origin 200
before reading the original acquisition. A strided preview likewise returns
preview indices, not original scan indices. No implicit origin/stride conversion
is performed. Detector-region controls do not change this scan selection.
Encoded acquisitions#
On a GPU, read_4dstem(path) (which calls quantem.gpu.io.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.
offline=True packs a GPU acquisition for the browser as it packs an array:
the acquisition is read in small scan windows into the packed host array, so
the GPU never holds the dense cube, and a pack above the 2 GB budget is skipped
with one printed line. h5_urls= (browser reads of the source files) raises
for an acquisition. export_html 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), 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 (this and the next sections use quantem.gpu directly and need a GPU):
from quantem.gpu.io import load
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
from quantem.gpu.io import load
loaded = load(path)
center, radius = detector.fit_probe(detector.mean(loaded))
Show4DSTEM(loaded, bright_field_center_px=center, bright_field_radius_px=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, bright_field_center_px=center, bright_field_radius_px=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.
Show4DSTEM is one class in quantem.widget.show4dstem: an acquisition from
io.load becomes a bounded view inside the constructor, and every other input
is a dense tensor on its device.
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.
To verify the live path, introduce genuine master/chunk files while one Jupyter widget is mounted and measure both discovery/control paint and the 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#
The default on CUDA and MPS uses 200 initial TPE trials, then jointly
refines aberrations through fifth order with sample tilt and depth.
ptycho.find_aberrations(search="broad") adds broader exploration.
The initial result is provisional; final refinement can change its phase.
With a live kernel on CUDA or MPS, the Compute SSB control, or
viewer.compute_ssb(), runs quantem.gpu’s default fit,
quantem.gpu.DirectPtycho(...).find_aberrations(), on the current 4D frame: the SSB
model fitted to the data together with the sample tilt and depth spread
(tilt-corrected; the fitted C10 is the defocus at mid-depth). It attaches the SSB phase and the aligned DPC row/col maps as
virtual-image sources and switches the virtual image to the phase. The
voltage, the convergence semiangle and the scan step are read from the dataset
or .qem file:
from quantem.gpu.io import load
viewer = Show4DSTEM(load("scan.qem"))
phase = viewer.compute_ssb() # 200 initial trials, then joint fifth-order refinement
control = viewer.compute_ssb(tilt=False) # the same model at tilt 0
The two fits are quantem.gpu’s:
Call |
Method |
Use it for |
|---|---|---|
|
model-based, tilt-corrected |
the default: every fit |
|
model-based, tilt held at 0 |
thin, untilted samples, or to see what tilt correction changes |
The phase-variance fit is retired: compute_ssb(objective=...) prints one line
and runs the model fit.
An input that records no calibration (an array, an HDF5 master) takes it from
the constructor, under the .qem format’s names:
viewer = Show4DSTEM(counts, voltage_kV=300, semiangle_mrad=30, scan_sampling_A=(0.5, 0.5))
scan_sampling_A is the scan step (row, col) in angstrom,
detector_sampling_mrad or detector_sampling_inv_A the detector step
(row, col) in mrad or 1/angstrom per pixel. A keyword that contradicts a
value the input records raises: the dataset or file is the source of its
calibration. The fit uses every detected
bright-field pixel (the compute_ssb keywords
bright_field_intensity_threshold and bright_field_radius_px). The keywords
tilt (True, the default, or False), which the Fit menu sets
(Tilt-corrected, Tilt 0), trials, refinement ("auto", the default,
quantem.gpu’s exact-derivative refinement, or None for the best trial without
one; the retired "newton" and "nelder-mead" print one line and run
"auto") and seed control the search, for example
viewer.compute_ssb(trials=100, seed=0). Lock C10 and Lock C12
hold a coefficient at its slider value; in the tilt-corrected fit C12 is held
alone only at 0, and both locks hold all three coefficients and fit the tilt
and depth spread alone. A manual recompute from the sliders keeps the last
tilt-corrected fit’s tilt and depth spread. The fitted values are the traits
ssb_compute_C10_nm, ssb_compute_C12_nm, ssb_compute_phi12_deg and
ssb_compute_scan_detector_rotation_deg (the physical rotation in [0, 360)),
and the downloadable *_ssb_calibration.json holds them under the same names
with "schema": "quantem.show4dstem-ssb-calibration/3" (its refinement names the local
refinement quantem.gpu ran, or null; objective
what the fit optimized, null for a manual recompute; tilt,
sample_tilt_mrad in the scan frame, depth_spread_nm and tilt_fit_gain
record the sample model, false and null at tilt 0). It is not a
ShowPtycho calibration.json (quantem.ssb-calibration/6), which records the
source file, scan region, phase estimator and session identity. Compute SSB
needs one 4D frame; quantem.gpu centres any scan shape in its FFT grid and the
phase shows the measured positions. 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 datasets from read_4dstem and Show4DSTEM.from_folder(...) open in
multiple mode by default.
from quantem.widget import Show4DSTEM, read_4dstem
widget = Show4DSTEM(
read_4dstem([path1, path2, path3, path4]),
view_mode="multiple",
compare_cols=2,
compare_max_panels=4,
compare_dp_mode="selected",
)
widget
compare_cols=0 lets the frontend pick a responsive layout. 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" to collapse all visible pages into one dense
comparison grid, and compare_group_mode="paged" 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.
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 as lists of panel indices, so a notebook can reuse the same state in a later cell or saved HTML export:
widget.compare_panel_order = [3, 0, 1, 2, 4] # display order
widget.compare_hidden_panels = [4]
widget.compare_starred_panels = [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 --detector-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
detector_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
detector_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,
detector_bin=1,
)
Equivalent CLI for users who do not want a notebook:
quantem show4dstem /data/session --backend webgpu --html --count 7 --detector-bin 1 --dtype uint8 --out ~/Downloads
Both scan_bin and detector_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,
detector_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 detector_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 --detector-bin 1 --count 1
quantem show4dstem /path/to/h5_family --backend webgpu --html --detector-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
viewer page in Chrome. The page carries the browser widget-manager runtime, so
the handoff does not depend on public CDNs or any network. 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.
--dtype uint8 decodes the low byte of each count in the browser (compact
browse); the default uint16 keeps native counts, and the bundle injects
__QT_H5_DECODE_DTYPE="u2" so the high bitplanes are kept.
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 |
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)#
Interactive 4D-STEM viewer.
Drag over the scan to pick a position and see its diffraction pattern; drag or resize the detector ROI on the pattern and the virtual image follows. BF, ABF, ADF and HAADF presets place the ROI around the fitted bright-field disk. Several datasets of one geometry open as a comparison grid with one shared detector.
export_htmlwrites a kernel-less page (the counts run on browser WebGPU) or a static report.- Parameters:
data – A file path (
*_master.h5, HDF5 or.npy), a dataset fromread_4dstem(aDataset4dstemGPUor a quantem coreDataset4dstem), a list of datasets, a 4D(scan_row, scan_col, det_row, det_col)or 5D(n_datasets, ...)array or tensor, or a 3D(positions, det_row, det_col)flat scan withscan_shape. A path opens asread_4dstem(path, device)reads it: on the GPU through quantem.gpu when it is installed and a GPU is present, else densely, block by block ontodevice(one printed line; refused above a memory ceiling, never reduced). None withh5_urlsorqem_urls, whose data the browser reads. A preparedDetectorSessionSourcekeeps its acquisition resident and supplies bounded detector queries, including distributed CUDA sessions. The widget closes that borrowed session onfree(); close its source acquisition separately. Such sessions requireoffline=Falseand do not expose SSB or source cropping here. A loaded scan withdetector_session()prepares that session automatically; pass the scan directly toShow4DSTEM.device – Where a NumPy array or a file is placed and reduced:
"auto"(CUDA, then MPS, then CPU; printed once),"cuda","cuda:N","mps"or"cpu". A tensor stays on its device unless this names another.scan_region –
(row_start, row_stop, col_start, col_stop)of one 4D scan to view, exclusive stops: a GPU acquisition is read only there, a dense scan is cut before it moves to its device.hot_pixel_correction – A file path only:
"median"(default) replaces the detector pixels the master flags with the median of their unflagged 3x3 neighbours (a larger window inside a flagged cluster, half to even, as quantem.gpu’s io.load),"zero"sets them to 0 and"none"keeps the stored values; one line says how many pixels were replaced or left.scan_sampling_A – Calibration of an input that records none (an array, an uncalibrated file): the scan step
(row, col)in angstrom, the detector step(row, col)in mrad or in 1/angstrom per pixel (one of the two), the accelerating voltage in kV and the convergence semiangle in mrad. A dataset or file is the source of its own calibration: these are read from it, and a keyword that contradicts a recorded value raises.detector_sampling_mrad – Calibration of an input that records none (an array, an uncalibrated file): the scan step
(row, col)in angstrom, the detector step(row, col)in mrad or in 1/angstrom per pixel (one of the two), the accelerating voltage in kV and the convergence semiangle in mrad. A dataset or file is the source of its own calibration: these are read from it, and a keyword that contradicts a recorded value raises.detector_sampling_inv_A – Calibration of an input that records none (an array, an uncalibrated file): the scan step
(row, col)in angstrom, the detector step(row, col)in mrad or in 1/angstrom per pixel (one of the two), the accelerating voltage in kV and the convergence semiangle in mrad. A dataset or file is the source of its own calibration: these are read from it, and a keyword that contradicts a recorded value raises.voltage_kV – Calibration of an input that records none (an array, an uncalibrated file): the scan step
(row, col)in angstrom, the detector step(row, col)in mrad or in 1/angstrom per pixel (one of the two), the accelerating voltage in kV and the convergence semiangle in mrad. A dataset or file is the source of its own calibration: these are read from it, and a keyword that contradicts a recorded value raises.semiangle_mrad – Calibration of an input that records none (an array, an uncalibrated file): the scan step
(row, col)in angstrom, the detector step(row, col)in mrad or in 1/angstrom per pixel (one of the two), the accelerating voltage in kV and the convergence semiangle in mrad. A dataset or file is the source of its own calibration: these are read from it, and a keyword that contradicts a recorded value raises.bright_field_center_px – Bright-field disk centre
(row, col)and edge radius in detector pixels. Omitted, the disk a dataset recorded withdata.calibration.fit_detector_sampling(), else that same fit (quantem.gpu.detector.fit_bright_field_disk) of the mean pattern: the BF, ABF, ADF and HAADF presets are multiples of this radius.bright_field_radius_px – Bright-field disk centre
(row, col)and edge radius in detector pixels. Omitted, the disk a dataset recorded withdata.calibration.fit_detector_sampling(), else that same fit (quantem.gpu.detector.fit_bright_field_disk) of the mean pattern: the BF, ABF, ADF and HAADF presets are multiples of this radius.precompute_virtual_images – Compute the four preset images up front so preset clicks are instant.
panel_width_px – Display widths in CSS pixels. The diffraction and virtual-image widths override
panel_width_pxindependently; zero uses the shared default. Resizing changes the display only, never the data or detector sampling.dp_panel_width_px – Display widths in CSS pixels. The diffraction and virtual-image widths override
panel_width_pxindependently; zero uses the shared default. Resizing changes the display only, never the data or detector sampling.vi_panel_width_px – Display widths in CSS pixels. The diffraction and virtual-image widths override
panel_width_pxindependently; zero uses the shared default. Resizing changes the display only, never the data or detector sampling.sync_resize – Resize both panels together when dragging a corner (default True). Set False to resize each panel independently.
view_mode – Comparison grid layout for several datasets:
"multiple"shows one virtual image per dataset,compare_max_panelsper page, with the diffraction pattern of the"selected"dataset or the"average".compare_cols – Comparison grid layout for several datasets:
"multiple"shows one virtual image per dataset,compare_max_panelsper page, with the diffraction pattern of the"selected"dataset or the"average".compare_grid_width_px – Comparison grid layout for several datasets:
"multiple"shows one virtual image per dataset,compare_max_panelsper page, with the diffraction pattern of the"selected"dataset or the"average".compare_max_panels – Comparison grid layout for several datasets:
"multiple"shows one virtual image per dataset,compare_max_panelsper page, with the diffraction pattern of the"selected"dataset or the"average".compare_group_mode – Comparison grid layout for several datasets:
"multiple"shows one virtual image per dataset,compare_max_panelsper page, with the diffraction pattern of the"selected"dataset or the"average".compare_dp_mode – Comparison grid layout for several datasets:
"multiple"shows one virtual image per dataset,compare_max_panelsper page, with the diffraction pattern of the"selected"dataset or the"average".offline – Pack the counts for the browser so the widget works without a kernel (
Nonepacks automatically under a 2 GB budget). A GPU acquisition is read into the pack in small scan windows.offline_dtype – Pack the counts for the browser so the widget works without a kernel (
Nonepacks automatically under a 2 GB budget). A GPU acquisition is read into the pack in small scan windows.h5_urls – Browser WebGPU source over linked HDF5 masters or .qem acquisitions, written by the CLI and
show4dstem.export.write_qem_bundle.qem_urls – Browser WebGPU source over linked HDF5 masters or .qem acquisitions, written by the CLI and
show4dstem.export.write_qem_bundle.scan_shape – Browser WebGPU source over linked HDF5 masters or .qem acquisitions, written by the CLI and
show4dstem.export.write_qem_bundle.detector_shape – Browser WebGPU source over linked HDF5 masters or .qem acquisitions, written by the CLI and
show4dstem.export.write_qem_bundle.
Validate the arguments, place the data on its compute device and set the traits (see the class docstring).
- classmethod 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: str = 'auto', hot_pixel_correction: str = 'median', 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) Self#
Open every ready
*_master.h5in a folder as one live comparison viewer.With quantem.gpu and a GPU, each acquisition is loaded with
quantem.gpu.io.loadinto encoded storage at full detector resolution, so the whole folder stays resident. Without them every master is read densely (show4dstem.reader) into one 5D tensor ondevice; that path cannot watch the folder and refuses a folder larger than the memory ceiling. Masters still being 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). Withwatch=Truea master that completes later is appended once its headers are unchanged on two polls, everywatch_intervalseconds.columnssets the grid width andpage_sizethe datasets per page.Examples
>>> viewer = Show4DSTEM.from_folder("/data/session", scan_size=512)
- export_html(path: str | Path | None = None, *, title: str | None = None, dtype: str = 'uint8', detector_bin: int = 1, scan_bin: int = 1, export_kind: str = 'interactive', dataset_scope: str = 'unhidden') Path#
Write a standalone HTML viewer.
export_kind="interactive"packs the counts (dtypeuint8oruint16, mean-binned bydetector_binon the detector andscan_binon the scan) into a page whose detector ROI runs on browser WebGPU. Packed counts above what one page can load (2 GiB) are refused before any frame is read; the error names the least binning that fits.export_kind="report"writes PNG virtual-image pages for the datasets indataset_scope(unhidden,current_page,starredorall) with no raw 4D payload.
- compute_ssb(*, set_source: bool = True, verbose: bool = False, **kwargs) ndarray#
Reconstruct the SSB phase of the current dataset and show it as the image.
The fit is quantem.gpu’s default
SSB.find_aberrations(), model-based and tilt-corrected: the SSB model fitted to the data together with the sample tilt and depth spread (C10 is then the defocus at mid-depth).compute_ssb(tilt=False)fits the same model with the tilt held at 0.kwargsgo to the solver:tilt(thessb_compute_tilttrait, True by default),trials(default 200;num_trialsis a compatibility alias),refinement("auto", the default, quantem.gpu’s exact-derivative refinement, or None for the best trial without one),seed,aberrations(a dict ofC10_nm,C12_nmandphi12_deg; a missing key is 0),scan_detector_rotation_deg(the physical angle in [0, 360); omitted, the rotation the acquisition records, else the centre-of-mass estimate, as quantem.gpu.SSB chooses it),scan_detector_rotation_flipped,bright_field_center_pxandbright_field_radius_px(omitted, the constructor’s, else the disk quantem.gpu detects),bright_field_intensity_threshold(quantem.gpu’s default when omitted),lock_aberrations,lock_C10andlock_C12. In the tilt-corrected fit the locks are quantem.gpu’sfixed_aberrations: C10 alone, C12 at 0 alone, or both, which hold all three coefficients and fit only the tilt and depth spread.lock_aberrationsreconstructs the given coefficients with the sample tilt and depth spread of the last tilt-corrected fit (none before one, or withtilt=False). The voltage, semiangle and sampling are the widget’s calibration: what the dataset or file records, or the constructor keywords for an input that records none. The aligned DPC maps from the same data are attached beside the phase.
- apply_preset(name: str) Self#
Place the detector ROI on a named preset:
bf,abf,adforhaadf.Annuli are multiples of the bright-field radius (ADF 1 to 2, HAADF 2 to 4). A ring ends at the detector edge nearest the bright-field centre, and one line says so; a preset whose ring would start beyond that edge is refused with one line and the ROI is left unchanged.
- state_dict() dict#
The display state (cursor, ROI, colormaps, layout); no pixel data.
- load_state_dict(state) None#
Restore a
state_dict, its envelope or asavefile; positions and the dataset index are clamped to this data.A file of another
schemaor a keystate_dictdoes not write raises before anything is applied.
- free() None#
Release the data held by this widget so the stack leaves GPU memory.
Detector sessions cache views of the data, so they close first; the allocator caches of every device the data touched are flushed; the acquisitions
from_folderloaded are closed. Call before loading a new dataset into the same kernel. The packed browser copy of the counts (up to the 2 GB offline budget) and the last export go too.
- close() None#
Stop background work, release sessions and close the widget comm.
- poll_folder() list[int]#
Append newly completed folder masters to the comparison.
Only
from_folderwidgets 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.
- save(path: str | Path) None#
Write the display state (not the pixels) to a versioned JSON file.
The envelope records the widget class and its
schemasoload_state_dictcan refuse a file written by another widget or by a release with another layout.
- pattern(row: int, col: int) ndarray#
The diffraction pattern at scan position
(row, col)of the current dataset as float32.
- virtual_image() ndarray#
The image panel as float32: the product map in view, else the detector ROI sum.
- selected_scan_region() tuple[int, int, int, int]#
Return the scan rectangle selected in the virtual image.
Choose Rect in the virtual-image ROI controls, then call this method to use the same integer scan positions in an analysis. With the ROI off, the complete displayed scan is returned. Detector ROI controls do not affect this selection.
- Returns:
(row_start, row_stop, col_start, col_stop)with exclusive stop indices, clipped to the displayed scan. Integer pixel centers on the rectangle boundary are included, matching the viewer’s ROI. Coordinates belong to the input shown by this viewer: a cropped input starts at zero and a strided preview uses preview indices. No acquisition crop origin or stride is added automatically.- Return type:
tuple[int, int, int, int]
- Raises:
ValueError – If a nonrectangular ROI is active or the rectangle contains no scan positions. Select Rect, or turn the ROI off.
Examples
After selecting a rectangle in a live widget:
>>> row_start, row_stop, col_start, col_stop = viewer.selected_scan_region() >>> region = data[row_start:row_stop, col_start:col_stop]
Note
scan_region= applies to an acquisition from io.load; offline and
offline_dtype apply to array and tensor input.
Interactive controls#
For signed differences, use linear scaling and one symmetric absolute range for each image type. These limits also apply to every comparison tile:
viewer.dp_scale_mode = viewer.vi_scale_mode = "linear"
viewer.dp_colormap = viewer.vi_colormap = "RdBu_r"
viewer.dp_vmin, viewer.dp_vmax = -10.0, 10.0
viewer.vi_vmin, viewer.vi_vmax = -10.0, 10.0
With both absolute bounds set, the control shows Fixed color limits and
their values instead of inactive percentile handles. Update the bounds from
Python, or set both to None to restore the histogram controls. The primary
DP colorbar updates with the active display limits.
Choose the limits in your data’s units. In multiple view, a virtual image is the mean over the selected detector pixels; in single view it is their sum. A bound on absolute DP values also bounds the detector mean, though a narrower independently chosen VI range can reveal smaller variations. Use linear display when comparing residual magnitudes directly. This describes the live comparison; do not use an integer count export to store signed errors.
With a running kernel these recompute on the GPU backend (CUDA or MPS). In an
offline page (offline=True, or the HTML and h5_urls/qem_urls exports),
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, drawn with sharp pixels |
VI Smooth switch |
|
Off by default: sharp scan pixels; on: bilinear interpolation of the virtual image. The diffraction pattern is always drawn with sharp pixels |
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 |
Calibration |
|
Scan scale bars read in a length; the diffraction scale bar reads in mrad or in 1/angstrom, whichever the data record |