9194ef5abd
Docs/Test Workflow / Test docs build (push) Failing after 0s
Check links & references / links-check (push) Failing after 1s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.10) (push) Failing after 0s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.11) (push) Failing after 0s
PR Conflict Labeler / main (push) Failing after 2s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.12) (push) Failing after 2s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.13) (push) Failing after 0s
Pytest/Test Workflow / Build this Package (push) Failing after 5s
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.10) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.11) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.12) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.13) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.10) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.11) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.12) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.13) (push) Has been cancelled
Pytest/Test Workflow / testing-guardian (push) Has been cancelled
151 lines
5.7 KiB
Markdown
151 lines
5.7 KiB
Markdown
---
|
|
comments: true
|
|
description: Count objects entering a polygon zone in images and video using supervision's PolygonZone — measure throughput and density in any region.
|
|
authors:
|
|
- name: Piotr Skalski
|
|
role: Computer Vision Engineer, Roboflow
|
|
github: https://github.com/SkalskiP
|
|
date_modified: 2026-04-22
|
|
---
|
|
|
|
With supervision, you can count the number of objects in a zone in an image or video. In this guide, we will show how to count the number of cars in a traffic video.
|
|
|
|
[View the notebook that accompanies this tutorial](https://github.com/roboflow/notebooks/blob/main/notebooks/how-to-use-polygonzone-annotate-and-supervision.ipynb).
|
|
|
|
To make it easier for you to follow our tutorial download the video we will use as an example. You can do this using the `supervision.assets` module:
|
|
|
|
```python
|
|
from supervision.assets import download_assets, VideoAssets
|
|
|
|
download_assets(VideoAssets.VEHICLES_2)
|
|
```
|
|
|
|
## Initialize a Model and Load Video
|
|
|
|
First, we need to initialize a model. Let's use a YOLOv8 model with the default COCO checkpoint. We also need to load a video on which to run inference.
|
|
|
|
Create a YOLO model instance and download the source video. The model will process each frame during inference. A shared color palette ensures consistent zone coloring throughout the output video.
|
|
|
|
```python
|
|
import numpy as np
|
|
import supervision as sv
|
|
import cv2
|
|
|
|
from ultralytics import YOLO
|
|
from supervision.assets import VideoAssets, download_assets
|
|
|
|
model = YOLO("yolov8s.pt")
|
|
|
|
VIDEO = download_assets(VideoAssets.VEHICLES_2)
|
|
|
|
colors = sv.ColorPalette.DEFAULT
|
|
```
|
|
|
|
## Calculate Coordinates
|
|
|
|
To count objects in a zone, you need to know the coordinates where you want to draw the zone.
|
|
|
|
You can calculate coordinates using the [PolygonZone web utility](https://roboflow.github.io/polygonzone/).
|
|
|
|
To use the PolygonZone website, you will need to upload an image or frame from a video. You can retrieve a frame using this code:
|
|
|
|
```python
|
|
generator = sv.get_video_frames_generator(VIDEO)
|
|
iterator = iter(generator)
|
|
|
|
frame = next(iterator)
|
|
|
|
cv2.imwrite("first_frame.png", frame)
|
|
```
|
|
|
|
PolygonZone will give you NumPy arrays that you can use with supervision to count objects in zones.
|
|
|
|
<video width="100%" loop muted autoplay>
|
|
<source src="https://media.roboflow.com/polygonzone.mp4" type="video/mp4">
|
|
</video>
|
|
|
|
Save the coordinates in an array:
|
|
|
|
```python
|
|
polygons = [
|
|
np.array([[718, 595], [927, 592], [851, 1062], [42, 1059]]),
|
|
np.array([[987, 595], [1199, 595], [1893, 1056], [1015, 1062]]),
|
|
]
|
|
```
|
|
|
|
## Define Zones
|
|
|
|
With the coordinates of the zones to draw ready, we can set up our zones:
|
|
|
|
Instantiate a `PolygonZone` for each polygon array, pairing it with a `PolygonZoneAnnotator` for visual overlay and a `BoxAnnotator` for drawing detection boxes. Each zone will later trigger on incoming detections to determine which objects fall inside its boundaries, enabling per-zone counting in the inference callback.
|
|
|
|
```python
|
|
zones = [sv.PolygonZone(polygon=polygon) for polygon in polygons]
|
|
zone_annotators = [
|
|
sv.PolygonZoneAnnotator(
|
|
zone=zone,
|
|
color=colors.by_idx(index),
|
|
thickness=4,
|
|
text_thickness=8,
|
|
text_scale=4,
|
|
)
|
|
for index, zone in enumerate(zones)
|
|
]
|
|
box_annotators = [
|
|
sv.BoxAnnotator(
|
|
color=colors.by_idx(index),
|
|
thickness=4,
|
|
)
|
|
for index in range(len(polygons))
|
|
]
|
|
```
|
|
|
|
## Run Inference
|
|
|
|
We can run inference on a video using the [sv.process_video](https://supervision.roboflow.com/utils/video/#process_video) function. This function accepts a callback that runs inference on each frame and compiles the results into a video.
|
|
|
|
Below, we can call our YOLOv8 model, annotate predictions and zones, then save the results to a file called `result.mp4`.
|
|
|
|
```python
|
|
def process_frame(frame: np.ndarray, i) -> np.ndarray:
|
|
results = model(frame, imgsz=1280, verbose=False)[0]
|
|
detections = sv.Detections.from_ultralytics(results)
|
|
|
|
for zone, zone_annotator, box_annotator in zip(
|
|
zones, zone_annotators, box_annotators
|
|
):
|
|
mask = zone.trigger(detections=detections)
|
|
detections_filtered = detections[mask]
|
|
frame = box_annotator.annotate(scene=frame, detections=detections_filtered)
|
|
frame = zone_annotator.annotate(scene=frame)
|
|
|
|
return frame
|
|
|
|
|
|
sv.process_video(source_path=VIDEO, target_path="result.mp4", callback=process_frame)
|
|
```
|
|
|
|
Here is an example of inference run on the video:
|
|
|
|
<video width="100%" loop muted autoplay>
|
|
<source src="https://blog.roboflow.com/content/media/2023/03/trim-counting.mp4" type="video/mp4">
|
|
</video>
|
|
|
|
## Frequently Asked Questions
|
|
|
|
### How do I count objects in a zone with supervision?
|
|
|
|
Create `sv.PolygonZone` with a polygon defining your region. Call `zone.trigger(detections)` on each frame — it returns a mask of detections inside the zone.
|
|
|
|
### Can I count objects crossing a line instead of entering a zone?
|
|
|
|
Yes. Use `sv.LineZone` — define a start and end point. `zone.trigger(detections)` returns a tuple of two boolean arrays, `(crossed_in, crossed_out)`, indicating which detections crossed the line in each direction. `LineZone` requires `detections.tracker_id`; run a tracker first so the same object can be matched across frames.
|
|
|
|
### Can I combine zone counting with tracking?
|
|
|
|
Yes. You can pass tracker IDs from `sv.ByteTrack` alongside your detections, but `sv.PolygonZone` still evaluates the zone on each frame and reports which objects are currently inside it. If you want to count each object only once when it first enters the zone, maintain a set of seen `tracker_id` values after filtering detections with `zone.trigger(detections)`, or use a dedicated entry/crossing counting tool such as `sv.LineZone` when it better matches your use case.
|
|
|
|
## Author
|
|
|
|
- [Piotr Skalski](https://github.com/SkalskiP) — Computer Vision Engineer, Roboflow
|