Getting started¶
Install¶
git clone https://github.com/JaneliaSciComp/chunkmirage
cd chunkmirage
uv sync --extra all --group dev # or: pip install -e ".[all]"; add --extra gpu (or cpu) for register://
Requires Python 3.11+. Core dependencies are tensorstore, numpy, numcodecs, starlette,
uvicorn, hypercorn, pydantic and typer. Optional extras: hdf5 (h5py), tiff (tifffile
and imagecodecs, for GeoTIFFs), ops (scipy filters), mesh (scikit-image and DracoPy,
for --mesh), viewer (python-neuroglancer, for --python-viewer), https
(cryptography, for --https), mcp (planned MCP server), cupy (CuPy for CUDA 12, to
resample on a GPU with serve --device), and for register:// either gpu (PyTorch with
CUDA, about 3 GB) or cpu (its CPU-only build, for machines without an NVIDIA GPU). Neither is in all, and they cannot be installed together.
Serve something¶
chunkmirage serve /path/to/data.zarr/em/fibsem-uint8 --op threshold:low=120 --port 8000
The unprocessed source is served alongside as raw and shows as a second layer, at no
extra cost (--no-raw to skip it).
SOURCE can be a zarr v2/v3 array or multiscale group (s0, s1, ...), an N5 dataset or
group, a Neuroglancer precomputed volume, or file.h5::/dataset. Local paths and
s3://, gs://, http(s):// URLs all work. The command prints:
source [processed]: zarr3://http://10.123.4.56:8000/processed/@<digest>/zarr3
source [raw]: zarr3://http://10.123.4.56:8000/raw/@<digest>/zarr3
neuroglancer: https://neuroglancer-demo.appspot.com/#!...
control UI: http://10.123.4.56:8000/ui
control API: http://10.123.4.56:8000/api/datasets/processed
(--name changes processed.)
URLs use the machine's network address so they work from other machines too, unless the
machine cannot reach itself there (some VPNs refuse it): then they use localhost, and a
line says so. The Neuroglancer link opens showing all of the data. Open the
Neuroglancer link in Chrome or Firefox.
See FAQ: does this work with the hosted Neuroglancer?
The demo¶
No data needed: generate a virtual 4096³ volume (69 gigavoxels, nothing on disk) and run a small segmentation pipeline on it:
chunkmirage serve "synthetic://blobs+noise?shape=4096,4096,4096" \
--op gaussian:sigma=1.5 --op threshold:low=110 \
--op morphology:operation=open,radius=2 --op label:min_size=200 \
--python-viewer
Open the printed viewer link: raw shows the generated volume, processed the labelled
objects. Pan and zoom first: each chunk is computed as it comes on screen, the level you
are at first, and zooming out runs the same ops on the coarser levels (a preview of the
fine result, not its exact downsample: labels differ between levels). Then, on the control
page, drag low and watch objects appear and merge; change radius to remove specks.
The smaller disk-based demo is:
uv run python examples/demo.py --port 8000
Generates a synthetic multiscale volume of blobs, then serves it twice: raw as zarr v3
and thresh (gaussian then threshold) as a precomputed segmentation overlay. Both share
one cache, so the raw chunks are read once.
Change the pipeline interactively¶
Open http://localhost:8000/ui for sliders generated from each op's parameters. For live
updates that keep your camera position, start the server with --python-viewer and open
the printed viewer URL. Details and trade-offs: Interactivity.
Change the pipeline live from the shell¶
curl -X PUT localhost:8000/api/datasets/thresh -H 'content-type: application/json' \
-d '{"source": "examples/demo-data.zarr/blobs",
"ops": [{"op": "gaussian", "sigma": 1}, {"op": "threshold", "low": 160}]}'
The response contains new source URLs with an updated digest. Viewers cache chunks by URL, so paste the new URL into the layer's source field and it refetches. Only the changed stage recomputes; see Caching.
Add a step of your own¶
For what no op does, write it: a file defining apply(block, ...), with # lines for
the context it needs, what it returns and its sliders, and the pipeline runs it on every
chunk (the python op):
chunkmirage serve data.zarr --op gaussian:sigma=1 --op python:code=@mine.py --allow-code --python-viewer
--allow-code lets the control page and /api send code too, so it generates a token for
them; the printed control-page link carries it. The browser demos have the same code box,
with a chat that writes the code from a request if you give it your key for a model.
An op you will reuse becomes a class in a package of your own
(your own op).
Use it as a library¶
from chunkmirage import Pipeline, open_source, create_app
from chunkmirage_contrib.ops import Threshold
import uvicorn
src = open_source("s3://bucket/data.zarr/em") # multiscale group or single array
pipe = Pipeline(src, [Threshold(low=120)])
app = create_app({"em-thresh": pipe}) # Starlette ASGI app
uvicorn.run(app, port=8000)
Read the result from Python instead of a viewer¶
Anything that reads zarr over HTTP can consume a served pipeline as a lazy array:
import tensorstore as ts
arr = ts.open({"driver": "zarr3",
"kvstore": "http://localhost:8000/em-thresh/zarr3/s0/"}).result()
arr[0:64, 0:64, 0:64].read().result()
To keep a result instead, write it to disk as the server serves it, a zarr of its own that any tool reads (see formats):
chunkmirage materialize /path/to/data.zarr/em/fibsem-uint8 em-thresh.zarr --op threshold:low=120