# How to Use PolygonZone for Filtering Detections in Custom Regions

> Learn to use PolygonZone from roboflow/supervision to filter object detections within custom polygonal regions. Define zones and filter detections efficiently.

- Repository: [Roboflow/supervision](https://github.com/roboflow/supervision)
- Tags: how-to-guide
- Published: 2026-04-06

---

**PolygonZone is a lightweight utility in the roboflow/supervision library that defines arbitrary polygonal regions on video frames and filters object detections by testing whether specified anchor points fall inside the defined zone.**

Working with computer vision models often requires isolating objects within specific regions of interest, such as counting vehicles in a parking lot or monitoring activity in a retail aisle. The **PolygonZone** class in the `roboflow/supervision` repository provides an efficient way to define custom polygonal boundaries and filter `Detections` objects based on their spatial relationship to these zones. This utility integrates directly with the core detection pipeline defined in [`supervision/detection/__init__.py`](https://github.com/roboflow/supervision/blob/main/supervision/detection/__init__.py), leveraging anchor point geometry to determine inclusion.

## Creating a Polygon Zone

Define custom regions by instantiating `PolygonZone` with a NumPy array of `(x, y)` vertices. The constructor in [`src/supervision/detection/tools/polygon_zone.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/detection/tools/polygon_zone.py) (lines 61-77) accepts two primary arguments: `polygon` and `triggering_anchors`.

The `polygon` parameter requires a NumPy array of shape `(N, 2)` containing the vertex coordinates. The `triggering_anchors` parameter accepts a tuple of `sv.Position` enum values specifying which points of each bounding box to test against the zone mask. If this iterable is empty, the constructor raises a `ValueError` as verified in [`tests/detection/test_polygonzone.py`](https://github.com/roboflow/supervision/blob/main/tests/detection/test_polygonzone.py). Internally, the class converts the polygon to a binary mask using `polygon_to_mask` from [`src/supervision/detection/utils/converters.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/detection/utils/converters.py) for efficient point-in-polygon testing.

```python
import numpy as np
import supervision as sv

# Define a quadrilateral zone

polygon = np.array([[100, 100], [400, 80], [350, 300], [120, 320]])

# Use default BOTTOM_CENTER anchor (ideal for counting objects on the ground)

zone = sv.PolygonZone(polygon=polygon)

# Or specify multiple anchor points (e.g., top-left and bottom-right corners)

zone_multi = sv.PolygonZone(
    polygon=polygon,
    triggering_anchors=(sv.Position.TOP_LEFT, sv.Position.BOTTOM_RIGHT)
)

```

## Filtering Detections with the trigger Method

Call `zone.trigger(detections)` to obtain a boolean mask indicating which detections fall inside the zone. Implemented in [`polygon_zone.py`](https://github.com/roboflow/supervision/blob/main/polygon_zone.py) (lines 78-113), this method extracts anchor coordinates via the `Detections.get_anchors_coordinates` method, clips them to the mask bounds (`x_safe` and `y_safe`), and checks against the pre-computed binary mask (`self.mask[y_safe, x_safe]`).

The method returns a boolean NumPy array of the same length as the input detections. It also updates the `zone.current_count` attribute with the total number of detections currently inside the zone.

```python

# detections is an sv.Detections object from a model inference

inside_mask = zone.trigger(detections)  # Returns bool[np.ndarray]

filtered_detections = detections[inside_mask]

print(f"Objects in zone: {zone.current_count}")
print(f"Inside mask: {inside_mask}")

```

## Visualizing Zones with PolygonZoneAnnotator

Render polygon boundaries and real-time counts using **PolygonZoneAnnotator**, defined in the same [`polygon_zone.py`](https://github.com/roboflow/supervision/blob/main/polygon_zone.py) file. This class draws filled or outlined polygons using helper functions `draw_filled_polygon` and `draw_polygon` from [`src/supervision/draw/utils.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/draw/utils.py).

Configure the visual appearance with parameters like `color`, `opacity`, and `thickness`. The annotator automatically displays the `current_count` value on the frame.

```python
annotator = sv.PolygonZoneAnnotator(
    zone=zone,
    color=sv.Color.RED,
    opacity=0.2,
    thickness=2
)

# Apply visualization to a frame (NumPy array)

annotated_frame = annotator.annotate(frame)

```

## Complete Working Example

The following example integrates **PolygonZone** with Ultralytics YOLO to filter detections within a defined region of a parking lot or room corner.

```python
import cv2
import numpy as np
import supervision as sv
from ultralytics import YOLO

# Load image and run inference

image = cv2.imread("example.jpg")
model = YOLO("yolo11s")
result = model(image)[0]

# Convert model output to supervision Detections

detections = sv.Detections.from_ultralytics(result)

# Define a custom polygon zone (e.g., a specific parking spot or counter area)

polygon = np.array([[100, 100], [400, 80], [350, 300], [120, 320]])
zone = sv.PolygonZone(
    polygon=polygon,
    triggering_anchors=(sv.Position.BOTTOM_CENTER, sv.Position.BOTTOM_LEFT)
)

# Filter detections inside the zone

inside_mask = zone.trigger(detections)
filtered = detections[inside_mask]

print(f"Total objects detected: {len(detections)}")
print(f"Objects in zone: {zone.current_count}")

# Visualize results

zone_annotator = sv.PolygonZoneAnnotator(zone, color=sv.Color.GREEN, opacity=0.1)
box_annotator = sv.BoundingBoxAnnotator()

frame = zone_annotator.annotate(image.copy())
frame = box_annotator.annotate(scene=frame, detections=filtered)

cv2.imshow("Zone-filtered view", frame)
cv2.waitKey(0)

```

## Summary

- **PolygonZone** in [`src/supervision/detection/tools/polygon_zone.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/detection/tools/polygon_zone.py) defines custom regions using NumPy vertex arrays and converts them to binary masks via `polygon_to_mask` from [`src/supervision/detection/utils/converters.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/detection/utils/converters.py).
- The `trigger()` method returns a boolean mask indicating which detections fall inside the zone based on `triggering_anchors` (defaulting to `sv.Position.BOTTOM_CENTER`).
- The `current_count` property automatically tracks the number of detections inside the zone after each `trigger` call.
- **PolygonZoneAnnotator** renders zones and counts on frames using drawing utilities from [`src/supervision/draw/utils.py`](https://github.com/roboflow/supervision/blob/main/src/supervision/draw/utils.py).
- Providing an empty `triggering_anchors` iterable raises a `ValueError` during initialization.

## Frequently Asked Questions

### What anchor positions does PolygonZone support?

PolygonZone supports any combination of positions from the `sv.Position` enum, including `TOP_LEFT`, `TOP_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_RIGHT`, `CENTER`, and `BOTTOM_CENTER` (the default). You can pass a tuple of multiple positions to `triggering_anchors` to require any of the specified points to fall inside the zone for a detection to count.

### How does PolygonZone handle coordinates outside the frame boundaries?

The `trigger` method automatically clips coordinates to the mask bounds before checking values (`x_safe` and `y_safe` indexing), preventing index errors when detections extend beyond the defined polygon area or frame edges. This clipping ensures robust operation even with partial detections at image boundaries.

### Can I use multiple PolygonZones simultaneously on the same frame?

Yes, you can instantiate multiple `PolygonZone` objects with different polygons and call `trigger` on each independently using the same `Detections` object. Each zone maintains its own `current_count` and binary mask, allowing you to monitor separate regions (such as counting people in different aisles) within a single inference pipeline.

### How do I persist polygon coordinates between sessions?

Store the NumPy array of vertices to a file (JSON, CSV, or NumPy binary format) and reload it before initializing `PolygonZone`. The class expects a NumPy array of shape `(N, 2)` where each row contains an `(x, y)` coordinate pair, making it compatible with coordinates captured from annotation tools or manual UI selection.