> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyberwave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Spatial Filter node

> Keep only the detections whose reference point falls inside a polygon zone — defined in normalized image coordinates so the same zone works at any camera resolution.

`spatial_filter` is a stateless workflow node that drops detections
that lie outside an author-defined polygon zone. It pairs with
[`timed_condition`](./timed-condition) downstream to build zone-based
intrusion alerts without per-camera pixel coordinates.

Execution context is inferred from the upstream graph:

* Wired downstream of a `camera_frame` trigger + `call_model` chain it
  is compiled into the edge worker as an inline polygon-membership
  comprehension.
* Wired anywhere else it runs in the cloud workflow runner against the
  detection array of the upstream node.

Typical chain for a zone-scoped intrusion alert:

```
trigger (camera_frame) → call_model (YOLO) → anonymize → spatial_filter → timed_condition → send_alert
```

## Coordinate system

Polygons are stored as `[[x, y], ...]` of floats in
**normalized \[0..1] image coordinates** — the same space YOLO
bounding boxes already use. A zone authored against a 1080p stream
keeps working when you swap the camera for a 4K one without any
manual remap. Polygons must have at least 3 vertices.

## Inputs

| Field        | Type   | Description                                                                                                                                                                                                                                                     |
| ------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `detections` | array  | Detection results from an upstream `call_model` (or `anonymize` / `annotate` — the implicit-wiring contract carries the detection array through unchanged). Required.                                                                                           |
| `polygon`    | array  | `[[x, y], ...]` in normalized \[0..1] coordinates. ≥ 3 vertices.                                                                                                                                                                                                |
| `point_mode` | string | Which part of each detection's bounding box must fall inside the polygon to count as "in". `bbox_bottom_center` (default) for floor-anchored subjects, `centroid` for mid-air things, `any_overlap` if any pixel of the bbox touching the polygon should count. |
| `frame`      | string | `image` (only mode supported in v1; floor-plan zones are deferred).                                                                                                                                                                                             |
| `classes`    | array  | Optional class allow-list applied before the polygon test. `null` / empty means every detection class is tested.                                                                                                                                                |

## Outputs

| Field        | Type   | Description                                                                                                                                                                  |
| ------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `detections` | array  | Subset of input detections that passed the polygon test.                                                                                                                     |
| `count`      | number | Length of the filtered detection array — handy for downstream conditional gates.                                                                                             |
| `polygon`    | array  | Echo of the configured polygon (normalized coordinates) so downstream snapshot rendering and the live frontend overlay can draw the ROI without re-fetching the node config. |

## Authoring zones

Zones are authored in the workflow editor inspector for the
`spatial_filter` node:

1. Add a `Spatial Filter` node from the **Decision & Control** group.
2. Wire a `camera_frame` trigger upstream (typically through a
   `call_model` that runs detection).
3. In the inspector, click on the polygon canvas to add vertices,
   drag to move them, double-click to remove (≥ 3 vertices required).
4. Optional: click **Capture frame** to fetch the latest still from
   the upstream camera trigger and trace the polygon over the actual
   scene.
5. Pick the **Reference Point** that matches your subjects:
   `bbox_bottom_center` for people standing on the floor,
   `centroid` for mid-air objects, `any_overlap` when even a finger
   crossing the line should count.

Once the workflow is **active**, the polygon also renders as a
read-only overlay on the twin's WebRTC stream so operators can see
which zones are armed.

## Edge implementation

The node compiles into a Python comprehension inlined into the
generated edge worker (`wf_<uuid8>.py`) — no extra runtime
dependencies, no MQTT round-trip per frame. The polygon-membership
helper is generated only once even if multiple zones share a worker.
See [Edge Workers](/feature-reference/workflows/workers/overview) for the full lifecycle.

## Companion nodes

* [`timed_condition`](./timed-condition) — fire only when something
  has been *inside* the zone for at least N seconds.
* [`anonymize`](./anonymize-image) — pair upstream of
  `spatial_filter` to publish privacy-safe video while still alerting
  on zone violations.
