useq-schema¶
useq-schema (imported as useq) is an implementation-agnostic schema for
describing a sequence of events during a multi-dimensional imaging acquisition:
the time points, stage positions, channels, z-planes, etc... that you want to
acquire. It describes what to do, and leaves it to an acquisition engine (such
as the one in pymmcore-plus) to
actually do it.
The goal of useq-schema is to provide a specification (and some python utilities) for generating event objects that can be consumed by microscope acquisition engines. A hope is that this will encourage inter-operability between various efforts to drive automated image acquisition.
The schema tries to remain agnostic to the specific acquisition engine (though it was designed based on the capabilities of Micro-Manager). We welcome feedback from interested parties regarding limitations and/or potential extensions to the schema! Similarly, while the "ideal" schema will support arbitrary dimensions (i.e. more than the conventional position, time, channel, z, ...), it is also hard to avoid hard-coding some assumptions about dimensionality in certain places. Any and all feedback (even minor stuff, such as parameter naming, etc...) is welcome! Please open an issue.
A quick example¶
from useq import MDASequence
mda_seq = MDASequence(
stage_positions=[(100, 100, 30), (200, 150, 35)],
channels=["DAPI", "FITC"],
time_plan={"interval": 1, "loops": 20},
z_plan={"range": 4, "step": 0.5},
axis_order="tpcz",
)
The MDASequence object is iterable, yielding MDAEvent objects in the order
specified by the axis_order attribute:
>>> mda_seq.sizes
{'t': 20, 'p': 2, 'c': 2, 'z': 9}
>>> events = list(mda_seq)
>>> len(events)
720
>>> events[1]
MDAEvent(index={'t': 0, 'p': 0, 'c': 0, 'z': 1}, channel=Channel(config='DAPI'), min_start_time=0.0, x_pos=100.0, y_pos=100.0, z_pos=28.5)
Building Sequences walks through the common cases (time series, z-stacks, grids, skipping channels, etc...).
Core schema¶
MDAEvent¶
The primary "event" object is useq.MDAEvent. This describes a single event
that a microscope should perform, including preparation of the hardware, and
execution of the event (such as an image acquisition).
- For Micro-Manager, this
object is most similar (though not identical) to the events generated by
generate-acq-sequencein the clojure acquisition engine that drives Micro-Manager's multi-dimensional acquisitions. - For Pycro-Manager, this
object is similar to an individual acquisition event
dictgenerated bymulti_d_acquisition_events, (and,useq.pycromanagerprovides ato_pycromanagerfunction that returns a dict following the pycro-manager event spec)
MDASequence¶
useq.MDASequence represents a sequence of events, as might be
generated by the multidimensional acquisition GUI in most microscope software.
The Python MDASequence object is itself
iterable, and yields
useq.MDAEvent objects.
- For Micro-Manager, this
object is most similar to
org.micromanager.acquisition.SequenceSettings, (generated by clicking theAcquire!button in the Multi-D Acquisition GUI) - For Pycro-Manager, this
object is similar to the
multi_d_acquisition_eventsconvenience function, (anduseq.pycromanagerprovides ato_pycromanagerfunction that returns a list of pycro-manager event dicts)
Executing an MDASequence¶
This library is just a schema, and does not provide any built-in functionality
for executing an MDASequence. However,
pymmcore-plus implements an
acquisition engine that can execute an MDASequence object (or any iterable of
MDAEvent objects) with Micro-Manager (via the
pymmcore python wrapper around
the C++ MMCore). The MDAWidget in
pymmcore-widgets, pymmcore-gui and
napari-micromanager all build a
useq.MDASequence from user input and hand it to pymmcore-plus to run.
Built an engine that runs an MDASequence (or know of a similar schema in other
software)? Open an issue
and we'll list it here.
Serialization and deserialization¶
MDASequence and MDAEvent objects are designed to be serialized and deserialized,
allowing you to define an entire multi-dimensional acquisition in a
human-readable YAML (or JSON) file, and then load that file into your acquisition
engine.
For example, the following file defines an experiment with:
- 3 channels (
Cy5,FITC, andDAPI), specifying exposure times in ms forCy5andFITC, and acquiringDAPI(without a z-stack) only every third timepoint - a two-phase time-lapse: 3 frames, 3 seconds apart, followed by a frame every 10 seconds for 40 minutes
- a Z-stack at each timepoint, with a range of 4 microns and a step size of 0.5 micron
- two stage positions, specifying a unique Z-stack for the second position
Example
axis_order: tpcz
channels:
- config: Cy5
exposure: 50.0
- config: FITC
exposure: 100.0
- config: DAPI
acquire_every: 3
do_stack: false
time_plan:
phases:
- interval: 0:00:03
loops: 3
- duration: 0:40:00
interval: 0:00:10
z_plan:
range: 4.0
step: 0.5
stage_positions:
- x: 10.0
y: 20.0
z: 50.0
- name: Cell 1
x: 110.0
y: 220.0
z: 50.0
sequence:
z_plan:
above: 10.0
below: 0.0
step: 1.0
metadata:
some info: about my experiment
{
"axis_order": "tpcz",
"channels": [
{
"config": "Cy5",
"exposure": 50.0
},
{
"config": "FITC",
"exposure": 100.0
},
{
"config": "DAPI",
"acquire_every": 3,
"do_stack": false
}
],
"time_plan": {
"phases": [
{
"interval": "0:00:03",
"loops": 3
},
{
"duration": "0:40:00",
"interval": "0:00:10"
}
]
},
"z_plan": {
"range": 4.0,
"step": 0.5
},
"stage_positions": [
{
"x": 10.0,
"y": 20.0,
"z": 50.0
},
{
"name": "Cell 1",
"x": 110.0,
"y": 220.0,
"z": 50.0,
"sequence": {
"z_plan": {
"above": 10.0,
"below": 0.0,
"step": 1.0
}
}
}
],
"metadata": {
"some info": "about my experiment"
}
}
Either file can be loaded with:
... and seq.yaml() or seq.model_dump_json(exclude_unset=True) go the other
way (see saving and loading
sequences).
Unknown keys are ignored
Keys that aren't part of the schema are silently dropped when a file is loaded,
so a misspelled or misplaced key (for example a z_plan placed directly on a
stage position, rather than inside its sequence) won't raise an error. It's
worth checking the loaded sequence (list(seq), or seq.sizes) before
running it.
What about useq.v2?¶
useq.v2 is a preview of the next version of the API (with user-defined axes,
among other things). useq.MDASequence, as described on these pages, is
unchanged, and nothing needs to migrate yet. See useq-schema v2
(preview) if you're curious.
Next steps¶
- Building Sequences: common experiments, step by step.
- The
MDASequence,MDAEvent, axes and autofocus reference pages. - The Acquisition Engine, to run a sequence on a microscope.