Images and surface maps

Show calibrated images, channels and projected mesh data alongside ordinary plots.

2 min readPlotting

Images and scale bars

These patterns assume import numpy as np, import fluxplot as fp, an axes ax, and image arrays (dapi, gfp) from your analysis.

im = fp.image(
    ax, np.stack([dapi, gfp]), series="cells", channels=["dapi", "gfp"],
    luts=["blue", "green"],
    display_range=[(0, 900), None],   # None: the 1st–99.8th percentiles
    pixel_size=0.325, units="µm",
    composite="add",                  # or "max"
)
fp.scalebar(ax, 10, "µm")             # a vector bar exactly 10 data units long
fp.colorbar(im.mappables["gfp"], ax=ax, name="gfp", label="GFP")

fp.image takes a (H, W) image or a (C, H, W) / (H, W, C) stack. It gives every channel a LUT (a colormap name, or a colour, meaning a black-to-colour ramp) and a display range (the black and white points), composites the channels into one RGB imshow in Python, and puts the axes in physical units from pixel_size.

Each channel is a colour scale in the manifest (cells.dapi, cells.gfp; for a single 2D image, just cells) with a linear norm over its display range, and a recipe control of the same name. Flux’s colour-scale editor therefore adjusts a channel’s brightness, contrast or LUT and reruns the script. The scale is marked recolor: "regenerate", or "raster" when you pass value_raster=True so that the channel’s values are saved beside the SVG. The series’ image payload records the channels, LUTs, display ranges, pixel size, units, extent and composite.

fp.scalebar draws a Line2D exactly length data units long, anchored in a corner (loc) and drawn in the theme’s ink. Its label ("<length> <units>") is the part scalebar.<n>.label. The fluxplot repository’s examples/image_example.py draws a complete two-channel field.

Surface maps

# lh_vertices, lh_faces, ... and the per-vertex labels come from your analysis.
fp.surface(ax, labels, series="blocks",
           surfaces={"left": (lh_vertices, lh_faces),
                     "right": (rh_vertices, rh_faces)},
           kind="label", legend=True)

surfaces maps each hemisphere to a (vertices, faces) pair or a GIFTI file path (read with nibabel, an optional dependency). The second argument, values (here labels), holds one value per vertex. kind is "label", "continuous", or "auto" (the default, which chooses label maps for integral, few-valued data); see fp.surface for every option.

A label map draws one collection per category, so every region becomes a separately addressable, separately recolourable part (blocks.<category>; the group blocks.regions lists them all) with a matching legend key. A continuous map draws one field (blocks.field) plus a colorbar. The manifest records the complete value-to-colour contract: cmap, color_range, the category-to-colour table, and which vertices were treated as missing. Views (lateral, medial, …) and hemispheres are laid out side by side inside the one axes.

Vertices with no data are an explicit missing part rather than a value, so an on-mesh zero stays distinguishable from absent data and sentinel codes grey out instead of becoming a spurious category. Rendering is orthographic with back-face culling — a fold cannot paint over the surface in front of it — with optional Lambertian shading for relief; a face straddling a boundary takes the majority label rather than being dropped.

For an orbitable mesh rather than a projected view, use 3D plots.

Search the docs

Search guides, workspaces, and reference topics.

↑ ↓ to move · Enter to open · Esc to close