Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5302ecc
Refactor README for better clarity and organization
vikassaini77 Jun 12, 2026
c5ad5a5
Revise README formatting and example paths
vikassaini77 Jun 12, 2026
b498db5
Update README.md
vikassaini77 Jun 12, 2026
c3923be
Update README.md
vikassaini77 Jun 12, 2026
1c2351e
Update README.md
vikassaini77 Jun 12, 2026
f7344db
Update README.md
vikassaini77 Jun 12, 2026
396632e
refactor(detection): replace assert with ValueError in converters.py
vikassaini77 Jun 14, 2026
26068c9
refactor(detection): replace assert with ValueError in iou_and_nms.py
vikassaini77 Jun 14, 2026
bd37b27
refactor(metrics): replace assert with ValueError in mean_average_pre…
vikassaini77 Jun 14, 2026
314e63c
refactor(detection): replace assert with ValueError in vlm.py
vikassaini77 Jun 14, 2026
96a11b6
refactor(detection): replace assert with ValueError in line_zone.py
vikassaini77 Jun 14, 2026
2caf9a1
refactor(key_points): replace assert with TypeError in annotators.py
vikassaini77 Jun 14, 2026
64faba6
refactor(detection): replace assert with TypeError and ValueError in …
vikassaini77 Jun 14, 2026
fad0a18
refactor(utils): replace assert with TypeError and ValueError in imag…
vikassaini77 Jun 14, 2026
757a2f5
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] Jun 14, 2026
099db6f
refactor(annotators): replace assert with ValueError and TypeError in…
vikassaini77 Jun 14, 2026
01cbaca
refactor(tracker): replace asserts with ValueError in single_object_t…
vikassaini77 Jun 15, 2026
f88bb35
docs(image): fix dangling docstring word in crop_image
vikassaini77 Jun 16, 2026
ec04190
docs(image): fix dangling docstring word in scale_image
vikassaini77 Jun 16, 2026
f4b02b1
docs(image): fix dangling docstring word in resize_image
vikassaini77 Jun 16, 2026
cad410b
docs(image): fix dangling docstring word in tint_image
vikassaini77 Jun 16, 2026
c95125b
docs(image): fix dangling docstring word in grayscale_image
vikassaini77 Jun 16, 2026
dd6a6c0
docs(image): fix dangling docstring word in ImageSink
vikassaini77 Jun 16, 2026
687491e
docs(image): fix dangling docstring word in create_tiles
vikassaini77 Jun 16, 2026
2ce4ddf
docs(conversion): fix dangling docstring word in pillow_to_cv2
vikassaini77 Jun 16, 2026
76f262e
Merge branch 'develop' into refactor-asserts-part-2
vikassaini77 Jun 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">
<p>
<a align="center" href="" target="https://supervision.roboflow.com">
<a align="center" href="https://supervision.roboflow.com" target="_blank">
<img
width="100%"
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529"
Expand All @@ -24,11 +24,24 @@

</div>

## 👋 hello
## 📑 Table of Contents

- [👋 Hello](#-hello)
- [💻 Install](#-install)
- [🔥 Quickstart](#-quickstart)
- [Models](#models)
- [Annotators](#annotators)
- [Datasets](#datasets)
- [🎬 Tutorials](#-tutorials)
- [💜 Built with Supervision](#-built-with-supervision)
- [📚 Documentation](#-documentation)
- [🏆 Contribution](#-contribution)

## 👋 Hello

**We are your essential toolkit for computer vision.** From data loading to real-time zone counting, we provide the building blocks so you can focus on building applications around your models. 🤝

## 💻 install
## 💻 Install

Pip install the supervision package in a [**Python>=3.9**](https://www.python.org/) environment.

Expand All @@ -38,9 +51,9 @@ pip install supervision

Read more about conda, mamba, and installing from source in our [guide](https://roboflow.github.io/supervision/).

## 🔥 quickstart
## 🔥 Quickstart

### models
### Models

Supervision was designed to be model agnostic. Just plug in any classification, detection, or segmentation model. For your convenience, we have created [connectors](https://supervision.roboflow.com/latest/detection/core/#detections) for the most popular libraries like Ultralytics, Transformers, MMDetection, or Inference. Other integrations, like `rfdetr`, already return `sv.Detections` directly.

Expand All @@ -51,7 +64,7 @@ import supervision as sv
from PIL import Image
from rfdetr import RFDETRSmall

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

Expand All @@ -71,7 +84,7 @@ len(detections)
from PIL import Image
from inference import get_model

image = Image.open(...)
image = Image.open("path/to/image.jpg")
model = get_model(model_id="rfdetr-small", api_key="ROBOFLOW_API_KEY")
result = model.infer(image)[0]
detections = sv.Detections.from_inference(result)
Expand All @@ -82,15 +95,16 @@ len(detections)

</details>

### annotators
### Annotators

Supervision offers a wide range of highly customizable [annotators](https://supervision.roboflow.com/latest/detection/annotators/), allowing you to compose the perfect visualization for your use case.

```python
import cv2
import supervision as sv

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

box_annotator = sv.BoxAnnotator()
Expand All @@ -99,7 +113,7 @@ annotated_frame = box_annotator.annotate(scene=image.copy(), detections=detectio

https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce

### datasets
### Datasets

Supervision provides a set of [utils](https://supervision.roboflow.com/latest/datasets/core/) that allow you to load, split, merge, and save datasets in one of the supported formats.

Expand All @@ -123,7 +137,7 @@ for path, image, annotation in ds:
pass
```

<details close>
<details>
<summary>👉 more dataset utils</summary>

- load
Expand Down Expand Up @@ -213,7 +227,7 @@ for path, image, annotation in ds:

</details>

## 🎬 tutorials
## 🎬 Tutorials

Want to learn how to use Supervision? Explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/), [end-to-end examples](./examples), [cheatsheet](https://roboflow.github.io/cheatsheet-supervision/), and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/)!

Expand All @@ -233,7 +247,7 @@ Want to learn how to use Supervision? Explore our [how-to guides](https://superv
<div><strong>Created: 11 Jan 2024</strong></div>
<br/>Learn how to track and estimate the speed of vehicles using YOLO, ByteTrack, and Roboflow Inference. This comprehensive tutorial covers object detection, multi-object tracking, filtering detections, perspective transformation, speed estimation, visualization improvements, and more.</p>

## 💜 built with supervision
## 💜 Built with Supervision

Did you build something cool using supervision? [Let us know!](https://github.com/roboflow/supervision/discussions/categories/built-with-supervision)

Expand All @@ -243,11 +257,11 @@ https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-

https://github.com/roboflow/supervision/assets/26109316/3ac6982f-4943-4108-9b7f-51787ef1a69f

## 📚 documentation
## 📚 Documentation

Visit our [documentation](https://roboflow.github.io/supervision) page to learn how supervision can help you build computer vision applications faster and more reliably.

## 🏆 contribution
## 🏆 Contribution

We love your input! Please see our [contributing guide](.github/CONTRIBUTING.md) to get started. Thank you 🙏 to all our contributors!

Expand All @@ -259,8 +273,6 @@ We love your input! Please see our [contributing guide](.github/CONTRIBUTING.md)

<br>

<div align="center">

<div align="center">
<a href="https://youtube.com/roboflow">
<img
Expand Down Expand Up @@ -295,13 +307,12 @@ We love your input! Please see our [contributing guide](.github/CONTRIBUTING.md)
src="https://media.roboflow.com/notebooks/template/icons/purple/forum.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672949633584"
width="3%"
/>
</a>
<img src="https://raw.githubusercontent.com/ultralytics/assets/main/social/logo-transparent.png" width="3%"/>
<a href="https://blog.roboflow.com">
<img
src="https://media.roboflow.com/notebooks/template/icons/purple/blog.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672949633605"
width="3%"
/>
</a>
</a>
</div>
</div>
37 changes: 22 additions & 15 deletions src/supervision/annotators/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1360,11 +1360,12 @@ def _draw_labels(
detections: Detections,
custom_color_lookup: npt.NDArray[np.int_] | None,
) -> None:
assert len(labels) == len(label_properties) == len(detections), (
f"Number of label properties ({len(label_properties)}), "
f"labels ({len(labels)}) and detections ({len(detections)}) "
"do not match."
)
if not (len(labels) == len(label_properties) == len(detections)):
raise ValueError(
f"Number of label properties ({len(label_properties)}), "
f"labels ({len(labels)}) and detections ({len(detections)}) "
"do not match."
)

color_lookup = (
custom_color_lookup
Expand Down Expand Up @@ -1584,7 +1585,8 @@ def annotate(

```
"""
assert isinstance(scene, Image.Image)
if not isinstance(scene, Image.Image):
raise TypeError("scene must be a PIL Image")
_validate_labels(labels, detections)

draw = ImageDraw.Draw(scene)
Expand Down Expand Up @@ -1674,11 +1676,12 @@ def _draw_labels(
detections: Detections,
custom_color_lookup: npt.NDArray[np.int_] | None,
) -> None:
assert len(labels) == len(label_properties) == len(detections), (
f"Number of label properties ({len(label_properties)}), "
f"labels ({len(labels)}) and detections ({len(detections)}) "
"do not match."
)
if not (len(labels) == len(label_properties) == len(detections)):
raise ValueError(
f"Number of label properties ({len(label_properties)}), "
f"labels ({len(labels)}) and detections ({len(detections)}) "
"do not match."
)
color_lookup = (
custom_color_lookup
if custom_color_lookup is not None
Expand Down Expand Up @@ -2656,7 +2659,8 @@ def annotate(
if custom_values is not None:
value = custom_values[detection_idx]
else:
assert detections.confidence is not None # MyPy type hint
if detections.confidence is None:
raise ValueError("detections.confidence cannot be None")
value = detections.confidence[detection_idx]

color = resolve_color(
Expand Down Expand Up @@ -3127,7 +3131,8 @@ def annotate(

@staticmethod
def _use_obb(detections_1: Detections, detections_2: Detections) -> bool:
assert not detections_1.is_empty() or not detections_2.is_empty()
if detections_1.is_empty() and detections_2.is_empty():
raise ValueError("At least one detections object must not be empty.")
is_obb_1 = ORIENTED_BOX_COORDINATES in detections_1.data
is_obb_2 = ORIENTED_BOX_COORDINATES in detections_2.data
return (
Expand All @@ -3138,7 +3143,8 @@ def _use_obb(detections_1: Detections, detections_2: Detections) -> bool:

@staticmethod
def _use_mask(detections_1: Detections, detections_2: Detections) -> bool:
assert not detections_1.is_empty() or not detections_2.is_empty()
if detections_1.is_empty() and detections_2.is_empty():
raise ValueError("At least one detections object must not be empty.")
is_mask_1 = detections_1.mask is not None
is_mask_2 = detections_2.mask is not None
return (
Expand Down Expand Up @@ -3185,7 +3191,8 @@ def _mask_from_mask(
mask = np.zeros(scene.shape[:2], dtype=np.bool_)
if detections.is_empty():
return mask
assert detections.mask is not None
if detections.mask is None:
raise ValueError("detections.mask cannot be None")

for detections_mask in detections.mask:
mask |= detections_mask.astype(np.bool_)
Expand Down
76 changes: 52 additions & 24 deletions src/supervision/detection/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1883,15 +1883,21 @@ def from_vlm(
vlm = _validate_vlm_parameters(vlm, result, kwargs)

if vlm == VLM.PALIGEMMA:
assert isinstance(result, str)
if not isinstance(result, str):
raise TypeError(
f"Expected str result for {vlm}, got {type(result).__name__}"
)
xyxy, class_id, class_name = from_paligemma(result, **kwargs)
data: dict[str, npt.NDArray[np.generic] | list[Any]] = {
CLASS_NAME_DATA_FIELD: class_name,
}
return cls(xyxy=xyxy, class_id=class_id, data=data)

if vlm == VLM.QWEN_2_5_VL:
assert isinstance(result, str)
if not isinstance(result, str):
raise TypeError(
f"Expected str result for {vlm}, got {type(result).__name__}"
)
xyxy, class_id, class_name = from_qwen_2_5_vl(result, **kwargs)
data = {CLASS_NAME_DATA_FIELD: class_name}
confidence_arr: npt.NDArray[np.floating[Any]] = np.ones(
Expand All @@ -1902,7 +1908,10 @@ def from_vlm(
)

if vlm == VLM.QWEN_3_VL:
assert isinstance(result, str)
if not isinstance(result, str):
raise TypeError(
f"Expected str result for {vlm}, got {type(result).__name__}"
)
xyxy, class_id, class_name = from_qwen_3_vl(result, **kwargs)
data = {CLASS_NAME_DATA_FIELD: class_name}
confidence_arr = np.ones(len(xyxy), dtype=float)
Expand All @@ -1911,13 +1920,19 @@ def from_vlm(
)

if vlm == VLM.DEEPSEEK_VL_2:
assert isinstance(result, str)
if not isinstance(result, str):
raise TypeError(
f"Expected str result for {vlm}, got {type(result).__name__}"
)
xyxy, class_id, class_name = from_deepseek_vl_2(result, **kwargs)
data = {CLASS_NAME_DATA_FIELD: class_name}
return cls(xyxy=xyxy, class_id=class_id, data=data)

if vlm == VLM.FLORENCE_2:
assert isinstance(result, dict)
if not isinstance(result, dict):
raise TypeError(
f"Expected dict result for {vlm}, got {type(result).__name__}"
)
xyxy, labels, mask, xyxyxyxy = from_florence_2(result, **kwargs)
if len(xyxy) == 0:
empty = cls.empty()
Expand All @@ -1933,18 +1948,27 @@ def from_vlm(
return cls(xyxy=xyxy, mask=mask, data=data)

if vlm == VLM.GOOGLE_GEMINI_2_0:
assert isinstance(result, str)
if not isinstance(result, str):
raise TypeError(
f"Expected str result for {vlm}, got {type(result).__name__}"
)
xyxy, class_id, class_name = from_google_gemini_2_0(result, **kwargs)
data = {CLASS_NAME_DATA_FIELD: class_name}
return cls(xyxy=xyxy, class_id=class_id, data=data)

if vlm == VLM.MOONDREAM:
assert isinstance(result, dict)
if not isinstance(result, dict):
raise TypeError(
f"Expected dict result for {vlm}, got {type(result).__name__}"
)
xyxy = from_moondream(result, **kwargs)
return cls(xyxy=xyxy)

if vlm == VLM.GOOGLE_GEMINI_2_5:
assert isinstance(result, str)
if not isinstance(result, str):
raise TypeError(
f"Expected str result for {vlm}, got {type(result).__name__}"
)
gemini_result = from_google_gemini_2_5(result, **kwargs)
data = {CLASS_NAME_DATA_FIELD: gemini_result[2]}
return cls(
Expand Down Expand Up @@ -2493,17 +2517,19 @@ def with_nms(
if len(self) == 0:
return self

assert self.confidence is not None, (
"Detections confidence must be given for NMS to be executed."
)
if self.confidence is None:
raise ValueError(
"Non-Maximum Suppression requires `confidence` to be not `None`"
)

if class_agnostic:
predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))
else:
assert self.class_id is not None, (
"Detections class_id must be given for NMS to be executed. If you"
" intended to perform class agnostic NMS set class_agnostic=True."
)
if self.class_id is None:
raise ValueError(
"Non-Maximum Suppression requires `class_id` to be not `None` if "
"`class_agnostic` is False"
)
predictions = np.hstack(
(
self.xyxy,
Expand Down Expand Up @@ -2582,17 +2608,19 @@ def with_nmm(
if len(self) == 0:
return self

assert self.confidence is not None, (
"Detections confidence must be given for NMM to be executed."
)
if self.confidence is None:
raise ValueError(
"Detections confidence must be given for NMM to be executed."
)

if class_agnostic:
predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))
else:
assert self.class_id is not None, (
"Detections class_id must be given for NMM to be executed. If you"
" intended to perform class agnostic NMM set class_agnostic=True."
)
if self.class_id is None:
raise ValueError(
"Detections class_id must be given for NMM to be executed. If you"
" intended to perform class agnostic NMM set class_agnostic=True."
)
predictions = np.hstack(
(
self.xyxy,
Expand Down Expand Up @@ -2815,8 +2843,8 @@ def merge_inner_detection_object_pair(
if detections_1.confidence is None and detections_2.confidence is None:
merged_confidence = None
else:
assert detections_1.confidence is not None
assert detections_2.confidence is not None
if detections_1.confidence is None or detections_2.confidence is None:
raise ValueError("Both detections must have confidence scores.")
detection_1_area = (xyxy_1[2] - xyxy_1[0]) * (xyxy_1[3] - xyxy_1[1])
detections_2_area = (xyxy_2[2] - xyxy_2[0]) * (xyxy_2[3] - xyxy_2[1])
merged_confidence = (
Expand Down
Loading