diff --git a/README.md b/README.md
index 7a9d6a3248..0f19ee65dd 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
-## 👋 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.
@@ -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.
@@ -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)
@@ -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)
@@ -82,7 +95,7 @@ len(detections)
-### 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.
@@ -90,7 +103,8 @@ Supervision offers a wide range of highly customizable [annotators](https://supe
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()
@@ -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.
@@ -123,7 +137,7 @@ for path, image, annotation in ds:
pass
```
-
+
👉 more dataset utils
- load
@@ -213,7 +227,7 @@ for path, image, annotation in ds:
-## 🎬 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/)!
@@ -233,7 +247,7 @@ Want to learn how to use Supervision? Explore our [how-to guides](https://superv
Created: 11 Jan 2024
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.
-## 💜 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)
@@ -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!
@@ -259,8 +273,6 @@ We love your input! Please see our [contributing guide](.github/CONTRIBUTING.md)
-
diff --git a/src/supervision/annotators/core.py b/src/supervision/annotators/core.py
index 559528cc64..07697766c3 100644
--- a/src/supervision/annotators/core.py
+++ b/src/supervision/annotators/core.py
@@ -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
@@ -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)
@@ -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
@@ -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(
@@ -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 (
@@ -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 (
@@ -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_)
diff --git a/src/supervision/detection/core.py b/src/supervision/detection/core.py
index 4f38f1e594..b2aaf19d8a 100644
--- a/src/supervision/detection/core.py
+++ b/src/supervision/detection/core.py
@@ -1883,7 +1883,10 @@ 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,
@@ -1891,7 +1894,10 @@ def from_vlm(
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(
@@ -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)
@@ -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()
@@ -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(
@@ -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,
@@ -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,
@@ -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 = (
diff --git a/src/supervision/detection/line_zone.py b/src/supervision/detection/line_zone.py
index 3178fca24a..0fe6603ada 100644
--- a/src/supervision/detection/line_zone.py
+++ b/src/supervision/detection/line_zone.py
@@ -281,8 +281,10 @@ def _compute_anchor_sides(
The third array, `has_any_right_trigger`, indicates if the
detection's anchor is on the right side of the line zone.
"""
- assert len(detections) > 0
- assert detections.tracker_id is not None
+ if len(detections) == 0:
+ raise ValueError("Detections cannot be empty.")
+ if detections.tracker_id is None:
+ raise ValueError("Detections must have a tracker_id.")
all_anchors = np.array(
[
@@ -312,7 +314,8 @@ def _update_class_id_to_name(self, detections: Detections) -> None:
Assumes that class_names are only provided when class_ids are.
"""
class_names = detections.data.get(CLASS_NAME_DATA_FIELD)
- assert class_names is None or detections.class_id is not None
+ if class_names is not None and detections.class_id is None:
+ raise ValueError("class_names are provided but class_id is None.")
if detections.class_id is None:
return
@@ -618,7 +621,8 @@ def _draw_oriented_label(
text_box_color=self.color,
line_angle_degrees=line_angle_degrees,
)
- assert label_image.shape[0] == label_image.shape[1]
+ if label_image.shape[0] != label_image.shape[1]:
+ raise ValueError("label_image must be a square image.")
text_width, text_height = cv2.getTextSize(
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
diff --git a/src/supervision/detection/utils/converters.py b/src/supervision/detection/utils/converters.py
index 9626daf6dd..dcaf1ca55a 100644
--- a/src/supervision/detection/utils/converters.py
+++ b/src/supervision/detection/utils/converters.py
@@ -691,8 +691,10 @@ def mask_to_rle(
{ align=center width="800" }
"""
- assert mask.ndim == 2, "Input mask must be 2D"
- assert mask.size != 0, "Input mask cannot be empty"
+ if mask.ndim != 2:
+ raise ValueError("Input mask must be 2D")
+ if mask.size == 0:
+ raise ValueError("Input mask cannot be empty")
counts: list[int] = cast(list[int], _mask_to_rle_counts(mask).tolist())
if compressed:
diff --git a/src/supervision/detection/utils/iou_and_nms.py b/src/supervision/detection/utils/iou_and_nms.py
index 9b367c141a..4d3bc1d604 100644
--- a/src/supervision/detection/utils/iou_and_nms.py
+++ b/src/supervision/detection/utils/iou_and_nms.py
@@ -345,9 +345,11 @@ def box_iou_batch_with_jaccard(
```
"""
- assert len(is_crowd) == len(boxes_true), (
- "`is_crowd` must have the same length as `boxes_true`"
- )
+ if len(is_crowd) != len(boxes_true):
+ raise ValueError(
+ f"Length of is_crowd ({len(is_crowd)}) must be equal to "
+ f"length of boxes_true ({len(boxes_true)})."
+ )
if len(boxes_detection) == 0 or len(boxes_true) == 0:
return cast(npt.NDArray[np.float64], np.array([]))
ious: npt.NDArray[np.float64] = np.zeros(
@@ -831,10 +833,11 @@ def mask_non_max_suppression(
AssertionError: If `iou_threshold` is not within the closed
range from `0` to `1`.
"""
- assert 0 <= iou_threshold <= 1, (
- "Value of `iou_threshold` must be in the closed range from 0 to 1, "
- f"{iou_threshold} given."
- )
+ if not (0 <= iou_threshold <= 1):
+ raise ValueError(
+ "Value of `iou_threshold` must be in the closed range from 0 to 1, "
+ f"{iou_threshold} given."
+ )
rows, columns = predictions.shape
if columns == 5:
@@ -924,10 +927,11 @@ def box_non_max_suppression(
AssertionError: If `iou_threshold` is not within the
closed range from `0` to `1`.
"""
- assert 0 <= iou_threshold <= 1, (
- "Value of `iou_threshold` must be in the closed range from 0 to 1, "
- f"{iou_threshold} given."
- )
+ if not (0 <= iou_threshold <= 1):
+ raise ValueError(
+ "Value of `iou_threshold` must be in the closed range from 0 to 1, "
+ f"{iou_threshold} given."
+ )
sort_index, predictions, categories = _prepare_predictions_for_nms(predictions)
ious = box_iou_batch(predictions[:, :4], predictions[:, :4], overlap_metric)
keep = _nms_loop_from_iou_matrix(ious, categories, iou_threshold)
@@ -1256,10 +1260,11 @@ def oriented_box_non_max_suppression(
>>> keep
array([ True, False])
"""
- assert 0 <= iou_threshold <= 1, (
- "Value of `iou_threshold` must be in the closed range from 0 to 1, "
- f"{iou_threshold} given."
- )
+ if not (0 <= iou_threshold <= 1):
+ raise ValueError(
+ "Value of `iou_threshold` must be in the closed range from 0 to 1, "
+ f"{iou_threshold} given."
+ )
for name, arr in (("predictions", predictions), ("oriented_boxes", oriented_boxes)):
if name == "predictions":
if arr.ndim != 2 or arr.shape[1] not in (5, 6):
@@ -1401,10 +1406,11 @@ class are used by the grouping logic; overlap is computed on
f"`predictions` and `oriented_boxes` must have the same length, "
f"got {len(predictions)} and {len(oriented_boxes)}."
)
- assert 0 <= iou_threshold <= 1, (
- "Value of `iou_threshold` must be in the closed range from 0 to 1, "
- f"{iou_threshold} given."
- )
+ if not (0 <= iou_threshold <= 1):
+ raise ValueError(
+ "Value of `iou_threshold` must be in the closed range from 0 to 1, "
+ f"{iou_threshold} given."
+ )
def group_within(global_indices: npt.NDArray[np.int_]) -> list[list[int]]:
return _group_overlapping_oriented_boxes(
diff --git a/src/supervision/detection/vlm.py b/src/supervision/detection/vlm.py
index 0b0bc830a5..c83afeab8f 100644
--- a/src/supervision/detection/vlm.py
+++ b/src/supervision/detection/vlm.py
@@ -515,7 +515,8 @@ def from_florence_2(
`obb_boxes` is an optional array of shape `(n, 4, 2)` with oriented
bounding boxes.
"""
- assert len(result) == 1, f"Expected result with a single element. Got: {result}"
+ if len(result) != 1:
+ raise ValueError(f"Expected result with a single element. Got: {result}")
task = next(iter(result.keys()))
if task not in SUPPORTED_TASKS_FLORENCE_2:
raise ValueError(
@@ -564,18 +565,20 @@ def from_florence_2(
return xyxy, labels, None, None
if task in ["
", ""]:
- assert isinstance(result, str), (
- f"Expected string as result, got {type(result)}"
- )
+ if not isinstance(result, str):
+ raise TypeError(
+ f"Expected string as result, got {type(result)}"
+ )
if result == "No object detected.":
return np.empty((0, 4), dtype=np.float32), np.array([]), None, None
pattern = re.compile(r"")
match = pattern.search(result)
- assert match is not None, (
- f"Expected string to end in location tags, but got {result}"
- )
+ if match is None:
+ raise ValueError(
+ f"Expected string to end in location tags, but got {result}"
+ )
w, h = _validate_resolution(resolution_wh)
xyxy = np.array([match.groups()], dtype=np.float32)
diff --git a/src/supervision/key_points/annotators.py b/src/supervision/key_points/annotators.py
index 63a2b7d1ac..fa76e8e867 100644
--- a/src/supervision/key_points/annotators.py
+++ b/src/supervision/key_points/annotators.py
@@ -84,7 +84,8 @@ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
```
"""
- assert isinstance(scene, np.ndarray)
+ if not isinstance(scene, np.ndarray):
+ raise TypeError("scene must be a numpy array")
if len(key_points) == 0:
return scene
@@ -205,7 +206,8 @@ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
```
"""
- assert isinstance(scene, np.ndarray)
+ if not isinstance(scene, np.ndarray):
+ raise TypeError("scene must be a numpy array")
if len(key_points) == 0:
return scene
@@ -445,7 +447,8 @@ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
```
"""
- assert isinstance(scene, np.ndarray)
+ if not isinstance(scene, np.ndarray):
+ raise TypeError("scene must be a numpy array")
if len(key_points) == 0:
return scene
@@ -544,7 +547,8 @@ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
```
"""
- assert isinstance(scene, np.ndarray)
+ if not isinstance(scene, np.ndarray):
+ raise TypeError("scene must be a numpy array")
if len(key_points) == 0:
return scene
@@ -645,7 +649,8 @@ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
```
"""
- assert isinstance(scene, np.ndarray)
+ if not isinstance(scene, np.ndarray):
+ raise TypeError("scene must be a numpy array")
if len(key_points) == 0:
return scene
@@ -824,7 +829,8 @@ def annotate(
```
"""
- assert isinstance(scene, np.ndarray)
+ if not isinstance(scene, np.ndarray):
+ raise TypeError("scene must be a numpy array")
font = cv2.FONT_HERSHEY_SIMPLEX
skeletons_count, points_count, _ = key_points.xy.shape
diff --git a/src/supervision/metrics/mean_average_precision.py b/src/supervision/metrics/mean_average_precision.py
index 8d598da9c2..be09942c2b 100644
--- a/src/supervision/metrics/mean_average_precision.py
+++ b/src/supervision/metrics/mean_average_precision.py
@@ -481,9 +481,8 @@ def load_predictions(self, predictions: list[dict[str, Any]]) -> EvaluationDatas
ids = [pred["image_id"] for pred in predictions]
# Make sure the image ids from predictions exist in the current dataset
- assert set(ids) == (set(ids) & set(self.get_image_ids())), (
- "Results do not correspond to current coco set"
- )
+ if set(ids) != (set(ids) & set(self.get_image_ids())):
+ raise ValueError("Results do not correspond to current coco set")
# Check if the predictions contain any unsupported keys
if "caption" in predictions[0]:
diff --git a/src/supervision/tracker/byte_tracker/single_object_track.py b/src/supervision/tracker/byte_tracker/single_object_track.py
index 26f6bb6831..0c2d4a88fd 100644
--- a/src/supervision/tracker/byte_tracker/single_object_track.py
+++ b/src/supervision/tracker/byte_tracker/single_object_track.py
@@ -49,9 +49,8 @@ def __init__(
self.external_track_id = self.external_id_counter.NO_ID
def predict(self) -> None:
- assert self.mean is not None
- assert self.covariance is not None
- assert self.kalman_filter is not None
+ if self.mean is None or self.covariance is None or self.kalman_filter is None:
+ raise ValueError("mean, covariance and kalman_filter must not be None")
mean_state = self.mean.copy()
if self.state != TrackState.Tracked:
mean_state[7] = 0
@@ -65,8 +64,8 @@ def multi_predict(stracks: list[STrack], shared_kalman: KalmanFilter) -> None:
multi_mean = []
multi_covariance = []
for i, st in enumerate(stracks):
- assert st.mean is not None
- assert st.covariance is not None
+ if st.mean is None or st.covariance is None:
+ raise ValueError("strack mean and covariance must not be None")
multi_mean.append(st.mean.copy())
multi_covariance.append(st.covariance)
if st.state != TrackState.Tracked:
@@ -98,9 +97,8 @@ def activate(self, kalman_filter: KalmanFilter, frame_id: int) -> None:
self.start_frame = frame_id
def re_activate(self, new_track: STrack, frame_id: int) -> None:
- assert self.kalman_filter is not None
- assert self.mean is not None
- assert self.covariance is not None
+ if self.kalman_filter is None or self.mean is None or self.covariance is None:
+ raise ValueError("mean, covariance and kalman_filter must not be None")
self.mean, self.covariance = self.kalman_filter.update(
self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh)
)
@@ -118,9 +116,8 @@ def update(self, new_track: STrack, frame_id: int) -> None:
new_track: The new track data.
frame_id: The current frame ID.
"""
- assert self.kalman_filter is not None
- assert self.mean is not None
- assert self.covariance is not None
+ if self.kalman_filter is None or self.mean is None or self.covariance is None:
+ raise ValueError("mean, covariance and kalman_filter must not be None")
self.frame_id = frame_id
self.tracklet_len += 1
diff --git a/src/supervision/utils/conversion.py b/src/supervision/utils/conversion.py
index 00a57818bf..41fc33b651 100644
--- a/src/supervision/utils/conversion.py
+++ b/src/supervision/utils/conversion.py
@@ -147,8 +147,7 @@ def images_to_cv2(images: list[ImageType]) -> list[npt.NDArray[np.uint8]]:
def pillow_to_cv2(image: Image.Image) -> npt.NDArray[np.uint8]:
"""
- Converts Pillow image into OpenCV image, handling RGB -> BGR
- conversion.
+ Converts Pillow image into OpenCV image, handling RGB -> BGR conversion.
Args:
image: Pillow image (in RGB format).
diff --git a/src/supervision/utils/image.py b/src/supervision/utils/image.py
index a4768e948a..0f200b2e1c 100644
--- a/src/supervision/utils/image.py
+++ b/src/supervision/utils/image.py
@@ -44,8 +44,7 @@ def crop_image(
Bounding box coordinates in `(x_min, y_min, x_max, y_max)` format.
Returns:
- Cropped image matching input
- type.
+ Cropped image matching input type.
Examples:
```pycon
@@ -101,8 +100,7 @@ def scale_image(image: ImageType, scale_factor: float) -> ImageType:
scale_factor: Factor by which to scale the image.
Returns:
- Scaled image matching input
- type.
+ Scaled image matching input type.
Raises:
ValueError: If scale factor is non-positive.
@@ -132,7 +130,8 @@ def scale_image(image: ImageType, scale_factor: float) -> ImageType:
{ align=center width="1000" }
""" # noqa E501 // docs
- assert isinstance(image, np.ndarray)
+ if not isinstance(image, np.ndarray):
+ raise TypeError("image must be a numpy array")
if scale_factor <= 0:
raise ValueError("Scale factor must be positive.")
@@ -158,8 +157,7 @@ def resize_image(
Defaults to `False`.
Returns:
- Resized image matching input
- type.
+ Resized image matching input type.
Examples:
```pycon
@@ -190,7 +188,8 @@ def resize_image(
{ align=center width="1000" }
""" # noqa E501 // docs
- assert isinstance(image, np.ndarray)
+ if not isinstance(image, np.ndarray):
+ raise TypeError("image must be a numpy array")
if keep_aspect_ratio:
image_ratio = image.shape[1] / image.shape[0]
target_ratio = resolution_wh[0] / resolution_wh[1]
@@ -252,7 +251,8 @@ def letterbox_image(
{ align=center width="1000" }
""" # noqa E501 // docs
- assert isinstance(image, np.ndarray)
+ if not isinstance(image, np.ndarray):
+ raise TypeError("image must be a numpy array")
color = unify_to_bgr(color=color)
resized_image = resize_image(
image=image, resolution_wh=resolution_wh, keep_aspect_ratio=True
@@ -368,8 +368,7 @@ def tint_image(
Defaults to `0.5`.
Returns:
- Tinted image matching input
- type.
+ Tinted image matching input type.
Raises:
ValueError: If opacity is outside range [0.0, 1.0].
@@ -389,7 +388,8 @@ def tint_image(
{ align=center width="1000" }
""" # noqa E501 // docs
- assert isinstance(image, np.ndarray)
+ if not isinstance(image, np.ndarray):
+ raise TypeError("image must be a numpy array")
if not 0.0 <= opacity <= 1.0:
raise ValueError("opacity must be between 0.0 and 1.0")
@@ -407,8 +407,7 @@ def grayscale_image(image: ImageType) -> ImageType:
all three channels for compatibility with color-based drawing helpers.
Args:
- image: The image to convert to
- grayscale.
+ image: The image to convert to grayscale.
Returns:
3-channel grayscale image
@@ -489,8 +488,7 @@ def __init__(
Initialize context manager for saving images to directory.
Args:
- target_dir_path: Target directory path where images will be
- saved.
+ target_dir_path: Target directory path where images will be saved.
overwrite: Whether to overwrite existing directory.
Defaults to `False`.
image_name_pattern: File name pattern for saved images.
@@ -595,8 +593,7 @@ def create_tiles(
Args:
images: Images to create tiles. Elements can be either np.ndarray or
- PIL.Image, and a common representation will be agreed by the
- function.
+ PIL.Image, and a common representation will be agreed by the function.
grid_size: Expected grid size in format (n_rows, n_cols). If not
given, automated grid placement will be applied. One may also
provide only one out of two elements of the tuple - then grid
@@ -744,11 +741,13 @@ def _establish_grid_size(
return _negotiate_grid_size(images=images)
if grid_size[0] is None:
columns = grid_size[1]
- assert columns is not None
+ if columns is None:
+ raise ValueError("columns cannot be None if size is None and grid is True")
return math.ceil(len(images) / columns), columns
if grid_size[1] is None:
rows = grid_size[0]
- assert rows is not None
+ if rows is None:
+ raise ValueError("rows cannot be None if size is None and grid is True")
return rows, math.ceil(len(images) / rows)
return cast(tuple[int, int], grid_size)