Show2D#
One or many 2D images, including gallery panels with independent local slice stacks, plus contrast control, FFT, ROIs, line profiles, and a calibrated scale bar. See the Show2D tutorial for worked examples and Advanced Show2D for report-ready galleries, rich labels, local annotations, overlays, inset plots, and export patterns.
Reference#
- class quantem.widget.show2d.Show2D(*args: Any, **kwargs: Any)#
Bases:
WatchedImageFolderMixin,StaticFallbackMixin,AnyWidget2D image comparison viewer with optional local panel stacks and analysis.
Display a single image or multiple images in a gallery layout. A 3-D item inside a list is an independent local stack for that panel, with its own in-panel slider and playback. A bare 3-D array remains a static gallery; use Show3D when every panel should share one global frame axis.
- Parameters:
data (array_like) – 2D array (height, width) for single image, or 3D array (N, height, width) for multiple images displayed as gallery. A Python list may mix 2D images with 3D
(frames, height, width)arrays. Each grayscale 3D list item is one gallery panel with an independent slider, playback state, frame count, and current frame; local frame counts do not need to match between panels. RGB images are first-class: inside a LIST input, any item shaped(H, W, 3)/(H, W, 4)is treated as an RGB(A) color image (uint8 or float in [0, 1]; alpha is dropped). A bare 3-D ARRAY keeps the historical(N, H, W)stack semantics unless its trailing dim is 3/4 AND its leading dim is > 4, in which case it is a single RGB image. Mixed galleries (grayscale + RGB side by side) are supported: RGB panels are display-ready and bypass the colormap, contrast, auto/log pipeline; their stats row, histogram, FFT, and line profile read the Rec. 709 luminance; the hover readout shows the (r, g, b) triplet.offline=True,export_htmlandrotateare not supported when RGB panels are present.labels (list of str, optional) – Labels for each image in gallery mode.
panel_overlays (mapping or sequence, optional) – Reproducible per-panel geometric overlays. Each overlay is a mapping with
shapeequal to"circle","rect"/"rectangle", or"square". Circle geometry usescenter=(row, col)plusradius; rectangles usebox=(row0, col0, row1, col1),xyxy=(col0, row0, col1, row1), orxywh=(col, row, width, height); squares may usecenterplussize. A dictionary keyed by panel index or label targets specific panels. Coordinates are data pixels by default; passcoords="relative"for normalized 0-1 panel coordinates. Style keys includestroke,stroke_width,line_style,dash,stroke_opacity,fill,fill_opacity,opacity, andz_order.overlays (mapping or sequence, optional) – Convenience alias for shared geometric overlays. A single overlay or a flat list without
panel=is broadcast to every panel. Use eitheroverlaysorpanel_overlays, not both.title (str, optional) – Title to display above the image(s).
cmap (str or sequence of str, default "inferno") – Colormap name (“magma”, “viridis”, “gray”, “inferno”, “plasma”). A sequence assigns one colormap per panel while preserving the first entry as the fallback/global colormap for older saved states.
sampling (float or tuple of float, optional) – Pixel size per axis
(row, col). Scalar broadcasts to both axes. Used for scale bar display. Defaults to(1, 1).units (str or list of str, optional) – Unit string per axis. Scalar broadcasts to both. Common:
"A","nm","pixels". Defaults to["pixels", "pixels"].show_fft (bool, default False) – Show FFT and histogram panels. Every interactive FFT panel includes its current browser-side zoom multiplier (for example
2.0×or5.0×), including independently zoomed gallery FFTs.fft_metrics (bool, default True) – Show compact FFT quality labels inside FFT panels when FFT is visible. The frontend computes these labels from the cached FFT magnitude and does not trigger another FFT.
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 behind a small GUI toggle. Unlike
show_controls=False, users can expand the controls in the frontend and Python can callexpand_controls()later.show_stats (bool, default True) – Show statistics (mean, min, max, std).
debug (bool, default False) – Show a compact frontend FPS/debug badge in the widget title row.
log_scale (bool, default False) – Use log scale for intensity mapping.
auto_contrast (bool, default False) – Use percentile-based contrast.
vmin (float, optional) – Absolute minimum intensity for color mapping. When both vmin and vmax are set, all gallery images share the same intensity scale: essential for A/B visual comparison.
vmax (float, optional) – Absolute maximum intensity for color mapping.
diff_mode (bool, default False) – Append a live signed-difference panel to a grayscale gallery (mirrors
overlay): the gallery becomes[a, b, a - b]for a pair, using a symmetric diverging colormap centered on zero so positive and negative residuals read with equal weight. The reference frame defaults to panel 0 (change it with thediff_referencetrait); the diff recomputes whenever either frame or the reference changes, so it tracks scrubbing stacks live. With more than two frames one diff panel is appended per non-reference grayscale frame (#ref - #other). RGB panels never get a diff panel. Combine withoverlay=Truefor[a, b, overlay]plus the diff panel.overlay (bool or str, default False) – Compose an alignment overlay panel from a 2-image grayscale gallery (mirrors
diff_mode): the gallery becomes[a, b, overlay]with image 0 -> magenta, image 1 -> green, aligned -> white (the colorblind-safe default; same math as quantem drift’soverlay_pair: one shared 1/99-percentile scale across both images).overlay="rgb"gives classic red/green (aligned -> yellow). Requires exactly 2 grayscale images. Combine withdiff_mode=Truefor[a, b, overlay]plus the dynamic signed diff panel of the pair.underlay (bool or str, default False) – Compose chemistry-on-structure from exactly two grayscale inputs
[haadf, map]: the gallery becomes[haadf, map, map on HAADF], blending the element map (magenta) onto the HAADF (gray structure) as a third RGB panel so sparse EDS signal reads against the lattice without washing bright columns to white. PassTrueor"haadf". Requires exactly two single-frame grayscale images (no per-panel frame stacks) and is mutually exclusive withoverlay. The two sources are never modified; only the composed panel is added. Seeunderlay_alphaandunderlay_haadf_gainto tune the blend.underlay_alpha (float, default 0.95) – Opacity of the element map over the HAADF in the
underlayblend, in[0, 1]. Higher means more chemistry, less structure; the slider re-blends live without touching the sources.underlay_haadf_gain (float, default 0.35) – Brightness of the HAADF structure showing through the
underlayblend, in[0, 1]. Lower keeps the map colors saturated over a dim lattice; higher lets more of the gray structure through.underlay_mode ({"haadf", "dual"}, default "haadf") – Composite recipe for the third
underlaypanel."haadf"blends a single element map (magenta) onto the HAADF lattice."dual"takes two grayscale maps[map A, map B](no HAADF) and composes a colorblind-safe magenta+green panel: map A -> magenta, map B -> green, co-located signal -> white. Both modes need exactly two single-frame grayscale inputs.stretch_percentiles (sequence of float, default (4.0, 99.0)) – Low/high display-stretch percentiles applied to the element map(s) before colorizing, matching the drift-paper Fig4 sweep. The slider re-stretches live without touching the stored counts; must satisfy
0 <= low < high <= 100.display_gamma (float, default 0.75) – Presence gamma inside the
"haadf"blend, > 0. Below 1 lifts mid-count columns into color; above 1 keeps only the brightest lit. Ignored in"dual"mode (the magenta+green composite has no ghost).dual_gain (sequence of float, default (1.0, 1.0)) – Per-channel brightness
[gain A, gain B]for the"dual"composite, each >= 0. Raise one channel to balance a weak element against a strong one; ignored in"haadf"mode.ncols (int, default 3) – Number of columns in gallery mode.
panel_frame_indices (sequence of int, optional) – Initial frame for every panel when
datais a list containing local 3-D stacks. Each value is resolved against that panel’s own frame count. Static 2-D panels accept only0. Negative indices follow Python indexing, so-1starts a stack on its final frame.panel_playback_fps (float, default 10) – Playback speed shared by the independent local-stack play buttons. Configure it at construction without adding another toolbar control. Values above 30 are capped at the browser playback budget.
size (int, default 0) – Canvas rendering size in CSS pixels (the on-screen width of each image).
0uses the frontend default: 500 px for a single image, 300 px per image in gallery mode. Pass e.g.size=800to enlarge for a presentation, orsize=200to compress alongside a control panel. This controls display only: the underlying image resolution is never resampled; zooming into a 4K image preserves every pixel.save_state (bool, default False) – When False, saved notebooks omit heavy image 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."jpeg"is the most portable notebook default,"webp"is smaller for local/report workflows,"png"is lossless but larger, andNonedisables the preview.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. Lower values make notebooks smaller; higher values make the static fallback sharper.
denoise (str or sequence of str, default "none") – Display-only denoise method for sparse maps (EDS, low dose). Three orthogonal choices:
"none","gaussian", or"anscombe"(Poisson count-respecting smoothing); binning is the separatedenoise_binknob. Recommendation ladder: sparse EDS ->"anscombe"withdenoise_bin=2and sigma 6-10; very sparse ->"anscombe"withdenoise_bin=4and sigma 8-12; decent-dose HAADF ->"gaussian"sigma 1-2 or"none"; anything quantitative ->"none". The compound spellings"bin2","bin2_anscombe"and"bin4_anscombe"stay accepted as aliases that fold into (mode, bin);"tv"remains available from Python (not in the UI menu). A scalar applies to every panel; a sequence (one entry per panel) gives each panel its own method, e.g.["none", "anscombe"]for a raw vs denoised A/B gallery. Pure view transform: the stored array, the stats row, and every export of raw data keep the original counts, and the lossless default is"none". When active, a one-line banner announces the reduction and how to get raw counts back. RGB panels are never filtered. Independent ofdisplay_bin(the GPU display budget knob). The per-paneldenoise_modes/denoise_sigmas/denoise_binslists are the source of truth; the scalardenoise/denoise_sigma/denoise_bintraits are the UI editor and, whiledenoise_scope == "panel", mirror only the selected panel. Set per-panel values imperatively withset_denoise().denoise_sigma (float or sequence of float, default 4.0) – Smoothing scale in pixels for the Gaussian/Anscombe display filters. A sequence sets one sigma per panel.
denoise_bin ({1, 2, 4} or sequence, default 1) – Display-side 2x bin passes for SNR, combined with
denoise.1(the default) is lossless. A sequence sets one bin factor per panel. This is the SNR knob for sparse maps (EDS, low dose): it trades resolution for counts. It is orthogonal todisplay_bin, which is a performance-only downsample to fit the GPU display budget and does not change reported intensities. Reach fordenoise_binto see faint chemistry,display_binonly to render a huge gallery faster.show_denoise (bool, default False) – Shows the denoise controls row; does not itself denoise - use
denoise=for that. Hidden by default to keep the widget clean; auto-enabled when any panel starts with an active denoise. An active reduction always shows its banner, even with the row hidden.denoise_scope ({"all", "panel"}, default "all") –
UI knob scope: “all” applies Denoise/σ/Bin edits to every panel, “panel” edits only the selected panel. Passing any per-panel sequence switches to the “panel” scope automatically. In gallery mode the toggle lives in the Link group (Link Zoom / Pan / Contrast / Denoise): checked means linked (“all”), unchecked means per panel.
Deprecated since version The:
display_filter-era kwargsdisplay_filter,display_sigma,spatial_binandfilter_per_panelare still accepted for one release and map ontodenoise,denoise_sigma,denoise_binanddenoise_scoperespectively. Passing any of them emits aDeprecationWarning; if both a new kwarg and its deprecated alias are given, the new kwarg wins. In particularspatial_binmaps ontodenoise_bin(the SNR knob), notdisplay_bin(the performance downsample).pad_ratio (float, default 0.0) – Ratio-based border added on each side of the displayed frame, as a fraction of the image’s max(rows, cols). Valid range 0 to 1. The border value is the frame minimum, which keeps the colormap floor. Display-only and reversible (single-panel widgets only): combine with
crop_to_view()and undo withreset_view_ops(). When active, a one-lineview:banner announces the reduction.
- render_total_ms#
End-to-end wall clock from constructor start to first browser paint, populated by a JS→Python round-trip after the first canvas render.
Noneuntil the browser has actually painted; also printed to stdout when it fires. Use to triage “is it Python, wire, or the browser?” during live acquisitions.- Type:
int or None
- render_python_build_ms#
Subset of
render_total_mscovering Python__init__only.- Type:
int or None
- render_wire_js_ms#
Subset covering everything after Python returns: Comm transfer, JS decode, colormap, and canvas paint.
- Type:
int or None
Examples
>>> import numpy as np >>> from quantem.widget import Show2D
Single 2D NumPy array:
>>> Show2D(np.random.rand(512, 512))
PyTorch tensor (CPU or GPU, any dtype):
>>> import torch >>> Show2D(torch.rand(512, 512))
3D NumPy stack
(N, H, W)rendered as a gallery:>>> Show2D(np.random.rand(6, 256, 256), ncols=3)
List of arrays with different shapes (center-padded to a common canvas):
>>> Show2D([np.random.rand(256, 256), np.random.rand(300, 400)])
One static map beside an independently scrubbed HAADF stack:
>>> haadf = np.random.rand(26, 256, 256) >>> Show2D([np.random.rand(256, 256), haadf], ... labels=["EDS map", "HAADF"], panel_frame_indices=[0, -1])
Four tomography reconstructions with independent slice counts and sliders:
>>> reconstructions = [ ... np.random.rand(n_slices, 64, 64) ... for n_slices in (8, 12, 16, 20) ... ] >>> labels = ["baseline", "regularized", "fine z", "alternative"] >>> w = Show2D(reconstructions, labels=labels, ... panel_frame_indices=[3, 5, 7, -1], ... panel_playback_fps=4, ncols=2, show_fft=True) >>> _ = w.set_panel_frame("fine z", 8)
quantem
Dataset2d: title, sampling, units auto-extracted:>>> from quantem.core.datastructures import Dataset2d >>> ds = Dataset2d.from_array(np.random.rand(512, 512)) >>> Show2D(ds)
quantem
Dataset3d: gallery view of N frames with calibration:>>> from quantem.core.datastructures import Dataset3d >>> ds = Dataset3d.from_array(np.random.rand(6, 256, 256)) >>> Show2D(ds, ncols=3)
A/B comparison with shared contrast and linked zoom/pan:
>>> a, b = np.random.rand(512, 512), np.random.rand(512, 512) >>> Show2D([a, b], vmin=0, vmax=1, link_zoom=True, link_pan=True)
Per-image absolute contrast (one
vmin/vmaxper image):>>> Show2D([a, b], vmin=[0.0, 0.2], vmax=[1.0, 0.8])
Drift comparison: diff mode adds a
A - Bpanel alongside the originals (gallery becomes[A, B, A - B]):>>> Show2D([a, b], diff_mode=True, link_zoom=True, link_pan=True)
Alignment overlay: append a composed green-magenta RGB panel (gallery becomes
[a, b, overlay]; aligned regions read white):>>> Show2D([a, b], overlay=True)
RGB images are first-class gallery items (e.g. drift’s green-magenta
overlay_paircomposite next to the grayscale merge):>>> rgb_overlay = np.stack([a, b, a], axis=-1) # (H, W, 3) in [0, 1] >>> Show2D([a, rgb_overlay], labels=["merged", "overlay (RGB)"])
Large image: display-only canvas size (full resolution preserved):
>>> Show2D(np.random.rand(4096, 4096), size=800)
Per-panel display width for galleries. Use
ncolsto choose an intentional gallery shape, for examplencols=2for a 2×2 comparison of four images orncols=4for a single row:>>> Show2D(np.random.rand(13, 128, 128), ncols=13, panel_width_px=70)
Static export to PDF or PNG (vector PDF for publication figures):
>>> w = Show2D(np.random.rand(512, 512), sampling=0.5, units="nm") >>> w.save_image("figure.pdf", dpi=150)
Denoise a sparse EDS map for display (raw counts stay untouched):
>>> eds_map = np.random.poisson(0.3, (256, 256)).astype(np.float32) >>> Show2D(eds_map, denoise="anscombe", denoise_bin=2, denoise_sigma=8)
Raw vs denoised A/B gallery from one call (per-panel
denoiselist):>>> Show2D([eds_map, eds_map], denoise=["none", "anscombe"], denoise_sigma=8)
Chemistry on structure: blend an element map onto HAADF (magenta on gray):
>>> haadf = np.random.rand(256, 256).astype(np.float32) >>> Show2D([haadf, eds_map], underlay=True)
Zoom into a feature, commit that window as the display, then undo it:
>>> w = Show2D(eds_map, view_box=(64, 64, 96)) # zoom to a 96x96 region >>> w.crop_to_view() # the window becomes the whole displayed frame >>> w.reset_view_ops() # back to the full frame, bit-identical
- classmethod from_gif(path: str | Path, *, ncols: int | None = None, labels: list[str | None] | bool | None = None, title: str | None = None, **kwargs) Self#
Open a GIF as a static frame gallery.
Animated GIFs become an
(N, H, W)stack displayed as a contact sheet. Usencolsto choose an intentional layout, for examplencols=2for a 2x2 denoising comparison orncols=1for a vertical strip.
- classmethod from_folder(path: str | Path, *, pattern: str = '*', recursive: bool = False, watch: bool = True, watch_interval: float = 1.0, page_size: int | None = 20, **kwargs) Self#
Display readable folder images as a paged full-resolution gallery.
Files are ordered naturally (
image_2beforeimage_10) and read throughquantem.widget.io.read_image(), including EMD, TIFF, PNG, NPY, and DM calibration paths. The same widget is updated when stable files are added. Unreadable files remain retryable. At mostpage_sizepanels are visible at once; passNoneto keep one unpaged gallery.- Parameters:
path (str or pathlib.Path) – Folder containing independent 2D image files.
pattern (str, default "*") – Glob selecting files within
path.recursive (bool, default False) – Search matching files below subdirectories as well.
watch (bool, default True) – Start background polling immediately.
watch_interval (float, default 1.0) – Seconds between background polls.
page_size (int or None, default 20) – Maximum visible panels per page. Paging appears only after the ready image count exceeds this value. Pass
Noneto disable automatic folder paging.**kwargs – Normal
Show2Doptions. File-derived labels and data are managed by the folder source.
- property folder_page_size: int | None#
Maximum visible panels per page for this folder-backed gallery.
- set_folder_page_size(page_size: int | None) Self#
Change automatic folder pagination without rebuilding the widget.
- Parameters:
page_size (int or None) – Maximum visible panels per page, or
Nonefor one unpaged gallery.- Returns:
This widget, for method chaining.
- Return type:
- set_image(data, labels: list[str | None] | None = None, *, panel_frame_indices: Sequence[int] | None = None) None#
Replace the displayed image stack without rebuilding the widget.
This is the light-weight live-update path used by ShowFolder watched selections. It preserves display controls such as colormap, contrast, FFT/profile toggles, and gallery layout, while resetting per-panel state tied to the previous image count or dimensions.
- save_image(path: str | Path, *, idx: int | None = None, format: str | None = None, dpi: int = 150, title: bool | str = False, colorbar: bool = False, scalebar: bool = False) Path#
Save current image as PNG, PDF, or TIFF.
When
title,colorbar, orscalebarare enabled, the output is a publication-quality figure rendered via matplotlib. Otherwise a raw colormapped image is saved directly (faster, exact pixel output).- Parameters:
path (str or pathlib.Path) – Output file path.
idx (int, optional) – Image index in gallery mode. Defaults to current selected_idx.
format (str, optional) – ‘png’, ‘pdf’, or ‘tiff’. If omitted, inferred from file extension.
dpi (int, default 150) – Output DPI.
title (bool or str, default False) –
Trueuses the widget title, a string sets a custom title.colorbar (bool, default False) – Include a colorbar showing the intensity mapping.
scalebar (bool, default False) – Include a scale bar (requires
pixel_size > 0).
- Returns:
The written file path.
- Return type:
pathlib.Path
- export_svg(path: str | Path | None = None, *, scale: float = 3, include_scale_bar: bool = True, include_colorbar: bool = False, title: str | None = None) Path#
Export the current Show2D gallery as a hybrid SVG figure.
The SVG keeps figure chrome editable as vector elements: panel frames, marker bars, panel labels, title, and scale-bar text/line. The scientific image panels are embedded as PNG images at
scaletimes the widget display size, which preserves the measured pixels while giving Illustrator or Inkscape sharp panels to place in a manuscript.- Parameters:
path (str or pathlib.Path, optional) – Output SVG path. Defaults to a descriptive filename in the current working directory.
scale (float, default 3) – Embedded image scale relative to the widget display panel size. Values below 1 are clamped to 1; use 3 for the default high-resolution export, or a smaller value when file size matters.
include_scale_bar (bool, default True) – Include the current scale bar when scale bars are visible on the widget. Set False to omit scale-bar chrome from the SVG.
include_colorbar (bool, default False) – Include an editable SVG colorbar for single-panel exports. The live browser export uses the current Color switch state instead.
title (str, optional) – Figure title override. Defaults to the widget title.
- Returns:
The written SVG path.
- Return type:
pathlib.Path
- property view_corner#
Current view as
(row0, col0, size)- paste straight intoview_box=.view_boxlive-syncs from JS on every pan/zoom, so after interacting this reads the region on screen. Restore next session withShow2D(..., view_box=w.view_corner).
- property view_center#
Current view as
dict(zoom=..., zoom_row=..., zoom_col=...)- paste as kwargs.The center/zoom flavor of
view_corner: restore withShow2D(..., **w.view_center).
- 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 image buffers from that snapshot, so a plain Show2D does not bake ~1 GB of pixels into the .ipynb. Targeted syncs (keyis a name or set, used by hold_sync / send_state during live rendering) are untouched, so the frontend still receivesframe_bytesnormally.save_state=Trueembeds everything so a reopened notebook restores the interactive widget without a kernel.
- save_view_state(name: str | None = None, *, update: bool = False) dict[str, Any]#
Save the current lightweight Show2D inspection state.
This is the programmatic version of the More → Save State button. It stores widget/view settings such as ROI, zoom/view box, padding, denoise/filter, FFT, selected panel, hidden panels, contrast, and frame indices. It never stores raw image arrays.
- load_view_state(name_or_index: str | int) Self#
Restore a named saved inspection state on the current data.
- delete_view_state(name_or_index: str | int) Self#
Delete one saved inspection state.
- clear_view_states() Self#
Delete all saved inspection states.
- export_html(path: str | Path | None = None, *, title: str | None = None, mode: str = 'single', encoding: str = 'full', downsample: int | None = None, quantized: bool | None = None) Path#
Write a standalone HTML viewer for this widget.
The exported file mounts the live anywidget JS bundle with the current widget state (data, labels, cmap, vmin/vmax, log_scale, sampling, …). Opens in any browser without a Jupyter kernel. Preferred export options are
mode="single",encoding="full"orencoding="uint8", anddownsample=None.quantizedis kept as a compatibility alias forencoding="uint8".- Parameters:
path (str or pathlib.Path, optional) – Destination HTML path.
quantized (bool, default False) – Store the displayed image stack as uint8 with min/max metadata. This is smaller and visually equivalent after colormapping. The default stores exact float32 display values.
title (str, optional) – Browser page title. Defaults to widget
titleor “Show2D”.
- property current_view: dict#
The field of view currently on screen, in image and calibrated coordinates.
Every pan/zoom in the browser syncs the visible region back to Python (debounced ~100 ms), so after zooming into a feature you can capture exactly where you are for figure-making:
view = w.current_view. Feedview["box"]back asShow2D(data, view_box=view["box"])to reproduce the same crop later. The box is axis-aligned, so the two corners (row0, col0) and (row1, col1) define all four.Returns a dict with
row0/col0/row1/col1(image pixel coordinates),height/width(visible extent in pixels),zoom,box(the (row0, row1, col0, col1) tuple accepted byview_box=), and whenpixel_size > 0the calibrated*_calextents plusunit.
- crop_to_view() Self#
Commit the current browser viewport as the display extent.
Zoom into a feature (mouse wheel, or
view_box=at construction), then call this to make that window the whole displayed frame: the widget repacks only the committed region, so an active denoise operates on the cropped view. Display-only and reversible: the stored array is never modified, the stats row keeps reporting the full raw data, and cursor coordinates stay full-image (row, col).reset_view_ops()restores the full frame bit-identically.- Returns:
The widget, for chaining.
- Return type:
Self
- Raises:
NotImplementedError – For galleries (crop-to-view is single panel only in this release) and for RGB panels.
Examples
>>> w = Show2D(image, view_box=(64, 64, 96)) # zoom into a feature >>> w.crop_to_view() # the 96x96 window becomes the display extent >>> w.reset_view_ops() # back to the full frame, bit-identical
- reset_view_ops() Self#
Restore the uncropped, unpadded display.
Undoes
crop_to_view()andpad_ratioin one call. Both ops are display-only view transforms, so resetting returns the exact original frame bytes; the stored data was never touched.- Returns:
The widget, for chaining.
- Return type:
Self
Examples
>>> w = Show2D(image, pad_ratio=0.1) >>> w.reset_view_ops() # full frame again, no border
- set_padding(ratio: float, *, fill: str = 'min', panels: Sequence[int] | int | str | None = None) Self#
Set display padding for every panel, or a chosen panel subset.
Padding is a reversible display transform: the stored arrays are never modified, while frame bytes, histograms, saved state, and exports use the padded display frame.
ratiois a fraction of the current display canvas size (0 to 1).fillchooses the constant border value from each panel:"min","median", or"mean".
- set_denoise(mode: str, *, sigma: float | None = None, bin: int | None = None, panels: int | Sequence[int] | None = None) Self#
Set the display denoise for every panel, or a chosen panel subset.
The imperative twin of the
denoise=constructor kwarg, chainable likecrop_to_view()andset_roi(). Denoise is a pure view transform: the stored array, the stats row, and every raw-data export keep the original counts, and an active reduction still announces itself with the one-line banner. The per-paneldenoise_modes/denoise_sigmas/denoise_binslists are the source of truth; the scalardenoise/denoise_sigma/denoise_bintraits mirror only the selected panel whiledenoise_scope == "panel".- Parameters:
mode (str) – Denoise method for the targeted panels:
"none","gaussian", or"anscombe". The compound spellings"bin2","bin2_anscombe"and"bin4_anscombe"fold into a (mode, bin) pair;"tv"stays available from Python.sigma (float or None, optional) – Smoothing scale in pixels.
Noneleaves each targeted panel’s current sigma unchanged.bin (int or None, optional) – Display-side SNR bin factor (1, 2, or 4).
Noneleaves each targeted panel’s current bin unchanged, unless a compoundmode(e.g."bin2_anscombe") sets it.panels (int, sequence of int, or None, optional) – Panel indices to change.
None(the default) targets every panel and applies one uniform setting; passing a subset edits only those panels and switchesdenoise_scopeto"panel"so later UI edits stay per panel.
- Returns:
The widget, for chaining.
- Return type:
Self
- Raises:
ValueError – When a
panelsindex is out of range for the widget.
Examples
>>> import numpy as np >>> from quantem.widget import Show2D >>> a = np.random.poisson(0.3, (256, 256)).astype("float32") >>> b = np.random.poisson(0.3, (256, 256)).astype("float32") >>> w = Show2D([a, b]) # a raw A/B gallery >>> _ = w.set_denoise("anscombe", sigma=8, bin=2) # denoise both panels >>> _ = w.set_denoise("none", panels=[0]) # keep panel 0 raw
- to_show3d(panels: Sequence[int | str] | int | str | None = None, *, title: str | None = None, copy: bool = True, include_hidden: bool = False)#
Create a
Show3Dstack from this Show2D image gallery.The default uses currently visible panels as frames, preserving labels, colormap, sampling, scale-bar units, and contrast settings. Use
include_hidden=Truewhen hidden panels should also become frames.
- property ordered_panels: list[int]#
Zero-based image panel indices in the current display order.
- property visible_panels: list[int]#
Zero-based image panel indices currently visible in the gallery.
- property starred_panels: list[int]#
Zero-based image panel indices marked with a star.
- property starred_pages: list[int]#
Zero-based page indices marked with a star.
Replace the hidden panel set by index or exact label.
Hidden panels remain in the widget state and standalone HTML export, but they are collapsed from the gallery until restored. At least one panel must stay visible.
- hide_panel(*panels: int | str) Self#
Hide one or more image panels by zero-based index or exact label.
- show_panel(*panels: int | str) Self#
Restore one or more hidden image panels by zero-based index or exact label.
- show_all_panels() Self#
Restore every image panel in the gallery.
- set_panel_order(panels: Sequence[int | str]) Self#
Set the gallery display order by panel index or exact label.
The order is display-only: source data, labels, hidden state, stars, and pixel sizes stay keyed by their original panel indices.
- reset_panel_order() Self#
Restore the natural source-image order.
- move_panel(panel: int | str, position: int) Self#
Move one panel to a zero-based display position.
- set_panel_frame(panel: int | str, frame: int) Self#
Set the displayed frame for one local stack panel.
Static panels have one frame and therefore only accept frame
0. Negative indices follow normal Python indexing, so-1selects the final frame (useful for Velox/EDS acquisitions whose exported HAADF is the last survey frame).
- collapse_controls() Self#
Collapse the live control UI while leaving the GUI toggle available.
- expand_controls() Self#
Expand the live control UI.
- toggle_controls() Self#
Toggle the collapsed state of the live control UI.
- set_starred_panels(panels: Sequence[int | str] | int | str) Self#
Replace the set of starred image panels by index or exact label.
- star_panel(panel: int | str) Self#
Mark an image panel with a star.
- unstar_panel(panel: int | str) Self#
Clear the star on an image panel.
- star_page(page: int) Self#
Mark a page with a star.
- unstar_page(page: int) Self#
Clear the star on a page.
- summary()#
Print a human-readable snapshot of the widget’s current state.
Reports image dimensions and pixel size, data min/max/mean, display settings (colormap, contrast, scale, FFT), active ROIs and profile line, per-image rotations, and the most recent render timings.
- rotate(idx: int, angle: int) Self#
Rotate image
idxbyangledegrees (CCW-positive, matches np.rot90).Rotation convention follows
np.rot90:angle | image_rotations | np.rot90 k | direction ------+-----------------+------------+---------- 90 | 1 | 1 | 90° CCW 180 | 2 | 2 | 180° -90 | 3 | 3 | 90° CW 360 | 0 | 0 | identity
- Parameters:
idx (int) – Image index in the gallery (0-based).
angle (int) – Rotation angle in degrees (must be a multiple of 90). Positive = counter-clockwise, negative = clockwise.
- Return type:
Self
- set_profile(start: tuple, end: tuple)#
Set a line profile between two points (image pixel coordinates).
- Parameters:
start (tuple of (row, col)) – Start point in pixel coordinates.
end (tuple of (row, col)) – End point in pixel coordinates.
- clear_profile()#
Clear the current line profile.
- get_roi_geometries(*, visible_only: bool = True) list[dict[str, Any]]#
Return normalized ROI geometry in image
(row, col)coordinates.The raw
roi_listtrait remains synced for widget state, while this helper gives notebooks, reports, and agents a stable public shape for downstream measurements. Bounds are reported in the image coordinate system;bounds_clippedis clamped to the current image extent for code that wants to slice arrays safely.- Parameters:
visible_only (bool, default=True) – If
True, omit ROIs whose synced state hasvisible=False.- Returns:
JSON-friendly ROI descriptions. Circle ROIs include
centerandradius. Rectangles and squares includecornersin clockwise order from the top-left. Annular ROIs includeradius_innerandradius_outer.- Return type:
list of dict
- roi_geometries(*, visible_only: bool = True) list[dict[str, Any]]#
Return normalized ROI geometry in image
(row, col)coordinates.The raw
roi_listtrait remains synced for widget state, while this helper gives notebooks, reports, and agents a stable public shape for downstream measurements. Bounds are reported in the image coordinate system;bounds_clippedis clamped to the current image extent for code that wants to slice arrays safely.- Parameters:
visible_only (bool, default=True) – If
True, omit ROIs whose synced state hasvisible=False.- Returns:
JSON-friendly ROI descriptions. Circle ROIs include
centerandradius. Rectangles and squares includecornersin clockwise order from the top-left. Annular ROIs includeradius_innerandradius_outer.- Return type:
list of dict
- property profile#
Get profile line endpoints as [(row0, col0), (row1, col1)] or [].
- Returns:
Line endpoints in pixel coordinates, or empty list if no profile.
- Return type:
list of tuple
- property profile_values#
Get intensity values along the profile line as a numpy array.
- Returns:
Float32 array of sampled intensities, or None if no profile.
- Return type:
np.ndarray or None
- property profile_distance#
Get total distance of the profile line in calibrated units.
- Returns:
Distance in angstroms (if pixel_size > 0) or pixels. None if no profile line is set.
- Return type:
float or None
Interactive controls#
Each control mutates the listed synced trait. A UI-test agent acts on the control, then asserts the trait changed and the canvas repainted (non-zero, no console error, no NaN frame).
Control |
Trait |
Expected effect |
|---|---|---|
Colormap dropdown |
|
Canvas recolors to the chosen map |
More menu: Color shared |
|
Shared by default. Turn off to let the Color dropdown edit only the selected gallery panel; turn on for one shared colormap, then turn off again to restore the previous individual panel colormaps |
Panel identity markers |
|
Optional panel colors plus row/column group frames make panels easy to reference in notebooks, reports, and agent instructions |
Local panel annotations |
|
Optional multiple in-image labels per panel, placed by corner, normalized point, or normalized region box |
Geometric panel overlays |
|
Optional circle, rectangle, and square overlays in data or relative coordinates, with stroke/fill opacity, dashed/dotted line styles, and z-order; More -> Overlay Edit allows live select, move, resize, delete, and reset |
Contrast min / max sliders |
|
Display clamp changes; histogram markers move |
Auto-contrast toggle |
|
Re-fits |
More menu: Contrast |
|
Applies scientist-friendly percentile ranges such as |
Log-scale toggle |
|
Intensity mapped through log |
FFT toggle |
|
Canvas shows the power spectrum; lattice spots appear |
FFT window toggle |
|
Apodization on/off (ringing at edges differs) |
FFT quality labels |
|
Compact in-panel label reports FFT sharpness, peak count, and peak SNR from the cached FFT magnitude |
FFT zoom / pan |
Browser-local per FFT panel; FFT |
Wheel or pinch zoom updates an always-visible |
Viewer chrome preset |
|
Applies shared display presets; see Viewer UI controls |
Control visibility |
|
Permanently remove controls or temporarily collapse them behind the top GUI toggle |
Title visibility |
|
Top title row shows/hides |
Stats visibility |
|
Mean/min/max/std readout shows/hides |
Panel title visibility |
|
Per-panel labels show/hide, resize, and optionally get title chrome such as background, border, and padding |
Rich panel title spans |
|
Optional structured |
Scale bar toggle |
|
Calibrated bar shows/hides (needs |
Scale bar style |
|
Restrict scale bars to selected panels and control exact publication text, font, outline, label spacing, offset, and bar thickness |
Gallery gap and borders |
|
Separately controls the layer between panels, the outside gallery frame, and each panel’s own inner stroke for browser, SVG, and static previews |
Pan (drag) |
per-image pan |
Image translates; with |
Zoom (wheel) |
|
Zooms about the cursor |
Smooth toggle |
|
Bilinear vs nearest sampling |
ROI add / drag |
|
Region overlay; stats panel reports the ROI; saved notebook previews include all ROI overlays and one comparable right-side zoom crop per visible ROI; Python can read circle centers/radii and rectangle/square corners in |
Gallery select |
|
Highlights the active panel |
Local stack slider / play |
|
Every grayscale 3D list item gets independent slider/play controls; changing one panel does not move another, and constructor-configured playback speed adds no toolbar clutter |
Gallery page controls |
|
Switch, star, or play through panel pages without changing the source stack |
Panel reorder |
|
Reorders gallery display without changing source data, labels, stars, or hidden state |
Diff mode |
|
Panels render as difference vs the reference |
Link Denoise switch (gallery) |
|
Linked (“all”): denoise edits apply to every panel; unlinked (“panel”): edits apply to the selected panel only |
Denoise master |
|
Off shows raw pixels and hides the banner without discarding method, sigma, bin, or per-panel settings |
Denoise Settings |
|
Expands/collapses the Method, sigma, and bin editor without changing whether the effect is active |
Filter master |
|
Turns frequency filtering on/off without discarding cutoff or band settings |
Filter Settings |
|
Chooses None, Low-pass, High-pass, or Band-pass and exposes the FFT-ring parameters |
More menu: Save State |
|
Save named lightweight inspection bookmarks, then load/update/delete them without storing another copy of the image data |
View menu: Crop to view |
|
Commits the current viewport as the display extent (single panel, display-only, reversible) |
View menu: Padding |
|
Adds a ratio-based display border with min/median/mean fill; gallery edits can apply to all panels or the selected panel |
View menu: Reset view |
|
Restores the uncropped, unpadded display bit-identically |
More menu: Flip |
|
Display-only horizontal/vertical flips help compare orientations and point-defect neighborhoods without changing stored arrays |
More menu: Rotate |
|
Display-only 0/90/180/270° rotation for every panel or the selected panel; raw data and |
Panel inset plots |
|
Optional per-panel mini line plots for calibration curves, ACF/R sweeps, or other scientific context; hover reports the nearest plotted coordinate |
Scale bar placement |
|
Move the scale bar between bottom corners and hide the zoom badge when an inset plot needs that space |
Rich panel labels and math#
Panel labels can be plain strings, inline math strings, or structured rich
spans. Use this for parameter sweeps where the compact panel chrome should say
λ, χ², σ, or similar symbols instead of spelling out the word.
from quantem.widget import Show2D
Show2D(
[raw, residual],
labels=[
r"$\lambda=0.03$ raw",
r"$\chi^2$/pixel residual",
],
show_stats=True,
)
The frontend renders common Greek symbols plus simple superscripts/subscripts
without requiring MathJax or KaTeX. It also normalizes doubled backslashes from
JSON/state files, so \\lambda is rendered as λ rather than as raw markup.
The same rich label path is used by panel titles, the Panels menu, the stats
row, and exported standalone HTML.
For mixed color or explicit math spans, use panel_title_spans. Each span can
contain text, math, and optional color:
Show2D(
[raw, filtered, residual],
labels=["raw", "filtered", "residual"],
panel_title_spans=[
[{"math": r"\lambda=0.03"}, {"text": " raw"}],
[{"text": "filtered", "color": "#34d399"}],
[{"math": r"\chi^2"}, {"text": "/pixel residual"}],
],
)
Use panel_annotations when the label belongs to a local region inside a panel
instead of the whole panel. An annotation can also use math or spans, so a
panel can have both a rich title and multiple local callouts:
Show2D(
[raw, residual],
panel_title_spans=[
[{"math": r"\lambda=0.03"}, {"text": " raw"}],
[{"math": r"\chi^2"}, {"text": "/pixel"}],
],
panel_annotations={
0: [
{"math": r"\lambda", "position": "top-left", "variant": "pill"},
{
"spans": [{"text": "ROI "}, {"text": "A", "color": "#60a5fa"}],
"box": [0.18, 0.25, 0.30, 0.16],
"variant": "callout",
},
],
1: {"math": r"\chi^2", "position": "top-right", "variant": "outline"},
},
)
Circle and rectangle overlays#
Use panel_overlays when the figure needs reproducible geometric callouts
in addition to analysis ROIs. Overlay coordinates use microscope image
coordinates by default: (row, col) for centers and (row0, col0, row1, col1)
for boxes. The same state is rendered in live Jupyter widgets, saved notebook
state, and standalone HTML exports.
from quantem.widget import Show2D
Show2D(
[raw, denoised, residual],
labels=["raw", "denoised", "residual"],
panel_overlays={
"raw": [
{
"shape": "circle",
"center": (96, 88),
"radius": 14,
"stroke": "#60a5fa",
"stroke_width": 3,
"line_style": "dashed",
},
{
"shape": "rect",
"box": (48, 58, 126, 146),
"stroke": "#facc15",
"fill": "#facc15",
"fill_opacity": 0.12,
"dash": [6, 2, 1, 2],
"z_order": 1,
},
],
"denoised": {
"shape": "square",
"center": (96, 88),
"size": 42,
"stroke": "#34d399",
"stroke_width": 2,
"line_style": "dotted",
},
},
)
Overlay strokes are solid by default. Set line_style="dashed",
line_style="dotted", or line_style="dashdot" for common scientific
callouts, or pass dash=[on, off, ...] for an exact canvas dash pattern.
For a shared overlay on every panel, pass overlays=[...] instead of
panel_overlays. Use coords="relative" when the geometry should follow
normalized panel coordinates from 0 to 1:
Show2D(
[raw, denoised],
labels=["raw", "denoised"],
overlays=[
{
"shape": "circle",
"center": (0.5, 0.5),
"radius": 0.08,
"coords": "relative",
"stroke": "#f87171",
}
],
)
The live widget and exported HTML also expose More -> Overlay Edit when
overlays are present. In edit mode, click a circle or rectangle to select it,
drag inside to move it, drag an edge to resize it, press Delete to remove the
selected overlay, and choose Reset Overlays to restore the constructor state.
Use ROIs when the shape should drive statistics, FFT crops, or Python
readback through get_roi_geometries().
Presentation and export chrome#
ui_mode="presentation" starts with controls collapsed so the scientific image
gets the first view. It still leaves a compact Controls button for adjusting
the widget and an Export button for saving the current standalone HTML:
w = Show2D(
[raw, residual],
labels=[r"$\lambda=0.03$ raw", r"$\chi^2$/pixel"],
ui_mode="presentation",
show_stats=True,
)
w.export_html("lambda_sweep.html", encoding="uint8")
In live Jupyter, the Export menu can request exact or uint8 standalone HTML through the Python backend. In standalone HTML, the Export button downloads the current embedded page using the representation already present in that file.
For Illustrator or Inkscape, use the Export menu’s SVG item or call
w.export_svg(...). Show2D writes a hybrid publication SVG: measured image
panels remain embedded rasters, while panel titles, annotations, geometric
overlays, inset plots, scale bars, colorbars, and group markers are editable
SVG objects that match the current figure state.
Publication SVG typography and layout keys#
Use these keys when Show2D should produce a publication figure that can go
directly to Illustrator or Inkscape.
Panel title fonts#
labels set the panel title text. panel_title_font_size sets the size, and
panel_title_style controls typography and placement:
PUBLICATION_FONT = (
"Nimbus Sans, Helvetica, Arial, "
"Liberation Sans, DejaVu Sans, sans-serif"
)
Show2D(
panels,
labels=["a 0° scan", "b 90° scan", "c combined"],
panel_title_font_size=16,
panel_title_style={
"font_family": PUBLICATION_FONT,
"font_weight": 700,
"fg": "#ffffff",
"outline_color": "#000000",
"outline_width": 2.2,
"x": 0.035,
"y": 0.035,
"anchor": "top-left",
"align": "left",
},
)
To match a published PDF, inspect its embedded fonts and put the matching family first:
pdffonts path/to/published_figure.pdf
fc-match "Helvetica"
If the PDF text was outlined or rasterized, no editable text font may be reported; use the source figure script or the closest installed Helvetica-like family instead.
Accepted panel_title_style keys:
Key |
Meaning |
|---|---|
|
CSS/SVG font-family stack, for example |
|
Numeric or named weight, for example |
|
Text fill color |
|
Text stroke used for publication contrast |
|
Relative panel coordinates from 0 to 1 |
|
|
|
Pixel nudge |
|
Text alignment fallback when |
|
Optional title chrome |
Local annotation fonts#
Use panel_annotations for multiple labels inside the same image panel. Each
annotation can have its own font, color, outline, and position:
Show2D(
panels,
labels=["f composite on 0° HAADF"],
panel_annotations={
"f composite on 0° HAADF": [
{
"text": "Ba+Ti",
"x": 0.20,
"y": 0.15,
"anchor": "center",
"align": "center",
"font_family": PUBLICATION_FONT,
"font_size": 15,
"font_weight": 800,
"fg": "#ff4dff",
"outline_color": "#000000",
"outline_width": 1.8,
"variant": "plain",
},
{
"text": "Sr",
"x": 0.80,
"y": 0.15,
"anchor": "center",
"align": "center",
"font_family": PUBLICATION_FONT,
"font_size": 15,
"font_weight": 800,
"fg": "#58ff58",
"outline_color": "#000000",
"outline_width": 1.8,
"variant": "plain",
},
],
},
)
Use x and y to attach a label to a physical region of the panel. Use
box=[left, top, width, height] when the label describes an area. Use
variant="plain" for text-only publication labels, or badge, pill,
outline, and callout for report-style labels.
Scale bar fonts and placement#
scale_bar_panels limits the bar to one or more panels. Values can be panel
indices or label strings. scale_bar_length is in the physical unit implied by
sampling and units; scale_bar_label overrides the displayed text.
Show2D(
panels,
labels=labels,
sampling=0.0105,
units="nm",
scale_bar_panels=["f corrected combined"],
scale_bar_length=2.0,
scale_bar_label="2 nm",
scale_bar_style={
"font_family": PUBLICATION_FONT,
"font_size": 16,
"font_weight": 700,
"color": "#ffffff",
"outline_color": "#000000",
"outline_width": 1.3,
"bar_height": 5,
"label_gap": 5,
"offset": (0, -8),
},
show_zoom_indicator=False,
)
Accepted scale_bar_style keys:
Key |
Meaning |
|---|---|
|
Scale-bar label typography |
|
Bar and label fill color |
|
Label text stroke |
|
Fallback label/bar shadow when no outline is used |
|
Bar thickness in SVG/browser CSS pixels |
|
Pixels between label baseline and bar |
|
Pixel nudge |
Pixel-perfect gutters#
Use explicit gallery chrome controls for manuscript grids:
Show2D(
panels,
labels=labels,
ncols=3,
inter_panel_gap_px=2,
inter_panel_gap_color="#000000",
gallery_outer_border_px=2,
gallery_outer_border_color="#000000",
panel_inner_border_px=1,
panel_inner_border_color="#000000",
)
inter_panel_gap_* controls only the layer between panels.
gallery_outer_border_* controls the frame around the whole grid.
panel_inner_border_* controls the stroke drawn inside each image panel. The
older gallery_gap_px / gallery_gap_color names remain as aliases for old
notebooks; when used together they populate all three black-grid layers for
the historical pixel-perfect SVG behavior.
Which denoise filter should I use?#
The Denoise controls are hidden behind their own toggle by default; everything here is display-only (the stored array, the stats row, and raw exports keep the original counts, and an active denoise always announces itself with a one-line banner). Three methods cover the space; binning is a separate knob, so there is no “bin2_anscombe” menu entry: pick Poisson (Anscombe) and set Bin 2.
Your data |
Use |
Why |
|---|---|---|
Sparse EDS / counting maps |
Poisson (Anscombe), Bin 2, sigma 6-10 |
Respects Poisson count statistics; the standard choice for element maps |
Very sparse maps (single counts) |
Poisson (Anscombe), Bin 4, sigma 8-12 |
More SNR from binning before smoothing |
HAADF / decent-dose images |
Gaussian, Bin 1, sigma 1-2 (or nothing) |
The data is not count-starved; a light smooth is enough |
Anything quantitative (FFT, intensities, stats) |
None |
Measure on raw counts; the stats row is always computed from raw data |
From Python, the same ladder is denoise="anscombe", denoise_bin=2, denoise_sigma=8 (per-panel lists supported for A/B galleries); legacy
spellings like display_filter="bin2_anscombe" keep working as aliases.
Reuse ROI coordinates in notebooks and agents#
ROIs are synced in roi_list and saved in state_dict(). For analysis code,
prefer get_roi_geometries() so the shape-specific coordinates are explicit:
w = Show2D(img).set_roi(row=72, col=65, radius=12)
roi = w.get_roi_geometries()[0]
roi["center"] # {"row": 72.0, "col": 65.0}
roi["radius"] # 12.0
Rectangle and square ROIs include clockwise corners; annular ROIs include
radius_inner and radius_outer. Every ROI also includes bounds and
bounds_clipped, which lets an agent crop an array safely while preserving the
user’s exact visible selection.
Save microscope-stage view states#
Show2D can store named lightweight inspection bookmarks. This is useful when a scientist explores a notebook interactively — for example drawing an ROI around a defect, turning on FFT, hiding panels, changing contrast, adding padding for a drift check, and then wanting to come back to exactly that view later without writing export code.
The GUI path is More → Save State. Existing states can be loaded, updated, deleted one at a time, or deleted all at once. The Python API uses the same state model:
w = Show2D([raw, corrected, residual], labels=["raw", "corrected", "residual"])
w.add_roi(row=72, col=65).set_padding(0.2, fill="median")
w.show_fft = True
w.save_view_state("defect A drift check")
w.hidden_panels = [2]
w.save_view_state("raw vs corrected")
w.load_view_state("defect A drift check")
w.delete_view_state("raw vs corrected")
w.clear_view_states()
Saved view states include UI/view traits such as ROI list, selected ROI,
selected panel, hidden panels, panel order, local stack frame indices, contrast,
colormap, FFT, denoise/filter, crop, padding, zoom/view box, and scale-bar
settings. They do not store another copy of frame_bytes,
panel_stack_bytes, or raw source arrays. state_dict() includes the named
bookmarks so a saved notebook can reopen with the same list of microscope-stage
positions.
Add scientific inset plots to panels#
Advanced figure-making sometimes needs a compact plot inside each image panel:
for example an autocorrelation score versus an R ratio while reviewing
automatic denoising calibration. Use inset_plots for that per-panel context.
The top-level parameter stays singular and readable, while each dictionary keeps
the plot data, placement, labels, and style together.
Show2D(
denoised_panels,
labels=["candidate A", "candidate B", "candidate C"],
inset_plots=[
{
"x": r_values,
"y": acf_values_for_panel,
"point": (best_r, best_acf),
"xlabel": "R",
"ylabel": "ACF",
"legend": "ACF/R",
"annotation": f"R*={best_r:.2f}",
"position": "bottom-right",
"margin": 24,
"size": 0.36,
"height": 0.26,
"show_ticks": True,
"xticks": [0, 1],
"yticks": [0, 1],
"line_width": 2.6,
"background_alpha": 0.58,
"border_width": 0,
}
for acf_values_for_panel, best_r, best_acf in calibration_results
],
scale_bar_position="bottom-left",
show_zoom_indicator=False,
)
For normal notebooks, prefer position plus margin, size, and height.
Use box=[left, top, width, height] only when a publication figure needs exact
normalized panel coordinates. border_width=0 gives a clean no-frame look;
increase background_alpha or use a subtle border_color when the underlying
image is noisy. On hover, the live widget reports the nearest plotted
coordinate using the axis labels, such as R 0.465 · ACF 0.48.
If inset_plots is present, the live widget shows an Inset Chart switch in
the More menu. A scientist can hide the chart during image inspection, turn
it back on for reporting, or drag the chart inside the panel. Dragging previews
freely and then snaps to the closest corner on release, updating position and
margin in the widget state.
Remove a background or isolate a periodicity#
Frequency Filter is deliberately separate from Denoise. Denoise promises a cleaner view of the measurement; High-pass and Band-pass deliberately remove real signal, so their displayed pixels must not be treated as measurable raw counts. The stored array, statistics row, and raw exports remain unchanged.
# Flatten a slow brightness gradient.
Show2D(eds_map, show_fft=True,
frequency_filter="highpass", frequency_filter_cutoff=0.08)
# Isolate a lattice-frequency ring.
Show2D(lattice, show_fft=True,
frequency_filter="bandpass",
frequency_filter_center=0.32,
frequency_filter_width=0.08)
Parameters are fractions of Nyquist from 0 to 1. Drag the cyan FFT ring to select the cutoff or band center by eye. The FFT overlay dims rejected frequencies: Low-pass keeps the clear area inside the ring, High-pass keeps the clear area outside, and Band-pass keeps only the clear annulus. Filter and its More-menu toggle are off by default; turning the master on with no remembered mode starts with Low-pass. Denoise and Filter are chainable in the fixed order Denoise then Filter; each active operation has its own banner. Turn the Filter master off or choose None to restore the unfiltered view without losing the settings.
For paged galleries, FFT work is demand-driven: only panels visible on the current page are transformed. Visiting another page computes that page once and retains the bounded cache for later return visits.
Crop and pad the view (advanced)#
Single-panel widgets can commit the current browser viewport as the display extent. Single-panel and gallery widgets can add a ratio-based display border, either from the toolbar’s View menu or from Python:
w = Show2D(image, view_box=(64, 64, 96)) # zoom into a feature
w.crop_to_view() # the 96x96 window becomes the displayed frame
w.set_padding(0.1, fill="median") # 10% border on each side
w.reset_view_ops() # full frame again, bit-identical
For a drift-correction gallery, use per-panel padding to compare candidate margins without rebuilding another notebook cell:
w = Show2D([raw, corrected, residual], ncols=3)
w.set_padding(0.20, fill="mean", panels=[1]) # only corrected panel
Both ops honor the display-only contract:
The stored array is never modified;
reset_view_ops()returns the exact original frame bytes.The crop applies in the display pipeline before denoise, so an active denoise operates on the cropped region; the pad applies after it and uses the selected fill mode (
min,median, ormean).The stats row keeps reporting the full raw data, and cursor coordinates remain full-image (row, col) while a crop or pad is active: the crop is a display window, not a new coordinate system.
The histogram and canvas are repacked from the padded display frame, so the border contribution is visible while the raw data remain unchanged.
An active crop or pad is never silent: a one-line
view:banner names the window and the ratio, e.g.view: cropped to (64,64)-(160,160) · pad 10% median (reset_view_ops() restores full frame).Both persist through
state_dict()/load_state_dict().
Crop-to-view remains single-panel in this release because it changes the coordinate origin. Padding is gallery-safe: panels share the largest padded canvas so comparisons stay aligned, while each panel can still have its own ratio/fill mode.
FFT quality labels#
Pass show_fft=True to show the FFT panel. By default, fft_metrics=True
adds a small white label inside each FFT panel with three quick checks:
sharpness, peak count, and peak SNR. These values are computed from the FFT
magnitude already used for rendering, so the label does not trigger a second
FFT. Set fft_metrics=False when a clean FFT image is more important than the
readout.
The first FFT for an image, local panel frame, ROI, or window configuration may take a moment on large data. After that, Show2D reuses that bounded cache entry for redraws, zoom/pan, contrast changes, metric labels, and return visits to the same slice. During a first-time slice computation, the previous valid FFT stays visible instead of flashing to a dark loading panel.
Each interactive FFT panel also carries its own bottom-left zoom multiplier.
It starts at 2.0×, follows wheel and touch-pinch zoom immediately, and returns
to 2.0× on double-click, double-tap, or Reset. In a gallery, unlinked FFTs
report independent values; enabling FFT Link Zoom makes their values move
together without changing the real-space zoom.
w = Show2D(images, labels=["raw", "filtered", "residual"])
w.set_panel_order(["residual", "raw", "filtered"])
w.move_panel("raw", 0)
w.reset_panel_order()
See also
The deeper behavioral spec (invariants, per-feature pass criteria, isolation
checks) lives alongside the integration test at widget/docs/show2d-test-spec.md.
Live image updates#
Use set_image() to trigger a new browser render in an already displayed
Show2D widget. Keep a reference to the widget object, display it once, then
replace the image or gallery data through that method:
import numpy as np
from quantem.widget import Show2D
w = Show2D(first_image, labels=["initial"], offline=False)
w
for step, next_image in enumerate(image_stream, start=1):
w.set_image(next_image, labels=[f"step {step}"])
For a gallery, pass a 3D stack (N, H, W) and one label per panel:
w.set_image(
np.stack([raw, filtered, residual]),
labels=["raw", "filtered", "residual"],
)
Independent local stack panels#
A bare 3D array still means a static gallery of N images. To give gallery
panels their own frame or slice axes, pass a Python list whose 3D items are each
shaped (frames, rows, cols). Every grayscale 3D list item becomes one panel
with its own slider, play/pause control, frame count, and current frame. Counts
may differ between panels. RGB(A) list items keep the color-image behavior
described in the constructor reference.
This is useful for tomography or multislice studies that produce several reconstruction volumes. The scientist can keep four methods, regularization settings, or slice thicknesses side by side, stop each one at the depth where a feature or artifact is clearest, and retain linked spatial context without forcing all panels onto one global slice index:
from quantem.widget import Show2D
reconstructions = [
recon_8_slices_20A,
recon_16_slices_10A,
recon_regularized,
recon_alternative,
]
w = Show2D(
reconstructions,
labels=[
"8 slices x 20 A",
"16 slices x 10 A",
"regularized",
"alternative",
],
panel_frame_indices=[3, 7, -1, 0],
panel_playback_fps=4,
ncols=2,
link_zoom=True,
link_pan=True,
)
w.set_panel_frame("16 slices x 10 A", 8)
w
Use a list even when all reconstructions happen to have the same slice count; the outer list is what declares independent local stacks. Use Show3D instead when all panels should advance together on one global frame or slice axis.
The list may also mix stack panels with ordinary 2D images:
w = Show2D(
[eds_sum_map, haadf_stack, ti_map, o_map],
labels=["EDS sum", "HAADF", "Ti", "O"],
panel_frame_indices=[0, -1, 0, 0],
ncols=2,
auto_contrast=True,
)
w
Only the HAADF panel gets an in-panel slider and play button. Each local frame
index remains keyed to its source panel when panels are hidden or reordered.
Click a stack panel and use the left or right arrow key to scrub it. From
Python, use w.set_panel_frame("HAADF", -1).
Set panel_playback_fps when constructing the widget to choose the cadence for
all local-stack play buttons without adding another compact control. The
default is 10 fps; values above the 30 fps browser budget are capped. The
setting is saved in widget state and carried into standalone HTML exports,
while the per-panel playing/paused state remains browser-local so reopening a
report does not start playback unexpectedly.
The active panel’s histogram, stats, and FFT follow its current local frame;
scrubbing it does not change neighboring panels. state_dict() records
panel_frame_indices, and single HTML exports using either encoding="full"
or encoding="uint8" contain every local frame. For an interactively restored
notebook output, construct with save_state=True; the default
save_state=False deliberately stores only a compact static preview rather
than embedding the stack payload in the notebook.
For single-image ROI review, that compact saved-notebook preview keeps the scientific marks visible: the fallback PNG draws every visible ROI on the full image and adds one right-side zoom crop per ROI. The zoom crops use a common crop size where image boundaries allow it, so multiple ROIs remain visually comparable. A reader who opens the notebook or report without rerunning the cell can still see the marked features.
set_image() accepts the same mixed list form:
w.set_image(
[next_eds_map, next_haadf_stack, next_ti_map, next_o_map],
labels=["EDS sum", "HAADF", "Ti", "O"],
panel_frame_indices=[0, 0, 0, 0],
)
Watch a growing image folder#
Use Show2D.from_folder(...) when a microscope or reconstruction job writes
new 2D images into one folder. Each newly readable file becomes another panel
in the existing gallery. The widget object is not rebuilt, so the current
selection and the state of panels already present remain stable.
from quantem.widget import Show2D
w = Show2D.from_folder(
"/data/session/haadf",
pattern="*.tif",
watch_interval=2.0,
page_size=20,
title="Live HAADF images",
)
w
watch=True is the default. Pass watch=False for a fixed folder or a
reproducible script that should update only when you call poll_folder().
Folder galleries default to page_size=20: page controls appear automatically
when the 21st ready image arrives. Use another positive integer to change the
visible limit, or page_size=None to keep one unpaged gallery. The final page
contains only real files, so 45 images form pages of 20, 20, and 5 panels.
Folder pages keep selection, stars, hidden state, and ordering keyed to each
source file rather than sharing a hidden slot across pages.
Change the grouping later without rebuilding the widget:
w.set_folder_page_size(50) # regroup the same source panels
w.set_folder_page_size(None) # show one unpaged gallery
Paging limits the panels, histograms, and FFT views rendered at one time. The
current folder implementation still retains the complete full-resolution
gallery in Python and transports its display previews to the browser; it is not
yet a lazy, bounded-memory source-page cache. Use ShowFolder for lightweight
thumbnail discovery when the full scientific arrays need not all be opened.
An empty watched folder remains mounted with a waiting view and becomes the
real gallery in the same widget model when its first stable file arrives.
The compact title-area badge reports Watching, Updating, Waiting for file completion, Watch error, or Stopped; fixed watch=False snapshots do not
show the badge.
new_panels = w.poll_folder() # scan now; return newly appended indices
w.stop_folder_watch() # pause background scans
w.watch_folder(interval=1.0) # resume with a different interval
w.close() # stop watching and close the widget
Folder watching is append-only. A file already represented in the gallery is not duplicated, an incomplete file is deferred until a later poll, and removing or rewriting a source file does not silently remove or replace an existing panel. An incompatible shape is reported without blocking a later compatible file. Close long-running widgets when the notebook no longer needs them.
Show2D.from_folder(...) reads the scientific image data at its source
resolution. It is different from ShowFolder, which intentionally uses small
cached thumbnails for fast folder discovery and selection. Use ShowFolder
to decide what to open; use Show2D.from_folder(...) when the displayed pixel
data and live panel append behavior matter.
Maintainer real-time signoff follows S2D-18: add genuine EMD files after the widget is visibly mounted and verify the same browser canvas paints each full-resolution panel.
Paged galleries#
Direct Show2D(...) pages are comparison pages. Use them when each view
contains the same panel grid across several
analysis settings, iterations, or parameter values. A common example is a
4-by-4 reconstruction sweep where each page is one iteration or one denoising
parameter, and the panels within the page are the related output images.
These repeated-slot comparison pages are distinct from the sequential item
pages created by Show2D.from_folder(..., page_size=20).
Pass a 4D array with shape (pages, panels_per_page, rows, cols):
from quantem.widget import Show2D
w = Show2D(
reconstruction_sweep,
labels=[
"lambda 0.01", "lambda 0.03", "lambda 0.10", "lambda 0.30",
"lambda 1", "lambda 3", "lambda 10", "lambda 30",
"raw", "filtered", "residual", "score",
"phase", "amplitude", "mask", "diagnostic",
],
page_labels=["iteration 10", "iteration 20", "iteration 30"],
ncols=4,
)
w
You can also pass explicit page dictionaries when the page titles naturally belong beside the image data:
w = Show2D(
[
{"title": "iteration 10", "images": iter10_panels, "labels": panel_labels},
{"title": "iteration 20", "images": iter20_panels, "labels": panel_labels},
],
ncols=4,
)
Paged galleries keep the Python data model simple: all pages use the same panel
count and image shape, the browser renders only the active page, and page stars
are stored separately from panel stars. Use star_page(page) and
unstar_page(page) from Python, or the star button beside the page slider in
the widget. The page row also includes play/pause and a small FPS menu so
readers can step through iteration or parameter pages without touching the main
image controls. Manual slider scrubbing pauses page playback. For very large 4K
sweeps, start with a reduced or representative stack; a future lazy page
transport can avoid sending every page to the browser at once.
set_image() is the re-render trigger. Mutating the original NumPy array in
place does not notify the frontend. The method sends fresh synced frame_bytes
and resets state tied to the old image count or dimensions, including stale
hidden panels, stars, view box, ROIs, line profiles, detail tiles, and panel
order. Use offline=False for acquisition-style updates so the live Jupyter
Comm path carries each new frame instead of the saved/offline notebook path.