Note
Go to the end to download the full example code.
2D Image with Histogram#
Display a 2-D image with physical axes, a colourmap, and an interactive histogram below — all wired together with draggable threshold widgets.
Layout#
A GridSpec with two rows puts the image
on top and a bar-chart histogram below. Two
VLineWidget handles on the histogram mark the
display_min / display_max thresholds; dragging them updates the
image colour scale in real time.
Key bindings on the image panel: R reset view · C toggle colorbar · L / S cycle colour-scale modes.
import numpy as np
import anyplotlib as apl
rng = np.random.default_rng(1)
# ── Synthetic diffraction pattern ─────────────────────────────────────────────
N = 256
x = np.linspace(-5, 5, N) # physical axis in nm
y = np.linspace(-5, 5, N)
XX, YY = np.meshgrid(x, y)
R = np.sqrt(XX ** 2 + YY ** 2)
def _ring(r, r0, width, amp):
return amp * np.exp(-0.5 * ((r - r0) / width) ** 2)
image = (
_ring(R, 0.0, 0.30, 1.00) # central spot
+ _ring(R, 2.1, 0.15, 0.55) # first-order ring
+ _ring(R, 4.2, 0.15, 0.25) # second-order ring
+ rng.normal(scale=0.04, size=(N, N))
)
# ── Layout: image (top, 3×) + histogram bar chart (bottom, 1×) ────────────────
gs = apl.GridSpec(2, 1, height_ratios=[3, 1])
fig = apl.Figure(figsize=(500, 640))
ax_img = fig.add_subplot(gs[0, 0])
ax_hist = fig.add_subplot(gs[1, 0])
# ── Image panel ───────────────────────────────────────────────────────────────
v = ax_img.imshow(image, axes=[x, y], units="nm")
v.set_colormap("inferno")
vmin_init = float(image.min())
vmax_init = float(image.max())
v.set_clim(vmin=vmin_init, vmax=vmax_init)
# First-order spot markers
dx = x[1] - x[0]
def phys_to_px(val):
return (np.asarray(val) - x[0]) / dx
spot_nm = np.array([[ 2.1, 0.0], [-2.1, 0.0],
[ 0.0, 2.1], [ 0.0, -2.1]])
spot_px = np.column_stack([phys_to_px(spot_nm[:, 0]),
phys_to_px(spot_nm[:, 1])])
v.add_circles(spot_px, name="spots", radius=7,
edgecolors="#00e5ff", facecolors="#00e5ff22",
labels=["g1", "g1_bar", "g2", "g2_bar"])
# ── Histogram bar chart ────────────────────────────────────────────────────────
counts, edges = np.histogram(image.ravel(), bins=64)
bin_centers = 0.5 * (edges[:-1] + edges[1:])
h = ax_hist.bar(counts, x_centers=bin_centers, orient="v",
color="#4fc3f7", y_units="count")
# ── Draggable threshold handles on the histogram ──────────────────────────────
wlo = h.add_vline_widget(vmin_init, color="#ff6e40") # low-threshold handle
whi = h.add_vline_widget(vmax_init, color="#ffffff") # high-threshold handle
@wlo.on_release
def _apply_low(event):
"""Update image display_min when the low handle is released."""
v.set_clim(vmin=event.x)
@whi.on_release
def _apply_high(event):
"""Update image display_max when the high handle is released."""
v.set_clim(vmax=event.x)
fig
Adjust colour map#
set_colormap() switches the palette;
set_clim() adjusts the display range.
v.set_colormap("viridis")
v.set_clim(vmin=0.0, vmax=0.8)
fig
Total running time of the script: (0 minutes 0.107 seconds)