OpenFollow Preview

Manual / Tracking

Zone Occupancy Detection

A zone is a polygon on the stage plane. The zone engine watches marker positions (and optionally person-detection results Experimental) and fires OSC messages on each transition of the occupant count – first entry, additional entry, partial exit, and final exit. Zones are how you do cue-style automation – “fire QLab cue 12 when anyone walks downstage centre” – without a continuous tracking stream.

Zone Occupancy Detection is currently experimental. The wire format and field names may change between releases.

Where it lives

  • Markers & Zones → Trigger Zones – global settings.
  • Markers & Zones → Zone Editor – graphical canvas to draw and edit zone polygons.

Global settings

FieldMeaningDefault
EnabledMaster on/off for the zone engine.On
Show OverlayDraw zone outlines on the live overlay.On
Eval Rate (FPS)How often the engine evaluates occupancy. Lower = lower CPU.10
Debounce (ms)Suppresses fast in/out flicker at the boundary. An event fires only after the marker has been on the new side for this long.200
Hysteresis (m)Boundary thickness. The marker must move this far past the boundary before an exit fires.0.05
Default OSC destinationThe named OSC destination used by zones that don’t pick their own.

Destinations – host, port, protocol, framing – are defined once under Output → OSC Output and shared with OSC transmitters. Each zone either inherits the default above or names its own.

Drawing a zone

Screenshot: Zone Editor with a polygon mid-draw and the toolbar visible.

The Zone Editor canvas shows the stage from above with the grid overlay and any current markers as dots. To draw a zone:

  1. Click + New Zone. The cursor switches to crosshair mode.
  2. Click on the canvas to drop each polygon vertex.
  3. Click the first vertex (or double-click the canvas) to close the polygon.
  4. The new zone appears in the list. Click it to select and edit.

To edit an existing zone:

  • Click the zone outline to select it.
  • Drag a vertex to move it.
  • Cancel Drawing aborts a polygon mid-draw.
  • Delete Selected removes the selected zone.

Per-zone configuration

With a zone selected, the details panel reorganises into four tabs (the same pattern as OSC Transmitters transmitters):

TabContents
BasicName, colour, trigger source, and the per-marker filter (Triggered by, see below).
AreaThe polygon vertices – explicit X/Y per vertex if you'd rather type than drag, plus a small live preview.
SettingsThe zone's OSC destination (or the global default) and the four OSC messages (First entry, Additional entry, Partial exit, Final exit), each with its own address and args. Args use the same quote-aware tokeniser as OSC Transmittersaddress arg1 arg2 "quoted arg" round-trips two args, not three.
DiagnosticsLive occupancy state, current occupants, and a small ring buffer of recent fired events with rendered addresses.

The actions row alongside Save also has a Duplicate button – copy a zone and edit the differences instead of redrawing.

Trigger source

  • Markers – any tracked marker entering the polygon fires events.
  • Detection Experimental – person-detection bounding boxes drive events (requires Person Detection enabled).
  • Both Experimental – either source fires.

Triggered by – per-marker filter

The Triggered by field on the Basic tab restricts which markers count for occupancy. It's a comma-separated list of marker IDs; empty means "any marker."

  • Empty – every marker counts (default).
  • 1 – only marker 1 counts. Even if other markers cross the polygon, they don't fire events on this zone.
  • 1, 2, 5 – only markers 1, 2, and 5 count. Useful when the show has many markers but a given zone only cares about a specific performer.

The current filter is rendered as a chip list on the live diagnostics surface so the operator can see at a glance which markers a zone is listening to.

Address & arguments

The OSC fields on the Settings tab use the same token rules as OSC Output transmitters: the first whitespace-separated token is the address, the rest are arguments, quotes group multi-word arguments. Unclosed quotes surface a 400 with a one-line message; empty / whitespace-only fields skip silently. See OSC Transmitters → Address vs arguments for the full rules.

Argument types are inferred by the same wire-boundary rules – integers stay integers, floats stay floats, anything else is a string.

The four event types

Events are transition-only – each one fires once when the occupant count changes across its threshold. Nothing repeats at the eval rate; if you want a continuous stream while a zone is occupied, use a Stream-triggered OSC Transmitters transmitter gated on the marker's position.

EventFires whenTypical use
First entryEmpty zone becomes occupied (0 → 1 occupant).Trigger a cue, raise an audio bus.
Additional entryAlready-occupied zone gains another occupant (n → n+1 where n ≥ 1).Increment a counter, layer an effect.
Partial exitAn occupant leaves but the zone stays occupied (n+1 → n where n ≥ 1).Decrement a counter, peel back a layer.
Final exitThe last occupant leaves (1 → 0).Restore default state, kill an effect.

Leave any of the four addresses blank to skip that event's message.

OSC messages

Each event sends one OSC message to the zone's OSC destination. Address and args use the same quote-aware tokeniser as OSC Transmitters; argument types are inferred from the literal at the wire boundary.

Typical example (fire QLab cue):

destination: QLab
address:     /cue/12/start
args:        (none)

Typical example (notify a media server when someone is downstage centre):

destination: Media Server XYZ
address:     /downstage/occupied
args:        1

Person-detection-driven zones Experimental

Instead of a marker, the zone engine can listen to bounding boxes from the optional Person Detection pipeline (Person Detection tab). This lets you fire zone events without an operator driving a marker – useful for unstaffed setups. Accuracy, model defaults, and the config surface are subject to change; don’t rely on it for show-critical paths.

Person Detection is opt-in: install a detection backend – ONNX Runtime (recommended on a Pi) or Ultralytics – from the web UI's Person Detection page. Detection stays disabled until a backend is installed and Enabled is on. See Person Detection.

Detection is more compute-heavy than marker-driven zones; on a Pi without a hardware accelerator, expect 5–10 fps detection at 320×320. See Hardware.

Hysteresis & debounce, in practice

If your zones flicker (rapid in/out events as a performer skirts the boundary), increase one or both:

  • Hysteresis bakes a “moat” around each boundary – the marker has to cross fully past the moat before an exit fires.
  • Debounce suppresses any state change shorter than the window.

Hysteresis is typically the right knob to reach for; debounce is the safety net.

Saving and reusing zone setups

The Zone Editor’s Save as template… stores the current zone configuration – every polygon plus its settings – as a reusable template, and Load template… replaces the current zones from a saved one (you confirm before it overwrites). Use it to keep per-show or per-venue zone presets and switch quickly between them. In the template chooser, each row Exports to a single .oftemplate file you can copy to another station, and Import… loads one in.