Open-source project
roboflow/supervision avatar
roboflow/supervision

supervision: from model output to boxes, zones and split datasets

Supervision provides reusable building blocks for computer-vision pipelines, from detections and tracking to annotation and evaluation.

51,079 stars4,865 forksPythonMIT

At a glance

What is it?
Supervision is the post-processing layer of a computer vision pipeline: it normalises model output into `sv.Detections`, draws it, and loads, splits, merges and saves datasets in COCO, YOLO and Pascal VOC. The interesting decisions are which connectors you trust with a key, and what the dataset helpers do not let you control.
Who is it for?
Adopt supervision when you have models that produce detections and you want one drawing and dataset layer across Ultralytics, Transformers, MMDetection, Inference or rfdetr instead of five sets of bespoke code. Do not adopt it expecting a training loop or a model, and think twice before wiring the Roboflow Inference connector or the Roboflow dataset download into a pipeline that must stay offline.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

sv.Detections is the contract, and connectors are how a model returns it

The library was designed to be model agnostic, and the mechanism that makes that work is one data structure. Whatever predicts, the result has to arrive as `sv.Detections` before anything else in the library can be used. Some integrations return that object directly, and the README notes that other integrations such as `rfdetr` already return `sv.Detections`. The rest arrive through connectors, with existing ones for Ultralytics, Transformers, MMDetection and the Roboflow Inference package. A local run looks like this:

python
import supervision as sv
from PIL import Image
from rfdetr import RFDETRSmall

image = Image.open("path/to/image.jpg")
model = RFDETRSmall()
detections = model.predict(image, threshold=0.5)

len(detections)
# 5

The practical value is that the annotators and dataset code below never learn which model produced the boxes. The cost is that the conversion step is yours to write for any model without a connector, and that the threshold you pass here becomes part of your pipeline's behaviour rather than a library setting.

The Inference connector needs a Roboflow API key, the local model does not

Two quickstart paths, two different postures. The `rfdetr` example above runs a model in your own process and needs nothing but `pip install pillow rfdetr`. The Inference path routes through the Roboflow service and is constructed with an explicit key:

python
    model = get_model(model_id="rfdetr-small", api_key="ROBOFLOW_API_KEY")
    result = model.infer(image)[0]
    detections = sv.Detections.from_inference(result)

The README states that running with Inference requires a Roboflow API key, and that is the whole of the configuration surface for that path. Decide early whether your pipeline is allowed to depend on a hosted service, because the difference is not a flag you flip later: it is a credential in your source or your environment, an extra network hop per frame, and a service that can be unavailable when your model would not have been. The dataset examples carry the same requirement in a different form, since they download through a workspace id, a project id and a project version. A pipeline that must run offline can still use the annotators and the local loaders, but not these two.

Annotators draw onto the scene you pass, which is why the example copies it

This line carries more design information than the rest of the quickstart:

python
image = cv2.imread("path/to/image.jpg")
# Assuming detections are obtained from a model
detections = sv.Detections(...)

box_annotator = sv.BoxAnnotator()
annotated_frame = box_annotator.annotate(scene=image.copy(), detections=detections)

`scene=image.copy()` is not defensive noise. The annotator draws onto the array you hand it and returns it, so the frame you pass in is modified. Pass the original and your next processing step, or your next video frame, is already covered in boxes. Copy when the frame is precious, which in a video loop it usually is. The `examples/` directory shows what people compose from these pieces: `count_people_in_zone`, `time_in_zone`, `heatmap_and_track`, `speed_estimation`, `traffic_analysis`, `tracking` and `compact_mask`. Those are applications rather than primitives, and they are the fastest way to judge whether the building blocks fit your pipeline.

split() takes a ratio and nothing else, so class balance is not in the signature

The dataset helpers are where a quiet surprise lives. Loading gives you a `DetectionDataset` from COCO, YOLO or Pascal VOC, and indexing loads the image on demand rather than holding the whole set in memory, which is why iterating a large set is affordable. Splitting looks like this:

python
    train_dataset, test_dataset = dataset.split(split_ratio=0.7)
    test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)

That produces 700, 150 and 150 from a set of 1,000 by splitting twice. The signature takes a ratio and returns two datasets, with no seed, no shuffle flag and no stratification option shown, so the only control you have is where the cut falls. If the loader yields images grouped by source or by class, your validation set inherits that grouping, and the resulting metric looks fine while measuring less than you think. Do the split yourself, keep a record of it, and treat the built-in one as a convenience for getting a pipeline running.

merge() takes the union of class names, so label indices move

Merging is the operation with the most consequence, and the README's own example shows the shape of the result. A dataset of 100 items with classes `['dog', 'person']` and one of 200 items with classes `['cat']` merge into 300 items whose `classes` is `['cat', 'dog', 'person']`, a sorted union of the vocabularies. Every label index in every dataset has to be remapped to that union, and a class that exists in one dataset and not the other becomes a label with no examples behind it in that half. If you trained anything on the pre-merge indices, that model now reads the wrong classes, and the failure is silent because the numbers still look like class ids. Merging is fine for assembling one training set from several sources, provided you read `classes` after the merge and re-derive the mapping rather than assuming the original order survived.

The base install pulls scipy, matplotlib and av before you draw a box

Install is one command in a Python 3.10 or newer environment:

bash
pip install supervision

The dependency list behind that command is worth reading once. It requires `av>=14.2`, `defusedxml>=0.7.1`, `matplotlib>=3.6`, `numpy>=1.21.2`, `pillow>=9.4`, `pydeprecate>=0.9,<0.13`, `pyyaml>=5.3`, `requests>=2.26`, `scipy>=1.10` and `tqdm>=4.62.3`. So a container that only draws rectangles also carries a video library, SciPy and Matplotlib, and `defusedxml` is there because the annotation formats are XML and JSON that arrive from elsewhere. Two constraints will bite in an existing environment. `pydeprecate` is pinned with an upper bound, so another package pinning it differently makes the install fail, and the heavier features are extras: `geotiff` pulls `rasterio>=1.3`, chosen in the project's own comment because 1.3 introduced a stable window-read API and `CRS.is_projected`, and `metrics` pulls `pandas>=2`.

The default branch is develop at 0.31.0.dev0, and the release is 0.30.6

Version management is the last thing to settle. The default branch is `develop`, and the version in `pyproject.toml` there is `0.31.0.dev0`, a development marker for the next minor series. The published releases are 0.30.6 on 2026-09-29, 0.30.5 on 2026-09-22 and 0.30.4 on 2026-09-17, and the last push was on 2026-09-29, so three releases landed inside twelve days and the branch is already on the following minor. `pip install supervision` gives you 0.30.6, not the branch. The documentation links make the same split visible, since the README points at `latest` paths for the detection core and annotators and at `develop` paths for the how-to guides and cookbooks, so you can end up reading instructions for code you are not running. The rest of the layout is conventional and healthy: `src/`, `tests/`, `tox.ini`, `uv.lock`, `.pre-commit-config.yaml`, `mkdocs.yml` with `docs/`, and classifiers claiming Python 3.10 through 3.14 with a typed marker.

Editorial conclusion

Adopt supervision when you have models that produce detections and you want one drawing and dataset layer across Ultralytics, Transformers, MMDetection, Inference or rfdetr instead of five sets of bespoke code. Do not adopt it expecting a training loop or a model, and think twice before wiring the Roboflow Inference connector or the Roboflow dataset download into a pipeline that must stay offline. Before you pin a version, check which documentation you are reading: the README links both `latest` and `develop` paths, the default branch is `develop` at version `0.31.0.dev0`, and the newest published release is 0.30.6, so install the release you intend to run rather than the branch you read about.

Frequently asked questions

how to install supervision

Install the package from PyPI into a Python 3.10 or newer environment with `pip install supervision`. The README also points to a guide covering conda, mamba and installing from source, and the project publishes its releases on PyPI as the `supervision` package.

how to install supervision in python

`pip install supervision` is the documented path, and `pyproject.toml` declares `requires-python = ">=3.10"` with classifiers for 3.10 through 3.14. Import it as `import supervision as sv`, and note that heavier features are optional extras rather than part of the base install.

Does supervision run the model, or only the output of a model?

It works on the output of a model. The library was designed to be model agnostic: connectors exist for Ultralytics, Transformers, MMDetection and the Roboflow Inference package, and integrations such as `rfdetr` return `sv.Detections` directly, which is the structure the annotators and dataset helpers consume.

What formats can supervision load and save datasets in?

`sv.DetectionDataset` loads from COCO, YOLO and Pascal VOC, and saves back out with `as_coco`, `as_yolo` and `as_pascal_voc`, so a conversion between formats is a load followed by a save. Datasets also support `split` with a ratio and `merge` across several datasets, where the merged `classes` list is the union of the inputs.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/roboflow-supervision.svg)](https://hysenlabs.com/projects/roboflow-supervision)