From ad0e6c3b51498beb25046b74704c86ac87128503 Mon Sep 17 00:00:00 2001
From: Raif Olson
Date: Fri, 29 Mar 2024 16:59:29 -0400
Subject: [PATCH 001/136] Add minimum consecutive frames to ByteTrack
---
supervision/tracker/byte_tracker/core.py | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index c77878cf..6475bf50 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -13,7 +13,7 @@ from supervision.utils.internal import deprecated_parameter
class STrack(BaseTrack):
shared_kalman = KalmanFilter()
- def __init__(self, tlwh, score, class_ids):
+ def __init__(self, tlwh, score, class_ids, minimum_consecutive_frames):
# wait activate
self._tlwh = np.asarray(tlwh, dtype=np.float32)
self.kalman_filter = None
@@ -24,6 +24,8 @@ class STrack(BaseTrack):
self.class_ids = class_ids
self.tracklet_len = 0
+ self.minimum_consecutive_frames = minimum_consecutive_frames
+
def predict(self):
mean_state = self.mean.copy()
if self.state != TrackState.Tracked:
@@ -60,7 +62,7 @@ class STrack(BaseTrack):
self.tracklet_len = 0
self.state = TrackState.Tracked
- if frame_id == 1:
+ if frame_id == 1 and self.minimum_consecutive_frames == 1:
self.is_activated = True
self.frame_id = frame_id
self.start_frame = frame_id
@@ -71,7 +73,7 @@ class STrack(BaseTrack):
)
self.tracklet_len = 0
self.state = TrackState.Tracked
- self.is_activated = True
+
self.frame_id = frame_id
if new_id:
self.track_id = self.next_id()
@@ -93,7 +95,8 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
- self.is_activated = True
+ if self.tracklet_len >= self.minimum_consecutive_frames:
+ self.is_activated = True
self.score = new_track.score
@@ -186,6 +189,10 @@ class ByteTrack:
Increasing minimum_matching_threshold improves accuracy but risks fragmentation.
Decreasing it improves completeness but risks false positives and drift.
frame_rate (int, optional): The frame rate of the video.
+ minimum_consecutive_frames (int, optional): Number of consecutive frames that an object must
+ be tracked before it is considered a 'valid' track.
+ Increasing minimum_consecutive_frames prevents the creation of accidental tracks from
+ false detection or double detection, but risks missing shorter tracks.
""" # noqa: E501 // docs
@deprecated_parameter(
@@ -218,6 +225,7 @@ class ByteTrack:
lost_track_buffer: int = 30,
minimum_matching_threshold: float = 0.8,
frame_rate: int = 30,
+ minimum_consecutive_frames: int = 1,
):
self.track_activation_threshold = track_activation_threshold
self.minimum_matching_threshold = minimum_matching_threshold
@@ -225,6 +233,7 @@ class ByteTrack:
self.frame_id = 0
self.det_thresh = self.track_activation_threshold + 0.1
self.max_time_lost = int(frame_rate / 30.0 * lost_track_buffer)
+ self.minimum_consecutive_frames = minimum_consecutive_frames
self.kalman_filter = KalmanFilter()
self.tracked_tracks: List[STrack] = []
@@ -290,6 +299,7 @@ class ByteTrack:
return detections[detections.tracker_id != -1]
else:
+ detections = Detections.empty()
detections.tracker_id = np.array([], dtype=int)
return detections
@@ -345,7 +355,7 @@ class ByteTrack:
if len(dets) > 0:
"""Detections"""
detections = [
- STrack(STrack.tlbr_to_tlwh(tlbr), s, c)
+ STrack(STrack.tlbr_to_tlwh(tlbr), s, c, self.minimum_consecutive_frames)
for (tlbr, s, c) in zip(dets, scores_keep, class_ids_keep)
]
else:
@@ -387,7 +397,7 @@ class ByteTrack:
if len(dets_second) > 0:
"""Detections"""
detections_second = [
- STrack(STrack.tlbr_to_tlwh(tlbr), s, c)
+ STrack(STrack.tlbr_to_tlwh(tlbr), s, c, self.minimum_consecutive_frames)
for (tlbr, s, c) in zip(dets_second, scores_second, class_ids_second)
]
else:
From 8fc541339440be84875a5cd24bfd19536cf3520e Mon Sep 17 00:00:00 2001
From: Onuralp SEZER
Date: Mon, 1 Apr 2024 00:32:35 +0300
Subject: [PATCH 002/136] =?UTF-8?q?ci:=20=F0=9F=91=B7=20supervision=20docu?=
=?UTF-8?q?mentation=20=F0=9F=93=9A=20release=20CI=20added?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Onuralp SEZER
---
.../{docs.yml => publish-dev-docs.yml} | 0
.github/workflows/publish-release-docs.yml | 55 +++++++++++++++++++
2 files changed, 55 insertions(+)
rename .github/workflows/{docs.yml => publish-dev-docs.yml} (100%)
create mode 100644 .github/workflows/publish-release-docs.yml
diff --git a/.github/workflows/docs.yml b/.github/workflows/publish-dev-docs.yml
similarity index 100%
rename from .github/workflows/docs.yml
rename to .github/workflows/publish-dev-docs.yml
diff --git a/.github/workflows/publish-release-docs.yml b/.github/workflows/publish-release-docs.yml
new file mode 100644
index 00000000..22710033
--- /dev/null
+++ b/.github/workflows/publish-release-docs.yml
@@ -0,0 +1,55 @@
+name: Supervision Release Documentation Workflow 📚
+on:
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
+ cancel-in-progress: true
+
+permissions:
+ contents: write
+ pages: write
+ pull-requests: write
+
+
+jobs:
+ doc-build-deploy:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ["3.10"]
+ steps:
+ - name: 🛎️ Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: ${{ github.head_ref }}
+
+ - name: 🐍 Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.10'
+ - name: 📦 Install mkdocs-material
+ run: pip install "mkdocs-material"
+ - name: 📦 Install mkdocstrings[python]
+ run: pip install "mkdocstrings[python]"
+ - name: 📦 Install mkdocs-material[imaging]
+ run: pip install "mkdocs-material[imaging]"
+ - name: 📦 Install mike
+ run: pip install "mike"
+ - name: 📦 Install mkdocs-git-revision-date-localized-plugin
+ run: pip install "mkdocs-git-revision-date-localized-plugin"
+ - name: 📦 Install JupyterLab
+ run: pip install jupyterlab
+ - name: 📦 Install mkdocs-jupyter
+ run: pip install mkdocs-jupyter
+ - name: 📦 Install mkdocs-git-committers-plugin-2
+ run: pip install mkdocs-git-committers-plugin-2
+ - name: ⚙️ Configure git for github-actions 👷
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ - name: 🚀 Deploy MkDoc-Material 📚
+ run: |
+ latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
+ MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} mike deploy --push --update-aliases $latest_tag latest
From 574c61b6b00bb80f90b6826b404b843b3c1f6bf4 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 14:28:40 +0200
Subject: [PATCH 003/136] initial commit
---
docs/how_to/detect_and_annotate.md | 2 +-
docs/how_to/save_detections.md | 124 +++++++++++++++++++++++++++++
mkdocs.yml | 4 +-
supervision/draw/color.py | 18 ++++-
supervision/utils/image.py | 51 ++++++------
test/utils/test_image.py | 4 +-
6 files changed, 170 insertions(+), 33 deletions(-)
create mode 100644 docs/how_to/save_detections.md
diff --git a/docs/how_to/detect_and_annotate.md b/docs/how_to/detect_and_annotate.md
index 221d9ca1..adea95cf 100644
--- a/docs/how_to/detect_and_annotate.md
+++ b/docs/how_to/detect_and_annotate.md
@@ -15,7 +15,7 @@ source image.

-## Run Inference
+## Run Detection
First, you'll need to obtain predictions from your object detection or segmentation
model.
diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md
new file mode 100644
index 00000000..622c504f
--- /dev/null
+++ b/docs/how_to/save_detections.md
@@ -0,0 +1,124 @@
+---
+comments: true
+status: new
+---
+
+# Save Detections
+
+TODO
+
+## Run Detection
+
+=== "Inference"
+
+ ```python
+ import cv2
+ from inference import get_model
+
+ model = get_model(model_id="yolov8n-640")
+ image = cv2.imread()
+ results = model.infer(image)[0]
+ ```
+
+=== "Ultralytics"
+
+ ```python
+ import cv2
+ from ultralytics import YOLO
+
+ model = YOLO("yolov8n.pt")
+ image = cv2.imread()
+ results = model(image)[0]
+ ```
+
+=== "Transformers"
+
+ ```python
+ import torch
+ from PIL import Image
+ from transformers import DetrImageProcessor, DetrForObjectDetection
+
+ processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+
+ image = Image.open()
+ inputs = processor(images=image, return_tensors="pt")
+
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = image.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ ```
+
+## Save Detections as CSV
+
+TODO
+
+=== "Inference"
+
+ ```python
+ import supervision as sv
+ from inference import get_model
+
+ model = get_model(model_id="yolov8n-640")
+
+ with sv.CSVSink() as sink:
+ for frame in sv.get_video_frames_generator():
+
+ results = model.infer(image)[0]
+ detections = sv.Detections.from_inference(results)
+ sink.append(detections, {})
+ ```
+
+=== "Ultralytics"
+
+ ```python
+ import supervision as sv
+ from ultralytics import YOLO
+
+ model = YOLO("yolov8n.pt")
+
+ with sv.CSVSink() as sink:
+ for frame in sv.get_video_frames_generator():
+
+ results = model(frame)[0]
+ detections = sv.Detections.from_ultralytics(results)
+ sink.append(detections, {})
+ ```
+
+=== "Transformers"
+
+ ```python
+ import torch
+ from PIL import Image
+ from transformers import DetrImageProcessor, DetrForObjectDetection
+
+ processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+
+ image = Image.open()
+ inputs = processor(images=image, return_tensors="pt")
+
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = image.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ ```
+
+## Custom Fields
+
+TODO
+
+## Save Detections as JSON
+
+TODO
+
+## Process Video and Save Detections
+
+TODO
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index 8bae5d76..dd6feb8d 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -38,9 +38,11 @@ nav:
- Home: index.md
- How to:
- Detect and Annotate: how_to/detect_and_annotate.md
+ - Save Detections: how_to/save_detections.md
+ - Filter Detections: how_to/filter_detections.md
- Detect Small Objects: how_to/detect_small_objects.md
- Track Objects: how_to/track_objects.md
- - Filter Detections: how_to/filter_detections.md
+
- API:
- Annotators: annotators.md
- Classifications:
diff --git a/supervision/draw/color.py b/supervision/draw/color.py
index debb46f3..b195cffe 100644
--- a/supervision/draw/color.py
+++ b/supervision/draw/color.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
-from typing import List, Tuple
+from typing import List, Tuple, Union
import matplotlib.pyplot as plt
@@ -448,3 +448,19 @@ class ColorPalette:
raise ValueError("idx argument should not be negative")
idx = idx % len(self.colors)
return self.colors[idx]
+
+
+def unify_to_bgr(color: Union[Tuple[int, int, int], Color]) -> Tuple[int, int, int]:
+ """
+ Converts a color input in multiple formats to a standardized BGR format.
+
+ Args:
+ color (Union[Tuple[int, int, int], Color]): The color input to be converted,
+ which can be either a tuple of RGB values or an instance of a Color class.
+
+ Returns:
+ Tuple[int, int, int]: The color in BGR format as a tuple of three integers.
+ """
+ if issubclass(type(color), Color):
+ return color.as_bgr()
+ return color
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 4e0ba5d7..64fb6de1 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -9,7 +9,7 @@ import cv2
import numpy as np
from supervision.annotators.base import ImageType
-from supervision.draw.color import Color
+from supervision.draw.color import Color, unify_to_bgr
from supervision.draw.utils import calculate_optimal_text_scale, draw_text
from supervision.geometry.core import Point
from supervision.utils.conversion import (
@@ -25,17 +25,18 @@ MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
@convert_for_image_processing
-def crop_image(image: np.ndarray, xyxy: np.ndarray) -> np.ndarray:
+def crop_image(image: ImageType, xyxy: np.ndarray) -> np.ndarray:
"""
Crops the given image based on the given bounding box.
Args:
- image (np.ndarray): The image to be cropped, represented as a numpy array.
+ image (ImageType): The image to be cropped. `ImageType` is a flexible type,
+ accepting either `numpy.ndarray` or `PIL.Image.Image`.
xyxy (np.ndarray): A numpy array containing the bounding box coordinates
in the format (x1, y1, x2, y2).
Returns:
- (np.ndarray): The cropped image as a numpy array.
+ (ImageType): The cropped image.
Examples:
```python
@@ -74,10 +75,10 @@ def resize_image(image: np.ndarray, scale_factor: float) -> np.ndarray:
raise ValueError("Scale factor must be positive.")
old_width, old_height = image.shape[1], image.shape[0]
- nwe_width = int(old_width * scale_factor)
+ new_width = int(old_width * scale_factor)
new_height = int(old_height * scale_factor)
- return cv2.resize(image, (nwe_width, new_height), interpolation=cv2.INTER_LINEAR)
+ return cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)
def place_image(
@@ -285,14 +286,14 @@ def create_tiles(
raise ValueError("Could not create image tiles from empty list of images.")
if return_type == "auto":
return_type = _negotiate_tiles_format(images=images)
- tile_padding_color = _color_to_bgr(color=tile_padding_color)
- tile_margin_color = _color_to_bgr(color=tile_margin_color)
+ tile_padding_color = unify_to_bgr(color=tile_padding_color)
+ tile_margin_color = unify_to_bgr(color=tile_margin_color)
images = images_to_cv2(images=images)
if single_tile_size is None:
single_tile_size = _aggregate_images_shape(images=images, mode=tile_scaling)
resized_images = [
letterbox_image(
- image=i, desired_size=single_tile_size, color=tile_padding_color
+ image=i, target_resolution_wh=single_tile_size, color=tile_padding_color
)
for i in images
]
@@ -311,8 +312,8 @@ def create_tiles(
titles_anchors = fill(
sequence=titles_anchors, desired_size=len(images), content=None
)
- titles_color = _color_to_bgr(color=titles_color)
- titles_background_color = _color_to_bgr(color=titles_background_color)
+ titles_color = unify_to_bgr(color=titles_color)
+ titles_background_color = unify_to_bgr(color=titles_background_color)
tiles = _generate_tiles(
images=resized_images,
grid_size=grid_size,
@@ -546,8 +547,8 @@ def _generate_color_image(
@convert_for_image_processing
def letterbox_image(
- image: np.ndarray,
- desired_size: Tuple[int, int],
+ image: ImageType,
+ target_resolution_wh: Tuple[int, int],
color: Union[Tuple[int, int, int], Color] = (0, 0, 0),
) -> np.ndarray:
"""
@@ -555,27 +556,27 @@ def letterbox_image(
ratio, adding padding of given color if needed to maintain aspect ratio.
Args:
- image (np.ndarray): Input image (type will be adjusted by decorator,
+ image (ImageType): Input image (type will be adjusted by decorator,
you can provide PIL.Image)
- desired_size (Tuple[int, int]): image size (width, height) representing
+ target_resolution_wh (Tuple[int, int]): image size (width, height) representing
the target dimensions.
color (Union[Tuple[int, int, int], Color]): the color to pad with - If
tuple provided - should be BGR.
Returns:
- np.ndarray: letterboxed image (type may be adjusted to PIL.Image by
+ ImageType: letterboxed image (type may be adjusted to PIL.Image by
decorator if function was called with PIL.Image)
"""
- color = _color_to_bgr(color=color)
+ color = unify_to_bgr(color=color)
resized_img = resize_image_keeping_aspect_ratio(
image=image,
- desired_size=desired_size,
+ desired_size=target_resolution_wh,
)
new_height, new_width = resized_img.shape[:2]
- top_padding = (desired_size[1] - new_height) // 2
- bottom_padding = desired_size[1] - new_height - top_padding
- left_padding = (desired_size[0] - new_width) // 2
- right_padding = desired_size[0] - new_width - left_padding
+ top_padding = (target_resolution_wh[1] - new_height) // 2
+ bottom_padding = target_resolution_wh[1] - new_height - top_padding
+ left_padding = (target_resolution_wh[0] - new_width) // 2
+ right_padding = target_resolution_wh[0] - new_width - left_padding
return cv2.copyMakeBorder(
resized_img,
top_padding,
@@ -625,9 +626,3 @@ def resize_image_keeping_aspect_ratio(
new_height = desired_size[1]
new_width = int(desired_size[1] * img_ratio)
return cv2.resize(image, (new_width, new_height))
-
-
-def _color_to_bgr(color: Union[Tuple[int, int, int], Color]) -> Tuple[int, int, int]:
- if issubclass(type(color), Color):
- return color.as_bgr()
- return color
diff --git a/test/utils/test_image.py b/test/utils/test_image.py
index e50f2e57..50b6b5c1 100644
--- a/test/utils/test_image.py
+++ b/test/utils/test_image.py
@@ -62,7 +62,7 @@ def test_letterbox_image_for_opencv_image() -> None:
# when
result = letterbox_image(
- image=image, desired_size=(1024, 1024), color=(255, 255, 255)
+ image=image, target_resolution_wh=(1024, 1024), color=(255, 255, 255)
)
# then
@@ -88,7 +88,7 @@ def test_letterbox_image_for_pillow_image() -> None:
# when
result = letterbox_image(
- image=image, desired_size=(1024, 1024), color=(255, 255, 255)
+ image=image, target_resolution_wh=(1024, 1024), color=(255, 255, 255)
)
# then
From 5b1ee8f870c1c9e091fb84167fbf060ebc9c0cac Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 12:31:30 +0000
Subject: [PATCH 004/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/how_to/save_detections.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md
index 622c504f..81de930e 100644
--- a/docs/how_to/save_detections.md
+++ b/docs/how_to/save_detections.md
@@ -62,12 +62,12 @@ TODO
```python
import supervision as sv
from inference import get_model
-
+
model = get_model(model_id="yolov8n-640")
-
+
with sv.CSVSink() as sink:
for frame in sv.get_video_frames_generator():
-
+
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
sink.append(detections, {})
@@ -78,12 +78,12 @@ TODO
```python
import supervision as sv
from ultralytics import YOLO
-
+
model = YOLO("yolov8n.pt")
-
+
with sv.CSVSink() as sink:
for frame in sv.get_video_frames_generator():
-
+
results = model(frame)[0]
detections = sv.Detections.from_ultralytics(results)
sink.append(detections, {})
@@ -121,4 +121,4 @@ TODO
## Process Video and Save Detections
-TODO
\ No newline at end of file
+TODO
From 2115c9a1526dfc8d2d5de5f74651ac48f3f2c585 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 14:50:32 +0200
Subject: [PATCH 005/136] crop_image improvements
---
supervision/utils/image.py | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 64fb6de1..a6466eaa 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -25,15 +25,19 @@ MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
@convert_for_image_processing
-def crop_image(image: ImageType, xyxy: np.ndarray) -> np.ndarray:
+def crop_image(
+ image: ImageType,
+ xyxy: Union[np.ndarray, List[int], Tuple[int, int, int, int]]
+) -> ImageType:
"""
Crops the given image based on the given bounding box.
Args:
image (ImageType): The image to be cropped. `ImageType` is a flexible type,
accepting either `numpy.ndarray` or `PIL.Image.Image`.
- xyxy (np.ndarray): A numpy array containing the bounding box coordinates
- in the format (x1, y1, x2, y2).
+ xyxy (Union[np.ndarray, List[int], Tuple[int, int, int, int]]): A bounding box
+ coordinates in the format (x_min, y_min, x_max, y_max), accepted as either
+ a numpy array, a list, or a tuple.
Returns:
(ImageType): The cropped image.
@@ -50,9 +54,11 @@ def crop_image(image: ImageType, xyxy: np.ndarray) -> np.ndarray:
```
"""
+ if isinstance(xyxy, (list, tuple)):
+ xyxy = np.array(xyxy)
xyxy = np.round(xyxy).astype(int)
- x1, y1, x2, y2 = xyxy
- return image[y1:y2, x1:x2]
+ x_min, y_min, x_max, y_max = xyxy.flatten()
+ return image[y_min:y_max, x_min:x_max]
@convert_for_image_processing
From f2e6d882fbb64f326e179cdd24f5837715982ac8 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 12:50:48 +0000
Subject: [PATCH 006/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/utils/image.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index a6466eaa..954533a2 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -26,8 +26,7 @@ MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
@convert_for_image_processing
def crop_image(
- image: ImageType,
- xyxy: Union[np.ndarray, List[int], Tuple[int, int, int, int]]
+ image: ImageType, xyxy: Union[np.ndarray, List[int], Tuple[int, int, int, int]]
) -> ImageType:
"""
Crops the given image based on the given bounding box.
From 7d0cf8a7233aa81b204e57d72de57b59b3e1f994 Mon Sep 17 00:00:00 2001
From: Onuralp SEZER
Date: Tue, 9 Apr 2024 15:44:53 +0300
Subject: [PATCH 007/136] =?UTF-8?q?feat:=20=F0=9F=9A=80=20initial=20sv.Key?=
=?UTF-8?q?points=20features=20added?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Onuralp SEZER
---
supervision/__init__.py | 1 +
supervision/detection/core.py | 2 +-
supervision/detection/utils.py | 98 +-----------------
supervision/keypoints/__init__.py | 0
supervision/keypoints/core.py | 160 +++++++++++++++++++++++++++++
supervision/keypoints/utils.py | 0
supervision/validators/__init__.py | 141 +++++++++++++++++++++++++
7 files changed, 304 insertions(+), 98 deletions(-)
create mode 100644 supervision/keypoints/__init__.py
create mode 100644 supervision/keypoints/core.py
create mode 100644 supervision/keypoints/utils.py
create mode 100644 supervision/validators/__init__.py
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 35f525b0..df04534d 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -69,6 +69,7 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
+from supervision.keypoints.core import KeyPoints
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
from supervision.utils.file import list_files_with_extensions
diff --git a/supervision/detection/core.py b/supervision/detection/core.py
index 8c29cfc4..bb8775ad 100644
--- a/supervision/detection/core.py
+++ b/supervision/detection/core.py
@@ -17,11 +17,11 @@ from supervision.detection.utils import (
mask_to_xyxy,
merge_data,
process_roboflow_result,
- validate_detections_fields,
xywh_to_xyxy,
)
from supervision.geometry.core import Position
from supervision.utils.internal import deprecated
+from supervision.validators import validate_detections_fields
@dataclass
diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py
index b3928fdb..3eeba5b4 100644
--- a/supervision/detection/utils.py
+++ b/supervision/detection/utils.py
@@ -1,5 +1,5 @@
from itertools import chain
-from typing import Any, Dict, List, Optional, Tuple, Union
+from typing import Dict, List, Optional, Tuple, Union
import cv2
import numpy as np
@@ -656,102 +656,6 @@ def calculate_masks_centroids(masks: np.ndarray) -> np.ndarray:
return np.column_stack((centroid_x, centroid_y)).astype(int)
-def validate_xyxy(xyxy: Any) -> None:
- expected_shape = "(_, 4)"
- actual_shape = str(getattr(xyxy, "shape", None))
- is_valid = isinstance(xyxy, np.ndarray) and xyxy.ndim == 2 and xyxy.shape[1] == 4
- if not is_valid:
- raise ValueError(
- f"xyxy must be a 2D np.ndarray with shape {expected_shape}, but got shape "
- f"{actual_shape}"
- )
-
-
-def validate_mask(mask: Any, n: int) -> None:
- expected_shape = f"({n}, H, W)"
- actual_shape = str(getattr(mask, "shape", None))
- is_valid = mask is None or (
- isinstance(mask, np.ndarray) and len(mask.shape) == 3 and mask.shape[0] == n
- )
- if not is_valid:
- raise ValueError(
- f"mask must be a 3D np.ndarray with shape {expected_shape}, but got shape "
- f"{actual_shape}"
- )
-
-
-def validate_class_id(class_id: Any, n: int) -> None:
- expected_shape = f"({n},)"
- actual_shape = str(getattr(class_id, "shape", None))
- is_valid = class_id is None or (
- isinstance(class_id, np.ndarray) and class_id.shape == (n,)
- )
- if not is_valid:
- raise ValueError(
- f"class_id must be a 1D np.ndarray with shape {expected_shape}, but got "
- f"shape {actual_shape}"
- )
-
-
-def validate_confidence(confidence: Any, n: int) -> None:
- expected_shape = f"({n},)"
- actual_shape = str(getattr(confidence, "shape", None))
- is_valid = confidence is None or (
- isinstance(confidence, np.ndarray) and confidence.shape == (n,)
- )
- if not is_valid:
- raise ValueError(
- f"confidence must be a 1D np.ndarray with shape {expected_shape}, but got "
- f"shape {actual_shape}"
- )
-
-
-def validate_tracker_id(tracker_id: Any, n: int) -> None:
- expected_shape = f"({n},)"
- actual_shape = str(getattr(tracker_id, "shape", None))
- is_valid = tracker_id is None or (
- isinstance(tracker_id, np.ndarray) and tracker_id.shape == (n,)
- )
- if not is_valid:
- raise ValueError(
- f"tracker_id must be a 1D np.ndarray with shape {expected_shape}, but got "
- f"shape {actual_shape}"
- )
-
-
-def validate_data(data: Dict[str, Any], n: int) -> None:
- for key, value in data.items():
- if isinstance(value, list):
- if len(value) != n:
- raise ValueError(f"Length of list for key '{key}' must be {n}")
- elif isinstance(value, np.ndarray):
- if value.ndim == 1 and value.shape[0] != n:
- raise ValueError(f"Shape of np.ndarray for key '{key}' must be ({n},)")
- elif value.ndim > 1 and value.shape[0] != n:
- raise ValueError(
- f"First dimension of np.ndarray for key '{key}' must have size {n}"
- )
- else:
- raise ValueError(f"Value for key '{key}' must be a list or np.ndarray")
-
-
-def validate_detections_fields(
- xyxy: Any,
- mask: Any,
- class_id: Any,
- confidence: Any,
- tracker_id: Any,
- data: Dict[str, Any],
-) -> None:
- validate_xyxy(xyxy)
- n = len(xyxy)
- validate_mask(mask, n)
- validate_class_id(class_id, n)
- validate_confidence(confidence, n)
- validate_tracker_id(tracker_id, n)
- validate_data(data, n)
-
-
def is_data_equal(data_a: Dict[str, np.ndarray], data_b: Dict[str, np.ndarray]) -> bool:
"""
Compares the data payloads of two Detections instances.
diff --git a/supervision/keypoints/__init__.py b/supervision/keypoints/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/supervision/keypoints/core.py b/supervision/keypoints/core.py
new file mode 100644
index 00000000..bf2ab886
--- /dev/null
+++ b/supervision/keypoints/core.py
@@ -0,0 +1,160 @@
+from dataclasses import dataclass, field
+from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
+
+import numpy as np
+import numpy.typing as npt
+
+from supervision.config import CLASS_NAME_DATA_FIELD
+from supervision.detection.utils import get_data_item, is_data_equal
+from supervision.validators import validate_keypoints_fields
+
+
+@dataclass
+class KeyPoints:
+ """
+ The `sv.KeyPoints` allows you to convert results from a variety of keypoint
+ detections models into a single, unified format.
+
+ ```python
+ import cv2
+ import supervision as sv
+ from ultralytics import YOLO
+
+ image = cv2.imread()
+ model = YOLO('yolov8s-pose.pt')
+ result = model(image)[0]
+ keypoints = sv.KeyPoints.from_ultralytics(result)
+ ```
+
+ !!! tip
+
+ In `sv.KeyPoints`, detection data is categorized into two main field types:
+ fixed and custom. The fixed fields include `xy`, `confidence`,
+ `class_id`. For any additional data requirements, custom
+ fields come into play, stored in the data field. These custom fields are easily
+ accessible using the `detections[]` syntax, providing flexibility
+ for diverse data handling needs.
+
+ Attributes:
+ xy (np.ndarray): An array of shape `(n, 2)` containing
+ the bounding boxes coordinates in format `[x1, y1]`
+ confidence (Optional[np.ndarray]): An array of shape
+ `(n,)` containing the confidence scores of the keypoint detections.
+ class_id (Optional[np.ndarray]): An array of shape
+ `(n,)` containing the class ids of the keypoint detections.
+ data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
+ data where each key is a string representing the data type, and the value
+ is either a NumPy array or a list of corresponding data.
+
+ !!! warning
+
+ The `data` field in the `sv.KeyPoints` class is currently in an experimental
+ phase. Please be aware that its API and functionality are subject to change in
+ future updates as we continue to refine and improve its capabilities.
+ We encourage users to experiment with this feature and provide feedback, but
+ also to be prepared for potential modifications in upcoming releases.
+ """
+
+ xy: npt.NDArray[np.float32]
+ class_id: Optional[npt.NDArray[np.int_]] = None
+ confidence: Optional[npt.NDArray[np.float32]] = None
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+
+ def __post_init__(self):
+ validate_keypoints_fields(
+ xy=self.xy,
+ confidence=self.confidence,
+ class_id=self.class_id,
+ data=self.data,
+ )
+
+ def __len__(self) -> int:
+ """
+ Returns the number of keypoints in the keypoints object.
+ """
+ return len(self.xy)
+
+ def __iter__(
+ self,
+ ) -> Iterator[
+ Tuple[
+ np.ndarray,
+ Optional[np.ndarray],
+ Optional[float],
+ Optional[int],
+ Optional[int],
+ Dict[str, Union[np.ndarray, List]],
+ ]
+ ]:
+ """
+ Iterates over the Keypoint object and yield a tuple of
+ `(xy, confidence, class_id, data)` for each keypoint detection.
+ """
+ for i in range(len(self.xy)):
+ yield (
+ self.xy[i],
+ self.confidence[i] if self.confidence is not None else None,
+ self.class_id[i] if self.class_id is not None else None,
+ get_data_item(self.data, i),
+ )
+
+ def __eq__(self, other: "KeyPoints") -> bool:
+ return all(
+ [
+ np.array_equal(self.xy, other.xy),
+ np.array_equal(self.class_id, other.class_id),
+ np.array_equal(self.confidence, other.confidence),
+ is_data_equal(self.data, other.data),
+ ]
+ )
+
+ @classmethod
+ def from_ultralytics(cls, ultralytics_results) -> "KeyPoints":
+ """
+ Creates a Keypoints instance from a
+ [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
+
+ Args:
+ ultralytics_results (ultralytics.engine.results.Keypoints):
+ The output Results instance from YOLOv8
+
+ Returns:
+ KeyPoints: A new Keypoints object.
+
+ Example:
+ ```python
+ import cv2
+ import supervision as sv
+ from ultralytics import YOLO
+
+ image = cv2.imread()
+ model = YOLO('yolov8s-pose.pt')
+ result = model(image)[0]
+ keypoints = sv.KeyPoints.from_ultralytics(result)
+ ```
+ """ #
+
+ class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
+
+ xy = ultralytics_results.keypoints.xy.cpu().numpy()
+ confidence = ultralytics_results.keypoints.conf.cpu().numpy()
+ class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
+ data = {CLASS_NAME_DATA_FIELD: class_names}
+ return cls(xy, class_id, confidence, data)
+
+ @classmethod
+ def empty(cls) -> "KeyPoints":
+ """
+ Create an empty Keypoints object with no keypoints.
+
+ Returns:
+ (Keypoints): An empty Keypoints object.
+
+ Example:
+ ```python
+ from supervision import Keypoints
+ empty_keypoints = Keypoints.empty()
+ ```
+ """
+ return cls(xy=np.empty((0, 0, 2), dtype=np.float32))
diff --git a/supervision/keypoints/utils.py b/supervision/keypoints/utils.py
new file mode 100644
index 00000000..e69de29b
diff --git a/supervision/validators/__init__.py b/supervision/validators/__init__.py
new file mode 100644
index 00000000..9a9fca8c
--- /dev/null
+++ b/supervision/validators/__init__.py
@@ -0,0 +1,141 @@
+from typing import Any, Dict
+
+import numpy as np
+
+
+def validate_xyxy(xyxy: Any) -> None:
+ expected_shape = "(_, 4)"
+ actual_shape = str(getattr(xyxy, "shape", None))
+ is_valid = isinstance(xyxy, np.ndarray) and xyxy.ndim == 2 and xyxy.shape[1] == 4
+ if not is_valid:
+ raise ValueError(
+ f"xyxy must be a 2D np.ndarray with shape {expected_shape}, but got shape "
+ f"{actual_shape}"
+ )
+
+
+def validate_mask(mask: Any, n: int) -> None:
+ expected_shape = f"({n}, H, W)"
+ actual_shape = str(getattr(mask, "shape", None))
+ is_valid = mask is None or (
+ isinstance(mask, np.ndarray) and len(mask.shape) == 3 and mask.shape[0] == n
+ )
+ if not is_valid:
+ raise ValueError(
+ f"mask must be a 3D np.ndarray with shape {expected_shape}, but got shape "
+ f"{actual_shape}"
+ )
+
+
+def validate_class_id(class_id: Any, n: int) -> None:
+ expected_shape = f"({n},)"
+ actual_shape = str(getattr(class_id, "shape", None))
+ is_valid = class_id is None or (
+ isinstance(class_id, np.ndarray) and class_id.shape == (n,)
+ )
+ if not is_valid:
+ raise ValueError(
+ f"class_id must be a 1D np.ndarray with shape {expected_shape}, but got "
+ f"shape {actual_shape}"
+ )
+
+
+def validate_confidence(confidence: Any, n: int) -> None:
+ expected_shape = f"({n},)"
+ actual_shape = str(getattr(confidence, "shape", None))
+ is_valid = confidence is None or (
+ isinstance(confidence, np.ndarray) and confidence.shape == (n,)
+ )
+ if not is_valid:
+ raise ValueError(
+ f"confidence must be a 1D np.ndarray with shape {expected_shape}, but got "
+ f"shape {actual_shape}"
+ )
+
+
+def validate_keypoint_confidence(confidence: Any, n: int, m: int) -> None:
+ expected_shape = f"({n,m})"
+ actual_shape = str(getattr(confidence, "shape", None))
+
+ if confidence is not None:
+ is_valid = isinstance(confidence, np.ndarray) and confidence.shape == (n, m)
+ if not is_valid:
+ raise ValueError(
+ f"confidence must be a 1D np.ndarray with shape {expected_shape}, but "
+ "got"
+ f"shape {actual_shape}"
+ )
+
+
+def validate_tracker_id(tracker_id: Any, n: int) -> None:
+ expected_shape = f"({n},)"
+ actual_shape = str(getattr(tracker_id, "shape", None))
+ is_valid = tracker_id is None or (
+ isinstance(tracker_id, np.ndarray) and tracker_id.shape == (n,)
+ )
+ if not is_valid:
+ raise ValueError(
+ f"tracker_id must be a 1D np.ndarray with shape {expected_shape}, but got "
+ f"shape {actual_shape}"
+ )
+
+
+def validate_data(data: Dict[str, Any], n: int) -> None:
+ for key, value in data.items():
+ if isinstance(value, list):
+ if len(value) != n:
+ raise ValueError(f"Length of list for key '{key}' must be {n}")
+ elif isinstance(value, np.ndarray):
+ if value.ndim == 1 and value.shape[0] != n:
+ raise ValueError(f"Shape of np.ndarray for key '{key}' must be ({n},)")
+ elif value.ndim > 1 and value.shape[0] != n:
+ raise ValueError(
+ f"First dimension of np.ndarray for key '{key}' must have size {n}"
+ )
+ else:
+ raise ValueError(f"Value for key '{key}' must be a list or np.ndarray")
+
+
+def validate_xy(xy: Any, n: int, m: int) -> None:
+ expected_shape = f"({n, m},)"
+ actual_shape = str(getattr(xy, "shape", None))
+
+ is_valid = isinstance(xy, np.ndarray) and (
+ xy.shape == (n, m, 2) or xy.shape == (n, m, 3)
+ )
+ if not is_valid:
+ raise ValueError(
+ f"xy must be a 2D np.ndarray with shape {expected_shape}, but got shape "
+ f"{actual_shape}"
+ )
+
+
+def validate_detections_fields(
+ xyxy: Any,
+ mask: Any,
+ class_id: Any,
+ confidence: Any,
+ tracker_id: Any,
+ data: Dict[str, Any],
+) -> None:
+ validate_xyxy(xyxy)
+ n = len(xyxy)
+ validate_mask(mask, n)
+ validate_class_id(class_id, n)
+ validate_confidence(confidence, n)
+ validate_tracker_id(tracker_id, n)
+ validate_data(data, n)
+
+
+def validate_keypoints_fields(
+ xy: Any,
+ class_id: Any,
+ confidence: Any,
+ data: Dict[str, Any],
+) -> None:
+ n = len(xy)
+ m = len(xy[0]) if len(xy) > 0 else 0
+ validate_xy(xy, n, m)
+ validate_class_id(class_id, n)
+ validate_keypoint_confidence(confidence, n, m)
+ validate_data(data, n)
From 5d43e1380d248d2c8db5a3352feea855199d01f6 Mon Sep 17 00:00:00 2001
From: Onuralp SEZER
Date: Tue, 9 Apr 2024 17:42:31 +0300
Subject: [PATCH 008/136] =?UTF-8?q?feat(test):=20=F0=9F=9A=80=20mock=20key?=
=?UTF-8?q?points=20added?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Onuralp SEZER
---
test/test_utils.py | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/test/test_utils.py b/test/test_utils.py
index b8ef9231..d3624845 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -3,6 +3,7 @@ from typing import Any, Dict, List, Optional
import numpy as np
from supervision.detection.core import Detections
+from supervision.keypoints.core import KeyPoints
def mock_detections(
@@ -30,5 +31,24 @@ def mock_detections(
)
+def mock_keypoints(
+ xy: List[List[float]],
+ confidence: Optional[List[float]] = None,
+ class_id: Optional[List[int]] = None,
+ data: Optional[Dict[str, List[Any]]] = None,
+) -> KeyPoints:
+ def convert_data(data: Dict[str, List[Any]]):
+ return {k: np.array(v) for k, v in data.items()}
+
+ return KeyPoints(
+ xy=np.array(xy, dtype=np.float32),
+ confidence=(
+ confidence if confidence is None else np.array(confidence, dtype=np.float32)
+ ),
+ class_id=(class_id if class_id is None else np.array(class_id, dtype=int)),
+ data=convert_data(data) if data else {},
+ )
+
+
def assert_almost_equal(actual, expected, tolerance=1e-5):
assert abs(actual - expected) < tolerance, f"Expected {expected}, but got {actual}."
From b63379749e9862846307fcbea99be3d4b7b3b68b Mon Sep 17 00:00:00 2001
From: Onuralp SEZER
Date: Tue, 9 Apr 2024 17:43:19 +0300
Subject: [PATCH 009/136] =?UTF-8?q?fix(keypoint):=20=F0=9F=9A=80=20getitem?=
=?UTF-8?q?=20and=20setitem=20funcs=20added?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Onuralp SEZER
---
supervision/keypoints/core.py | 88 +++++++++++++++++++++++++++++++++--
1 file changed, 84 insertions(+), 4 deletions(-)
diff --git a/supervision/keypoints/core.py b/supervision/keypoints/core.py
index bf2ab886..646b99db 100644
--- a/supervision/keypoints/core.py
+++ b/supervision/keypoints/core.py
@@ -13,7 +13,7 @@ from supervision.validators import validate_keypoints_fields
class KeyPoints:
"""
The `sv.KeyPoints` allows you to convert results from a variety of keypoint
- detections models into a single, unified format.
+ keypoints models into a single, unified format.
```python
import cv2
@@ -32,16 +32,16 @@ class KeyPoints:
fixed and custom. The fixed fields include `xy`, `confidence`,
`class_id`. For any additional data requirements, custom
fields come into play, stored in the data field. These custom fields are easily
- accessible using the `detections[]` syntax, providing flexibility
+ accessible using the `keypoints[]` syntax, providing flexibility
for diverse data handling needs.
Attributes:
xy (np.ndarray): An array of shape `(n, 2)` containing
the bounding boxes coordinates in format `[x1, y1]`
confidence (Optional[np.ndarray]): An array of shape
- `(n,)` containing the confidence scores of the keypoint detections.
+ `(n,)` containing the confidence scores of the keypoint keypoints.
class_id (Optional[np.ndarray]): An array of shape
- `(n,)` containing the class ids of the keypoint detections.
+ `(n,)` containing the class ids of the keypoint keypoints.
data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
data where each key is a string representing the data type, and the value
is either a NumPy array or a list of corresponding data.
@@ -143,6 +143,86 @@ class KeyPoints:
data = {CLASS_NAME_DATA_FIELD: class_names}
return cls(xy, class_id, confidence, data)
+ def __getitem__(
+ self, index: Union[int, slice, List[int], np.ndarray, str]
+ ) -> Union["KeyPoints", List, np.ndarray, None]:
+ """
+ Get a subset of the KeyPoints object or access an item from its data field.
+
+ When provided with an integer, slice, list of integers, or a numpy array, this
+ method returns a new KeyPoints object that represents a subset of the original
+ keypoints. When provided with a string, it accesses the corresponding item in
+ the data dictionary.
+
+ Args:
+ index (Union[int, slice, List[int], np.ndarray, str]): The index, indices,
+ or key to access a subset of the KeyPoints or an item from the data.
+
+ Returns:
+ Union[KeyPoints, Any]: A subset of the KeyPoints object or an item from
+ the data field.
+
+ Example:
+ ```python
+ import supervision as sv
+
+ keypoints = sv.KeyPoints()
+
+ first_detection = keypoints[0]
+ first_10_keypoints = keypoints[0:10]
+ some_keypoints = keypoints[[0, 2, 4]]
+ class_0_keypoints = keypoints[keypoints.class_id == 0]
+ high_confidence_keypoints = keypoints[keypoints.confidence > 0.5]
+
+ feature_vector = keypoints['feature_vector']
+ ```
+ """
+ if isinstance(index, str):
+ return self.data.get(index)
+ if isinstance(index, int):
+ index = [index]
+ return KeyPoints(
+ xy=self.xy[index],
+ confidence=self.confidence[index] if self.confidence is not None else None,
+ class_id=self.class_id[index] if self.class_id is not None else None,
+ data=get_data_item(self.data, index),
+ )
+
+ def __setitem__(self, key: str, value: Union[np.ndarray, List]):
+ """
+ Set a value in the data dictionary of the KeyPoints object.
+
+ Args:
+ key (str): The key in the data dictionary to set.
+ value (Union[np.ndarray, List]): The value to set for the key.
+
+ Example:
+ ```python
+ import cv2
+ import supervision as sv
+ from ultralytics import YOLO
+
+ image = cv2.imread()
+ model = YOLO('yolov8s.pt')
+
+ result = model(image)[0]
+ keypoints = sv.KeyPoints.from_ultralytics(result)
+
+ keypoints['names'] = [
+ model.model.names[class_id]
+ for class_id
+ in keypoints.class_id
+ ]
+ ```
+ """
+ if not isinstance(value, (np.ndarray, list)):
+ raise TypeError("Value must be a np.ndarray or a list")
+
+ if isinstance(value, list):
+ value = np.array(value)
+
+ self.data[key] = value
+
@classmethod
def empty(cls) -> "KeyPoints":
"""
From 94e06ab0d6b71d87a3839207efe6b23d6bf74186 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 16:46:05 +0200
Subject: [PATCH 010/136] scale_image, resize_image changes
---
docs/utils/image.md | 10 +-
supervision/__init__.py | 4 +-
supervision/annotators/core.py | 4 +-
supervision/utils/image.py | 165 ++++++++++++++++++++++-----------
4 files changed, 124 insertions(+), 59 deletions(-)
diff --git a/docs/utils/image.md b/docs/utils/image.md
index 087aebd7..b61cddfc 100644
--- a/docs/utils/image.md
+++ b/docs/utils/image.md
@@ -18,10 +18,10 @@ status: new
:::supervision.utils.image.crop_image
-
letterbox_image
+ scale_image
-:::supervision.utils.image.letterbox_image
+:::supervision.utils.image.scale_image
resize_image
@@ -29,6 +29,12 @@ status: new
:::supervision.utils.image.resize_image
+
+
letterbox_image
+
+
+:::supervision.utils.image.letterbox_image
+
place_image
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 8c3acc26..d6aeeab5 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -78,8 +78,8 @@ from supervision.utils.image import (
crop_image,
letterbox_image,
place_image,
- resize_image,
- resize_image_keeping_aspect_ratio,
+ scale_image,
+ resize_image
)
from supervision.utils.notebook import plot_image, plot_images_grid
from supervision.utils.video import (
diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py
index 9f6cdb36..38ddc442 100644
--- a/supervision/annotators/core.py
+++ b/supervision/annotators/core.py
@@ -13,7 +13,7 @@ from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import draw_polygon
from supervision.geometry.core import Position
from supervision.utils.conversion import convert_for_annotation_method
-from supervision.utils.image import crop_image, place_image, resize_image
+from supervision.utils.image import crop_image, place_image, scale_image
class BoundingBoxAnnotator(BaseAnnotator):
@@ -1965,7 +1965,7 @@ class CropAnnotator(BaseAnnotator):
crop_image(image=scene, xyxy=xyxy) for xyxy in detections.xyxy.astype(int)
]
resized_crops = [
- resize_image(image=crop, scale_factor=self.scale_factor) for crop in crops
+ scale_image(image=crop, scale_factor=self.scale_factor) for crop in crops
]
anchors = detections.get_anchors_coordinates(anchor=self.position).astype(int)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 954533a2..99fcb3f2 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -7,6 +7,7 @@ from typing import Callable, List, Literal, Optional, Tuple, Union
import cv2
import numpy as np
+import numpy.typing as npt
from supervision.annotators.base import ImageType
from supervision.draw.color import Color, unify_to_bgr
@@ -26,7 +27,8 @@ MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
@convert_for_image_processing
def crop_image(
- image: ImageType, xyxy: Union[np.ndarray, List[int], Tuple[int, int, int, int]]
+ image: ImageType,
+ xyxy: Union[npt.NDArray[int], List[int], Tuple[int, int, int, int]]
) -> ImageType:
"""
Crops the given image based on the given bounding box.
@@ -35,23 +37,49 @@ def crop_image(
image (ImageType): The image to be cropped. `ImageType` is a flexible type,
accepting either `numpy.ndarray` or `PIL.Image.Image`.
xyxy (Union[np.ndarray, List[int], Tuple[int, int, int, int]]): A bounding box
- coordinates in the format (x_min, y_min, x_max, y_max), accepted as either
- a numpy array, a list, or a tuple.
+ coordinates in the format `(x_min, y_min, x_max, y_max)`, accepted as either
+ a `numpy.ndarray`, a `list`, or a `tuple`.
Returns:
- (ImageType): The cropped image.
+ (ImageType): The cropped image. The type is determined by the input type and
+ may be either a `numpy.ndarray` or `PIL.Image.Image`.
Examples:
+
+ === "OpenCV"
+
```python
+ import cv2
import supervision as sv
- detection = sv.Detections(...)
- with sv.ImageSink(target_dir_path='target/directory/path') as sink:
- for xyxy in detection.xyxy:
- cropped_image = sv.crop_image(image=image, xyxy=xyxy)
- sink.save_image(image=cropped_image)
+ image = cv2.imread(
)
+ image.shape
+ # (1080, 1920, 3)
+
+ xyxy = [200, 400, 600, 800]
+ cropped_image = sv.crop_image(image=image, xyxy=xyxy)
+ cropped_image.shape
+ # (400, 400, 3)
```
- """
+
+ === "Pillow"
+
+ ```python
+ from PIL import Image
+ import supervision as sv
+
+ image = Image.open()
+ image.size
+ # (1920, 1080)
+
+ xyxy = [200, 400, 600, 800]
+ cropped_image = sv.crop_image(image=image, xyxy=xyxy)
+ cropped_image.size
+ # (400, 400)
+ ```
+
+ { align=center width="800" }
+ """ # noqa E501 // docs
if isinstance(xyxy, (list, tuple)):
xyxy = np.array(xyxy)
@@ -61,29 +89,62 @@ def crop_image(
@convert_for_image_processing
-def resize_image(image: np.ndarray, scale_factor: float) -> np.ndarray:
+def scale_image(image: ImageType, scale_factor: float) -> ImageType:
"""
- Resizes an image by a given scale factor using cv2.INTER_LINEAR interpolation.
+ Scales the given image based on the given scale factor.
Args:
- image (np.ndarray): The input image to be resized.
+ image (ImageType): The image to be scaled. `ImageType` is a flexible type,
+ accepting either `numpy.ndarray` or `PIL.Image.Image`.
scale_factor (float): The factor by which the image will be scaled. Scale
- factor > 1.0 zooms in, < 1.0 zooms out.
+ factor > `1.0` zooms in, < `1.0` zooms out.
Returns:
- np.ndarray: The resized image.
+ (ImageType): The scaled image. The type is determined by the input type and
+ may be either a `numpy.ndarray` or `PIL.Image.Image`.
Raises:
ValueError: If the scale factor is non-positive.
+
+ Examples:
+
+ === "OpenCV"
+
+ ```python
+ import cv2
+ import supervision as sv
+
+ image = cv2.imread()
+ image.shape
+ # (1080, 1920, 3)
+
+ scaled_image = sv.scale_image(image=image, scale_factor=0.5)
+ scaled_image.shape
+ # (540, 960, 3)
+ ```
+
+ === "Pillow"
+
+ ```python
+ from PIL import Image
+ import supervision as sv
+
+ image = Image.open()
+ image.size
+ # (1920, 1080)
+
+ scaled_image = sv.scale_image(image=image, scale_factor=0.5)
+ scaled_image.size
+ # (540, 960)
+ ```
"""
if scale_factor <= 0:
raise ValueError("Scale factor must be positive.")
- old_width, old_height = image.shape[1], image.shape[0]
- new_width = int(old_width * scale_factor)
- new_height = int(old_height * scale_factor)
-
- return cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)
+ width_old, height_old = image.shape[1], image.shape[0]
+ width_new = int(width_old * scale_factor)
+ height_new = int(height_old * scale_factor)
+ return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
def place_image(
@@ -573,9 +634,10 @@ def letterbox_image(
decorator if function was called with PIL.Image)
"""
color = unify_to_bgr(color=color)
- resized_img = resize_image_keeping_aspect_ratio(
+ resized_img = resize_image(
image=image,
- desired_size=target_resolution_wh,
+ target_resolution_wh=target_resolution_wh,
+ keep_aspect_ratio=True
)
new_height, new_width = resized_img.shape[:2]
top_padding = (target_resolution_wh[1] - new_height) // 2
@@ -594,40 +656,37 @@ def letterbox_image(
@convert_for_image_processing
-def resize_image_keeping_aspect_ratio(
- image: np.ndarray,
- desired_size: Tuple[int, int],
-) -> np.ndarray:
+def resize_image(
+ image: ImageType,
+ target_resolution_wh: Tuple[int, int],
+ keep_aspect_ratio: bool = False
+) -> ImageType:
"""
- Resize and pad image preserving its aspect ratio.
+ Resizes the given image to a specified resolution. Can maintain the original aspect
+ ratio or resize directly to the desired dimensions.
- For example: input image is (640, 480) and we want to resize into
- (1024, 1024). If this rectangular image is just resized naively
- to square-shape output - aspect ratio would be altered. If we do not
- want this to happen - we may resize bigger dimension (640) to 1024.
- Ratio of change is 1.6. This ratio is later on used to calculate scaling
- in the other dimension. As a result we have (1024, 768) image.
-
- Parameters:
- - image (np.ndarray): Input image (type will be adjusted by decorator,
- you can provide PIL.Image)
- - desired_size (Tuple[int, int]): image size (width, height) representing the
- target dimensions. Parameter will be used to dictate maximum size of
- output image. Output size may be smaller - to preserve aspect ratio of original
- image.
+ Args:
+ image (ImageType): The image to be resized. `ImageType` is a flexible type,
+ accepting either `numpy.ndarray` or `PIL.Image.Image`.
+ target_resolution_wh (Tuple[int, int]): The target resolution as
+ `(width, height)`.
+ keep_aspect_ratio (bool, optional): Flag to maintain the image's original
+ aspect ratio. Defaults to `False`.
Returns:
- np.ndarray: resized image (type may be adjusted to PIL.Image by decorator
- if function was called with PIL.Image)
+ ImageType: The resized image. The type is determined by the input type and
+ may be either a `numpy.ndarray` or `PIL.Image.Image`.
"""
- if image.shape[:2] == desired_size[::-1]:
- return image
- img_ratio = image.shape[1] / image.shape[0]
- desired_ratio = desired_size[0] / desired_size[1]
- if img_ratio >= desired_ratio:
- new_width = desired_size[0]
- new_height = int(desired_size[0] / img_ratio)
+ if keep_aspect_ratio:
+ image_ratio = image.shape[1] / image.shape[0]
+ target_ratio = target_resolution_wh[0] / target_resolution_wh[1]
+ if image_ratio >= target_ratio:
+ width_new = target_resolution_wh[0]
+ height_new = int(target_resolution_wh[0] / image_ratio)
+ else:
+ height_new = target_resolution_wh[1]
+ width_new = int(target_resolution_wh[1] * image_ratio)
else:
- new_height = desired_size[1]
- new_width = int(desired_size[1] * img_ratio)
- return cv2.resize(image, (new_width, new_height))
+ width_new, height_new = target_resolution_wh
+
+ return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
From ad946893624e8609282035af3443d361f06b5564 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 14:46:45 +0000
Subject: [PATCH 011/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/__init__.py | 2 +-
supervision/utils/image.py | 8 +++-----
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/supervision/__init__.py b/supervision/__init__.py
index d6aeeab5..b14a37ab 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -78,8 +78,8 @@ from supervision.utils.image import (
crop_image,
letterbox_image,
place_image,
+ resize_image,
scale_image,
- resize_image
)
from supervision.utils.notebook import plot_image, plot_images_grid
from supervision.utils.video import (
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 99fcb3f2..8fe25eea 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -28,7 +28,7 @@ MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
@convert_for_image_processing
def crop_image(
image: ImageType,
- xyxy: Union[npt.NDArray[int], List[int], Tuple[int, int, int, int]]
+ xyxy: Union[npt.NDArray[int], List[int], Tuple[int, int, int, int]],
) -> ImageType:
"""
Crops the given image based on the given bounding box.
@@ -635,9 +635,7 @@ def letterbox_image(
"""
color = unify_to_bgr(color=color)
resized_img = resize_image(
- image=image,
- target_resolution_wh=target_resolution_wh,
- keep_aspect_ratio=True
+ image=image, target_resolution_wh=target_resolution_wh, keep_aspect_ratio=True
)
new_height, new_width = resized_img.shape[:2]
top_padding = (target_resolution_wh[1] - new_height) // 2
@@ -659,7 +657,7 @@ def letterbox_image(
def resize_image(
image: ImageType,
target_resolution_wh: Tuple[int, int],
- keep_aspect_ratio: bool = False
+ keep_aspect_ratio: bool = False,
) -> ImageType:
"""
Resizes the given image to a specified resolution. Can maintain the original aspect
From 233d25d4c539eb180e2091856f3ebdddb6695c95 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 17:48:13 +0200
Subject: [PATCH 012/136] fix `resize_image` tests after rename
---
supervision/utils/image.py | 116 ++++++++++++++++++++++++-------------
test/utils/test_image.py | 16 ++---
2 files changed, 86 insertions(+), 46 deletions(-)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 8fe25eea..4c5cc9c6 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -135,7 +135,7 @@ def scale_image(image: ImageType, scale_factor: float) -> ImageType:
scaled_image = sv.scale_image(image=image, scale_factor=0.5)
scaled_image.size
- # (540, 960)
+ # (960, 540)
```
"""
if scale_factor <= 0:
@@ -147,6 +147,81 @@ def scale_image(image: ImageType, scale_factor: float) -> ImageType:
return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
+@convert_for_image_processing
+def resize_image(
+ image: ImageType,
+ resolution_wh: Tuple[int, int],
+ keep_aspect_ratio: bool = False,
+) -> ImageType:
+ """
+ Resizes the given image to a specified resolution. Can maintain the original aspect
+ ratio or resize directly to the desired dimensions.
+
+ Args:
+ image (ImageType): The image to be resized. `ImageType` is a flexible type,
+ accepting either `numpy.ndarray` or `PIL.Image.Image`.
+ resolution_wh (Tuple[int, int]): The target resolution as
+ `(width, height)`.
+ keep_aspect_ratio (bool, optional): Flag to maintain the image's original
+ aspect ratio. Defaults to `False`.
+
+ Returns:
+ ImageType: The resized image. The type is determined by the input type and
+ may be either a `numpy.ndarray` or `PIL.Image.Image`.
+
+ Examples:
+
+ === "OpenCV"
+
+ ```python
+ import cv2
+ import supervision as sv
+
+ image = cv2.imread()
+ image.shape
+ # (1080, 1920, 3)
+
+ resized_image = sv.resize_image(
+ image=image, resolution_wh=(1000, 1000), keep_aspect_ratio=True
+ )
+ resized_image.shape
+ # (562, 1000, 3)
+ ```
+
+ === "Pillow"
+
+ ```python
+ from PIL import Image
+ import supervision as sv
+
+ image = Image.open()
+ image.size
+ # (1920, 1080)
+
+ resized_image = sv.resize_image(
+ image=image, resolution_wh=(1000, 1000), keep_aspect_ratio=True
+ )
+ resized_image.size
+ # (1000, 562)
+ ```
+
+ { align=center width="800" }
+ """ # noqa E501 // docs
+ if keep_aspect_ratio:
+ image_ratio = image.shape[1] / image.shape[0]
+ target_ratio = resolution_wh[0] / resolution_wh[1]
+ if image_ratio >= target_ratio:
+ width_new = resolution_wh[0]
+ height_new = int(resolution_wh[0] / image_ratio)
+ else:
+ height_new = resolution_wh[1]
+ width_new = int(resolution_wh[1] * image_ratio)
+ else:
+ width_new, height_new = resolution_wh
+
+ return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
+
+
def place_image(
scene: np.ndarray, image: np.ndarray, anchor: Tuple[int, int]
) -> np.ndarray:
@@ -635,7 +710,7 @@ def letterbox_image(
"""
color = unify_to_bgr(color=color)
resized_img = resize_image(
- image=image, target_resolution_wh=target_resolution_wh, keep_aspect_ratio=True
+ image=image, resolution_wh=target_resolution_wh, keep_aspect_ratio=True
)
new_height, new_width = resized_img.shape[:2]
top_padding = (target_resolution_wh[1] - new_height) // 2
@@ -651,40 +726,3 @@ def letterbox_image(
cv2.BORDER_CONSTANT,
value=color,
)
-
-
-@convert_for_image_processing
-def resize_image(
- image: ImageType,
- target_resolution_wh: Tuple[int, int],
- keep_aspect_ratio: bool = False,
-) -> ImageType:
- """
- Resizes the given image to a specified resolution. Can maintain the original aspect
- ratio or resize directly to the desired dimensions.
-
- Args:
- image (ImageType): The image to be resized. `ImageType` is a flexible type,
- accepting either `numpy.ndarray` or `PIL.Image.Image`.
- target_resolution_wh (Tuple[int, int]): The target resolution as
- `(width, height)`.
- keep_aspect_ratio (bool, optional): Flag to maintain the image's original
- aspect ratio. Defaults to `False`.
-
- Returns:
- ImageType: The resized image. The type is determined by the input type and
- may be either a `numpy.ndarray` or `PIL.Image.Image`.
- """
- if keep_aspect_ratio:
- image_ratio = image.shape[1] / image.shape[0]
- target_ratio = target_resolution_wh[0] / target_resolution_wh[1]
- if image_ratio >= target_ratio:
- width_new = target_resolution_wh[0]
- height_new = int(target_resolution_wh[0] / image_ratio)
- else:
- height_new = target_resolution_wh[1]
- width_new = int(target_resolution_wh[1] * image_ratio)
- else:
- width_new, height_new = target_resolution_wh
-
- return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
diff --git a/test/utils/test_image.py b/test/utils/test_image.py
index 50b6b5c1..b3e28a27 100644
--- a/test/utils/test_image.py
+++ b/test/utils/test_image.py
@@ -8,19 +8,20 @@ from supervision import Color, Point
from supervision.utils.image import (
create_tiles,
letterbox_image,
- resize_image_keeping_aspect_ratio,
+ resize_image,
)
-def test_resize_image_keeping_aspect_ratio_for_opencv_image() -> None:
+def test_resize_image_for_opencv_image() -> None:
# given
image = np.zeros((480, 640, 3), dtype=np.uint8)
expected_result = np.zeros((768, 1024, 3), dtype=np.uint8)
# when
- result = resize_image_keeping_aspect_ratio(
+ result = resize_image(
image=image,
- desired_size=(1024, 1024),
+ resolution_wh=(1024, 1024),
+ keep_aspect_ratio=True,
)
# then
@@ -29,15 +30,16 @@ def test_resize_image_keeping_aspect_ratio_for_opencv_image() -> None:
), "Expected output shape to be (w, h): (1024, 768)"
-def test_resize_image_keeping_aspect_ratio_for_pillow_image() -> None:
+def test_resize_image_for_pillow_image() -> None:
# given
image = Image.new(mode="RGB", size=(640, 480), color=(0, 0, 0))
expected_result = Image.new(mode="RGB", size=(1024, 768), color=(0, 0, 0))
# when
- result = resize_image_keeping_aspect_ratio(
+ result = resize_image(
image=image,
- desired_size=(1024, 1024),
+ resolution_wh=(1024, 1024),
+ keep_aspect_ratio=True,
)
# then
From a7dccfcc0d3f168e36b9f4be06686206be1d3260 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 15:48:30 +0000
Subject: [PATCH 013/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/utils/image.py | 2 +-
test/utils/test_image.py | 6 +-----
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 4c5cc9c6..e04ea45e 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -204,7 +204,7 @@ def resize_image(
resized_image.size
# (1000, 562)
```
-
+
{ align=center width="800" }
""" # noqa E501 // docs
if keep_aspect_ratio:
diff --git a/test/utils/test_image.py b/test/utils/test_image.py
index b3e28a27..a114d9aa 100644
--- a/test/utils/test_image.py
+++ b/test/utils/test_image.py
@@ -5,11 +5,7 @@ import pytest
from PIL import Image, ImageChops
from supervision import Color, Point
-from supervision.utils.image import (
- create_tiles,
- letterbox_image,
- resize_image,
-)
+from supervision.utils.image import create_tiles, letterbox_image, resize_image
def test_resize_image_for_opencv_image() -> None:
From 774ac2ed4f82706c0a889741b2619cc8575b5ee0 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 17:59:19 +0200
Subject: [PATCH 014/136] update `letterbox_image` docs
---
supervision/utils/image.py | 86 +++++++++++++++++++-------------------
test/utils/test_image.py | 4 +-
2 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index e04ea45e..274cef2f 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -222,6 +222,48 @@ def resize_image(
return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
+@convert_for_image_processing
+def letterbox_image(
+ image: ImageType,
+ resolution_wh: Tuple[int, int],
+ color: Union[Tuple[int, int, int], Color] = (0, 0, 0),
+) -> ImageType:
+ """
+ Resizes and pads an image to a specified resolution with a given color, maintaining
+ the original aspect ratio.
+
+ Args:
+ image (ImageType): The image to be resized. `ImageType` is a flexible type,
+ accepting either `numpy.ndarray` or `PIL.Image.Image`.
+ resolution_wh (Tuple[int, int]): The target resolution as
+ `(width, height)`.
+ color (Union[Tuple[int, int, int], Color]): The color to pad with. If tuple
+ provided it should be in BGR format.
+
+ Returns:
+ ImageType: The resized image. The type is determined by the input type and
+ may be either a `numpy.ndarray` or `PIL.Image.Image`.
+ """
+ color = unify_to_bgr(color=color)
+ resized_image = resize_image(
+ image=image, resolution_wh=resolution_wh, keep_aspect_ratio=True
+ )
+ height_new, width_new = resized_image.shape[:2]
+ padding_top = (resolution_wh[1] - height_new) // 2
+ padding_bottom = resolution_wh[1] - height_new - padding_top
+ padding_left = (resolution_wh[0] - width_new) // 2
+ padding_right = resolution_wh[0] - width_new - padding_left
+ return cv2.copyMakeBorder(
+ resized_image,
+ padding_top,
+ padding_bottom,
+ padding_left,
+ padding_right,
+ cv2.BORDER_CONSTANT,
+ value=color,
+ )
+
+
def place_image(
scene: np.ndarray, image: np.ndarray, anchor: Tuple[int, int]
) -> np.ndarray:
@@ -434,7 +476,7 @@ def create_tiles(
single_tile_size = _aggregate_images_shape(images=images, mode=tile_scaling)
resized_images = [
letterbox_image(
- image=i, target_resolution_wh=single_tile_size, color=tile_padding_color
+ image=i, resolution_wh=single_tile_size, color=tile_padding_color
)
for i in images
]
@@ -684,45 +726,3 @@ def _generate_color_image(
shape: Tuple[int, int], color: Tuple[int, int, int]
) -> np.ndarray:
return np.ones(shape[::-1] + (3,), dtype=np.uint8) * color
-
-
-@convert_for_image_processing
-def letterbox_image(
- image: ImageType,
- target_resolution_wh: Tuple[int, int],
- color: Union[Tuple[int, int, int], Color] = (0, 0, 0),
-) -> np.ndarray:
- """
- Resize and pad image to fit the desired size, preserving its aspect
- ratio, adding padding of given color if needed to maintain aspect ratio.
-
- Args:
- image (ImageType): Input image (type will be adjusted by decorator,
- you can provide PIL.Image)
- target_resolution_wh (Tuple[int, int]): image size (width, height) representing
- the target dimensions.
- color (Union[Tuple[int, int, int], Color]): the color to pad with - If
- tuple provided - should be BGR.
-
- Returns:
- ImageType: letterboxed image (type may be adjusted to PIL.Image by
- decorator if function was called with PIL.Image)
- """
- color = unify_to_bgr(color=color)
- resized_img = resize_image(
- image=image, resolution_wh=target_resolution_wh, keep_aspect_ratio=True
- )
- new_height, new_width = resized_img.shape[:2]
- top_padding = (target_resolution_wh[1] - new_height) // 2
- bottom_padding = target_resolution_wh[1] - new_height - top_padding
- left_padding = (target_resolution_wh[0] - new_width) // 2
- right_padding = target_resolution_wh[0] - new_width - left_padding
- return cv2.copyMakeBorder(
- resized_img,
- top_padding,
- bottom_padding,
- left_padding,
- right_padding,
- cv2.BORDER_CONSTANT,
- value=color,
- )
diff --git a/test/utils/test_image.py b/test/utils/test_image.py
index a114d9aa..487434ae 100644
--- a/test/utils/test_image.py
+++ b/test/utils/test_image.py
@@ -60,7 +60,7 @@ def test_letterbox_image_for_opencv_image() -> None:
# when
result = letterbox_image(
- image=image, target_resolution_wh=(1024, 1024), color=(255, 255, 255)
+ image=image, resolution_wh=(1024, 1024), color=(255, 255, 255)
)
# then
@@ -86,7 +86,7 @@ def test_letterbox_image_for_pillow_image() -> None:
# when
result = letterbox_image(
- image=image, target_resolution_wh=(1024, 1024), color=(255, 255, 255)
+ image=image, resolution_wh=(1024, 1024), color=(255, 255, 255)
)
# then
From 079e62fb0c544248cc6511d79d6be664928aa81e Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 21:23:23 +0200
Subject: [PATCH 015/136] rename `place_image` to overlay `image`
---
docs/utils/image.md | 14 ++---
supervision/__init__.py | 2 +-
supervision/annotators/core.py | 4 +-
supervision/utils/image.py | 96 +++++++++++++++++++++++++---------
4 files changed, 81 insertions(+), 35 deletions(-)
diff --git a/docs/utils/image.md b/docs/utils/image.md
index b61cddfc..16a18dc4 100644
--- a/docs/utils/image.md
+++ b/docs/utils/image.md
@@ -5,12 +5,6 @@ status: new
# Image Utils
-
-
ImageSink
-
-
-:::supervision.utils.image.ImageSink
-
crop_image
@@ -39,4 +33,10 @@ status: new
place_image
-:::supervision.utils.image.place_image
+:::supervision.utils.image.overlay_image
+
+
+
ImageSink
+
+
+:::supervision.utils.image.ImageSink
diff --git a/supervision/__init__.py b/supervision/__init__.py
index b14a37ab..9ff09557 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -77,7 +77,7 @@ from supervision.utils.image import (
create_tiles,
crop_image,
letterbox_image,
- place_image,
+ overlay_image,
resize_image,
scale_image,
)
diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py
index 38ddc442..854a9ece 100644
--- a/supervision/annotators/core.py
+++ b/supervision/annotators/core.py
@@ -13,7 +13,7 @@ from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import draw_polygon
from supervision.geometry.core import Position
from supervision.utils.conversion import convert_for_annotation_method
-from supervision.utils.image import crop_image, place_image, scale_image
+from supervision.utils.image import crop_image, overlay_image, scale_image
class BoundingBoxAnnotator(BaseAnnotator):
@@ -1974,7 +1974,7 @@ class CropAnnotator(BaseAnnotator):
(x1, y1), (x2, y2) = self.calculate_crop_coordinates(
anchor=anchor, crop_wh=crop_wh, position=self.position
)
- scene = place_image(scene=scene, image=resized_crop, anchor=(x1, y1))
+ scene = overlay_image(scene=scene, inserted_image=resized_crop, anchor=(x1, y1))
color = resolve_color(
color=self.border_color,
detections=detections,
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 274cef2f..b579ba75 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -243,7 +243,41 @@ def letterbox_image(
Returns:
ImageType: The resized image. The type is determined by the input type and
may be either a `numpy.ndarray` or `PIL.Image.Image`.
- """
+
+ ## Examples:
+
+ === "OpenCV"
+
+ ```python
+ import cv2
+ import supervision as sv
+
+ image = cv2.imread()
+ image.shape
+ # (1080, 1920, 3)
+
+ letterboxed_image = sv.letterbox_image(image=image, resolution_wh=(1000, 1000))
+ letterboxed_image.shape
+ # (1000, 1000, 3)
+ ```
+
+ === "Pillow"
+
+ ```python
+ from PIL import Image
+ import supervision as sv
+
+ image = Image.open()
+ image.size
+ # (1920, 1080)
+
+ letterboxed_image = sv.letterbox_image(image=image, resolution_wh=(1000, 1000))
+ letterboxed_image.size
+ # (1000, 1000)
+ ```
+
+ { align=center width="800" }
+ """ # noqa E501 // docs
color = unify_to_bgr(color=color)
resized_image = resize_image(
image=image, resolution_wh=resolution_wh, keep_aspect_ratio=True
@@ -264,48 +298,60 @@ def letterbox_image(
)
-def place_image(
- scene: np.ndarray, image: np.ndarray, anchor: Tuple[int, int]
-) -> np.ndarray:
+def overlay_image(
+ image: npt.NDArray[np.uint8],
+ overlay: npt.NDArray[np.uint8],
+ anchor: Tuple[int, int]
+) -> npt.NDArray[np.uint8]:
"""
Places an image onto a scene at a given anchor point, handling cases where
the image's position is partially or completely outside the scene's bounds.
Args:
- scene (np.ndarray): The background scene onto which the image is placed.
- image (np.ndarray): The image to be placed onto the scene.
- anchor (Tuple[int, int]): The (x, y) coordinates in the scene where the
+ image (np.ndarray): The background scene onto which the image is placed.
+ overlay (np.ndarray): The image to be placed onto the scene.
+ anchor (Tuple[int, int]): The `(x, y)` coordinates in the scene where the
top-left corner of the image will be placed.
Returns:
- np.ndarray: The modified scene with the image placed at the anchor point,
- or unchanged if the image placement is completely outside the scene.
+ np.ndarray: The result image with overlay.
+
+ Examples:
+ ```python
+ import cv2
+ import numpy as np
+ import supervision as sv
+
+ image = cv2.imread()
+ overlay = np.zeros((200, 200, 3), dtype=np.uint8)
+ result_image = sv.letterbox_image(
+ image=image, overlay=overlay, anchor=(200, 400))
+ ```
"""
- scene_height, scene_width = scene.shape[:2]
- image_height, image_width = image.shape[:2]
+ scene_height, scene_width = image.shape[:2]
+ image_height, image_width = overlay.shape[:2]
anchor_x, anchor_y = anchor
is_out_horizontally = anchor_x + image_width <= 0 or anchor_x >= scene_width
is_out_vertically = anchor_y + image_height <= 0 or anchor_y >= scene_height
if is_out_horizontally or is_out_vertically:
- return scene
+ return image
- start_y = max(anchor_y, 0)
- start_x = max(anchor_x, 0)
- end_y = min(scene_height, anchor_y + image_height)
- end_x = min(scene_width, anchor_x + image_width)
+ x_min = max(anchor_x, 0)
+ y_min = max(anchor_y, 0)
+ x_max = min(scene_width, anchor_x + image_width)
+ y_max = min(scene_height, anchor_y + image_height)
- crop_start_y = max(-anchor_y, 0)
- crop_start_x = max(-anchor_x, 0)
- crop_end_y = image_height - max((anchor_y + image_height) - scene_height, 0)
- crop_end_x = image_width - max((anchor_x + image_width) - scene_width, 0)
+ crop_x_min = max(-anchor_x, 0)
+ crop_y_min = max(-anchor_y, 0)
+ crop_x_max = image_width - max((anchor_x + image_width) - scene_width, 0)
+ crop_y_max = image_height - max((anchor_y + image_height) - scene_height, 0)
- scene[start_y:end_y, start_x:end_x] = image[
- crop_start_y:crop_end_y, crop_start_x:crop_end_x
- ]
+ image[y_min:y_max, x_min:x_max] = \
+ overlay[crop_y_min:crop_y_max, crop_x_min:crop_x_max]
- return scene
+ return image
class ImageSink:
@@ -335,7 +381,7 @@ class ImageSink:
source_path='source_video.mp4', stride=2):
sink.save_image(image=image)
```
- """
+ """ # noqa E501 // docs
self.target_dir_path = target_dir_path
self.overwrite = overwrite
From f872fddaffe2d34c11f2fcc62b6d1b53a28aec85 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 19:24:02 +0000
Subject: [PATCH 016/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/annotators/core.py | 4 +++-
supervision/utils/image.py | 9 +++++----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/supervision/annotators/core.py b/supervision/annotators/core.py
index 854a9ece..ac901862 100644
--- a/supervision/annotators/core.py
+++ b/supervision/annotators/core.py
@@ -1974,7 +1974,9 @@ class CropAnnotator(BaseAnnotator):
(x1, y1), (x2, y2) = self.calculate_crop_coordinates(
anchor=anchor, crop_wh=crop_wh, position=self.position
)
- scene = overlay_image(scene=scene, inserted_image=resized_crop, anchor=(x1, y1))
+ scene = overlay_image(
+ scene=scene, inserted_image=resized_crop, anchor=(x1, y1)
+ )
color = resolve_color(
color=self.border_color,
detections=detections,
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index b579ba75..f6eef9ca 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -275,7 +275,7 @@ def letterbox_image(
letterboxed_image.size
# (1000, 1000)
```
-
+
{ align=center width="800" }
""" # noqa E501 // docs
color = unify_to_bgr(color=color)
@@ -301,7 +301,7 @@ def letterbox_image(
def overlay_image(
image: npt.NDArray[np.uint8],
overlay: npt.NDArray[np.uint8],
- anchor: Tuple[int, int]
+ anchor: Tuple[int, int],
) -> npt.NDArray[np.uint8]:
"""
Places an image onto a scene at a given anchor point, handling cases where
@@ -348,8 +348,9 @@ def overlay_image(
crop_x_max = image_width - max((anchor_x + image_width) - scene_width, 0)
crop_y_max = image_height - max((anchor_y + image_height) - scene_height, 0)
- image[y_min:y_max, x_min:x_max] = \
- overlay[crop_y_min:crop_y_max, crop_x_min:crop_x_max]
+ image[y_min:y_max, x_min:x_max] = overlay[
+ crop_y_min:crop_y_max, crop_x_min:crop_x_max
+ ]
return image
From c30e377eb5393aed81b182d6a66217e73c6d439a Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 22:07:12 +0200
Subject: [PATCH 017/136] more docs improvements
---
docs/utils/image.md | 2 +-
supervision/__init__.py | 1 +
supervision/utils/conversion.py | 4 ++--
supervision/utils/image.py | 31 ++++++++++++-------------------
supervision/utils/iterables.py | 4 ++--
5 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/docs/utils/image.md b/docs/utils/image.md
index 16a18dc4..87fd1943 100644
--- a/docs/utils/image.md
+++ b/docs/utils/image.md
@@ -30,7 +30,7 @@ status: new
:::supervision.utils.image.letterbox_image
-
place_image
+ overlay_image
:::supervision.utils.image.overlay_image
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 9ff09557..6cdb23ca 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -34,6 +34,7 @@ from supervision.dataset.core import (
ClassificationDataset,
DetectionDataset,
)
+from supervision.utils.conversion import pillow_to_cv2, cv2_to_pillow
from supervision.detection.annotate import BoxAnnotator
from supervision.detection.core import Detections
from supervision.detection.line_zone import LineZone, LineZoneAnnotator
diff --git a/supervision/utils/conversion.py b/supervision/utils/conversion.py
index 608104bc..8ddce969 100644
--- a/supervision/utils/conversion.py
+++ b/supervision/utils/conversion.py
@@ -81,7 +81,7 @@ def pillow_to_cv2(image: Image.Image) -> np.ndarray:
image (Image.Image): Pillow image (in RGB format).
Returns:
- np.ndarray: Input image converted to OpenCV format.
+ (np.ndarray): Input image converted to OpenCV format.
"""
scene = np.array(image)
scene = cv2.cvtColor(scene, cv2.COLOR_RGB2BGR)
@@ -97,7 +97,7 @@ def cv2_to_pillow(image: np.ndarray) -> Image.Image:
image (np.ndarray): OpenCV image (in BGR format).
Returns:
- Image.Image: Input image converted to Pillow format.
+ (Image.Image): Input image converted to Pillow format.
"""
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
return Image.fromarray(image)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index b579ba75..488a9d36 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -44,8 +44,6 @@ def crop_image(
(ImageType): The cropped image. The type is determined by the input type and
may be either a `numpy.ndarray` or `PIL.Image.Image`.
- Examples:
-
=== "OpenCV"
```python
@@ -106,8 +104,6 @@ def scale_image(image: ImageType, scale_factor: float) -> ImageType:
Raises:
ValueError: If the scale factor is non-positive.
- Examples:
-
=== "OpenCV"
```python
@@ -166,11 +162,9 @@ def resize_image(
aspect ratio. Defaults to `False`.
Returns:
- ImageType: The resized image. The type is determined by the input type and
+ (ImageType): The resized image. The type is determined by the input type and
may be either a `numpy.ndarray` or `PIL.Image.Image`.
- Examples:
-
=== "OpenCV"
```python
@@ -226,7 +220,7 @@ def resize_image(
def letterbox_image(
image: ImageType,
resolution_wh: Tuple[int, int],
- color: Union[Tuple[int, int, int], Color] = (0, 0, 0),
+ color: Union[Tuple[int, int, int], Color] = Color.BLACK,
) -> ImageType:
"""
Resizes and pads an image to a specified resolution with a given color, maintaining
@@ -241,11 +235,9 @@ def letterbox_image(
provided it should be in BGR format.
Returns:
- ImageType: The resized image. The type is determined by the input type and
+ (ImageType): The resized image. The type is determined by the input type and
may be either a `numpy.ndarray` or `PIL.Image.Image`.
- ## Examples:
-
=== "OpenCV"
```python
@@ -314,7 +306,7 @@ def overlay_image(
top-left corner of the image will be placed.
Returns:
- np.ndarray: The result image with overlay.
+ (np.ndarray): The result image with overlay.
Examples:
```python
@@ -323,10 +315,11 @@ def overlay_image(
import supervision as sv
image = cv2.imread()
- overlay = np.zeros((200, 200, 3), dtype=np.uint8)
- result_image = sv.letterbox_image(
- image=image, overlay=overlay, anchor=(200, 400))
+ overlay = np.zeros((400, 400, 3), dtype=np.uint8)
+ result_image = sv.overlay_image(image=image, overlay=overlay, anchor=(200, 400))
```
+
+ { align=center width="800" }
"""
scene_height, scene_width = image.shape[:2]
image_height, image_width = overlay.shape[:2]
@@ -374,11 +367,11 @@ class ImageSink:
Examples:
```python
import supervision as sv
+
+ frames_generator = sv.get_video_frames_generator(, stride=2)
- with sv.ImageSink(target_dir_path='target/directory/path',
- overwrite=True) as sink:
- for image in sv.get_video_frames_generator(
- source_path='source_video.mp4', stride=2):
+ with sv.ImageSink(target_dir_path=) as sink:
+ for image in frames_generator:
sink.save_image(image=image)
```
""" # noqa E501 // docs
diff --git a/supervision/utils/iterables.py b/supervision/utils/iterables.py
index ad570379..52bfbeb6 100644
--- a/supervision/utils/iterables.py
+++ b/supervision/utils/iterables.py
@@ -16,7 +16,7 @@ def create_batches(
batch_size (int): The expected size of a batch.
Returns:
- Generator[List[V], None, None]: A generator that yields chunks
+ (Generator[List[V], None, None]): A generator that yields chunks
of `sequence` of size `batch_size`, up to the length of
the input `sequence`.
@@ -54,7 +54,7 @@ def fill(sequence: List[V], desired_size: int, content: V) -> List[V]:
`sequence` as padding.
Returns:
- List[V]: A padded version of the input `sequence` (if needed).
+ (List[V]): A padded version of the input `sequence` (if needed).
Examples:
```python
From 6248fa7d170ce765e5d59d2bce839836171841f3 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 20:08:10 +0000
Subject: [PATCH 018/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/__init__.py | 2 +-
supervision/utils/image.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 6cdb23ca..f8e7a832 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -34,7 +34,6 @@ from supervision.dataset.core import (
ClassificationDataset,
DetectionDataset,
)
-from supervision.utils.conversion import pillow_to_cv2, cv2_to_pillow
from supervision.detection.annotate import BoxAnnotator
from supervision.detection.core import Detections
from supervision.detection.line_zone import LineZone, LineZoneAnnotator
@@ -72,6 +71,7 @@ from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
+from supervision.utils.conversion import cv2_to_pillow, pillow_to_cv2
from supervision.utils.file import list_files_with_extensions
from supervision.utils.image import (
ImageSink,
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index dd3316ec..44ccbeac 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -368,7 +368,7 @@ class ImageSink:
Examples:
```python
import supervision as sv
-
+
frames_generator = sv.get_video_frames_generator(, stride=2)
with sv.ImageSink(target_dir_path=) as sink:
From d929d6f58b989a0fd38a359ca4b12b40921cccf2 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 22:44:10 +0200
Subject: [PATCH 019/136] `How to Save Detections` guide update
---
docs/how_to/save_detections.md | 222 ++++++++++++++++++++++++++++-----
1 file changed, 191 insertions(+), 31 deletions(-)
diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md
index 81de930e..e0233f9b 100644
--- a/docs/how_to/save_detections.md
+++ b/docs/how_to/save_detections.md
@@ -9,48 +9,64 @@ TODO
## Run Detection
+First, you'll need to obtain predictions from your object detection or segmentation
+model. You can learn more on this topic in our
+[How to Detect and Annotate](/latest/how_to/detect_and_annotate.md) guide.
+
=== "Inference"
```python
- import cv2
+ import supervision as sv
from inference import get_model
model = get_model(model_id="yolov8n-640")
- image = cv2.imread()
- results = model.infer(image)[0]
+ frames_generator = sv.get_video_frames_generator()
+
+ for frame in frames_generator:
+
+ results = model.infer(image)[0]
+ detections = sv.Detections.from_inference(results)
```
=== "Ultralytics"
```python
- import cv2
+ import supervision as sv
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
- image = cv2.imread()
- results = model(image)[0]
+ frames_generator = sv.get_video_frames_generator()
+
+ for frame in frames_generator:
+
+ results = model(frame)[0]
+ detections = sv.Detections.from_ultralytics(results)
```
=== "Transformers"
```python
import torch
- from PIL import Image
+ import supervision as sv
from transformers import DetrImageProcessor, DetrForObjectDetection
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+ frames_generator = sv.get_video_frames_generator()
- image = Image.open()
- inputs = processor(images=image, return_tensors="pt")
+ for frame in frames_generator:
- with torch.no_grad():
- outputs = model(**inputs)
+ frame = sv.cv2_to_pillow(frame)
+ inputs = processor(images=frame, return_tensors="pt")
- width, height = image.size
- target_size = torch.tensor([[height, width]])
- results = processor.post_process_object_detection(
- outputs=outputs, target_sizes=target_size)[0]
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = frame.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ detections = sv.Detections.from_transformers(results)
```
## Save Detections as CSV
@@ -59,14 +75,15 @@ TODO
=== "Inference"
- ```python
+ ```{ .py hl_lines="7 12" }
import supervision as sv
from inference import get_model
model = get_model(model_id="yolov8n-640")
+ frames_generator = sv.get_video_frames_generator()
with sv.CSVSink() as sink:
- for frame in sv.get_video_frames_generator():
+ for frame in frames_generator:
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
@@ -75,14 +92,15 @@ TODO
=== "Ultralytics"
- ```python
+ ```{ .py hl_lines="7 12" }
import supervision as sv
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
+ frames_generator = sv.get_video_frames_generator()
with sv.CSVSink() as sink:
- for frame in sv.get_video_frames_generator():
+ for frame in frames_generator:
results = model(frame)[0]
detections = sv.Detections.from_ultralytics(results)
@@ -91,34 +109,176 @@ TODO
=== "Transformers"
- ```python
+ ```{ .py hl_lines="9 23" }
import torch
- from PIL import Image
+ import supervision as sv
from transformers import DetrImageProcessor, DetrForObjectDetection
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+ frames_generator = sv.get_video_frames_generator()
- image = Image.open()
- inputs = processor(images=image, return_tensors="pt")
+ with sv.CSVSink() as sink:
+ for frame in frames_generator:
- with torch.no_grad():
- outputs = model(**inputs)
+ frame = sv.cv2_to_pillow(frame)
+ inputs = processor(images=frame, return_tensors="pt")
- width, height = image.size
- target_size = torch.tensor([[height, width]])
- results = processor.post_process_object_detection(
- outputs=outputs, target_sizes=target_size)[0]
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = frame.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ detections = sv.Detections.from_transformers(results)
+ sink.append(detections, {})
```
+TODO
+
+| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name |
+|---------|----------|---------|----------|----------|------------|------------|------------|
+| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car |
+| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck |
+| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car |
+
## Custom Fields
TODO
+=== "Inference"
+
+ ```{ .py hl_lines="8 12" }
+ import supervision as sv
+ from inference import get_model
+
+ model = get_model(model_id="yolov8n-640")
+ frames_generator = sv.get_video_frames_generator()
+
+ with sv.CSVSink() as sink:
+ for frame_index, frame in enumerate(frames_generator):
+
+ results = model.infer(image)[0]
+ detections = sv.Detections.from_inference(results)
+ sink.append(detections, {"frame_index": frame_index})
+ ```
+
+=== "Ultralytics"
+
+ ```{ .py hl_lines="8 12" }
+ import supervision as sv
+ from ultralytics import YOLO
+
+ model = YOLO("yolov8n.pt")
+ frames_generator = sv.get_video_frames_generator()
+
+ with sv.CSVSink() as sink:
+ for frame_index, frame in enumerate(frames_generator):
+
+ results = model(frame)[0]
+ detections = sv.Detections.from_ultralytics(results)
+ sink.append(detections, {"frame_index": frame_index})
+ ```
+
+=== "Transformers"
+
+ ```{ .py hl_lines="10 23" }
+ import torch
+ import supervision as sv
+ from transformers import DetrImageProcessor, DetrForObjectDetection
+
+ processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+ frames_generator = sv.get_video_frames_generator()
+
+ with sv.CSVSink() as sink:
+ for frame_index, frame in enumerate(frames_generator):
+
+ frame = sv.cv2_to_pillow(frame)
+ inputs = processor(images=frame, return_tensors="pt")
+
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = frame.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ detections = sv.Detections.from_transformers(results)
+ sink.append(detections, {"frame_index": frame_index})
+ ```
+
+TODO
+
+| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index |
+|---------|----------|---------|----------|----------|------------|------------|------------|-------------|
+| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 |
+| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 |
+| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 |
+
## Save Detections as JSON
TODO
-## Process Video and Save Detections
+=== "Inference"
-TODO
+ ```{ .py hl_lines="7" }
+ import supervision as sv
+ from inference import get_model
+
+ model = get_model(model_id="yolov8n-640")
+ frames_generator = sv.get_video_frames_generator()
+
+ with sv.JSONSink() as sink:
+ for frame_index, frame in enumerate(frames_generator):
+
+ results = model.infer(image)[0]
+ detections = sv.Detections.from_inference(results)
+ sink.append(detections, {"frame_index": frame_index})
+ ```
+
+=== "Ultralytics"
+
+ ```{ .py hl_lines="7" }
+ import supervision as sv
+ from ultralytics import YOLO
+
+ model = YOLO("yolov8n.pt")
+ frames_generator = sv.get_video_frames_generator()
+
+ with sv.JSONSink() as sink:
+ for frame_index, frame in enumerate(frames_generator):
+
+ results = model(frame)[0]
+ detections = sv.Detections.from_ultralytics(results)
+ sink.append(detections, {"frame_index": frame_index})
+ ```
+
+=== "Transformers"
+
+ ```{ .py hl_lines="9" }
+ import torch
+ import supervision as sv
+ from transformers import DetrImageProcessor, DetrForObjectDetection
+
+ processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+ frames_generator = sv.get_video_frames_generator()
+
+ with sv.JSONSink() as sink:
+ for frame_index, frame in enumerate(frames_generator):
+
+ frame = sv.cv2_to_pillow(frame)
+ inputs = processor(images=frame, return_tensors="pt")
+
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = frame.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ detections = sv.Detections.from_transformers(results)
+ sink.append(detections, {"frame_index": frame_index})
+ ```
\ No newline at end of file
From 99fbd4b601657e7a25ed68a2ae805413788cd1ad Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 20:45:06 +0000
Subject: [PATCH 020/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/how_to/save_detections.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md
index e0233f9b..5d0dc61a 100644
--- a/docs/how_to/save_detections.md
+++ b/docs/how_to/save_detections.md
@@ -9,8 +9,8 @@ TODO
## Run Detection
-First, you'll need to obtain predictions from your object detection or segmentation
-model. You can learn more on this topic in our
+First, you'll need to obtain predictions from your object detection or segmentation
+model. You can learn more on this topic in our
[How to Detect and Annotate](/latest/how_to/detect_and_annotate.md) guide.
=== "Inference"
@@ -281,4 +281,4 @@ TODO
outputs=outputs, target_sizes=target_size)[0]
detections = sv.Detections.from_transformers(results)
sink.append(detections, {"frame_index": frame_index})
- ```
\ No newline at end of file
+ ```
From e0d5f00ae3439bdece59cb1bd8d07ada53d91d78 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 23:15:49 +0200
Subject: [PATCH 021/136] initial version of `How to Save Detections` guide
done
---
docs/how_to/save_detections.md | 31 +++++++++++++++++++++++--------
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md
index e0233f9b..f432c939 100644
--- a/docs/how_to/save_detections.md
+++ b/docs/how_to/save_detections.md
@@ -5,7 +5,13 @@ status: new
# Save Detections
-TODO
+Supervision enables an easy way to save detections in .CSV and .JSON files for offline
+processing. This guide demonstrates how to perform video inference using the
+[Inference](https://github.com/roboflow/inference),
+[Ultralytics](https://github.com/ultralytics/ultralytics) or
+[Transformers](https://github.com/huggingface/transformers) packages and save their results with
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) and
+[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
## Run Detection
@@ -71,7 +77,11 @@ model. You can learn more on this topic in our
## Save Detections as CSV
-TODO
+To save detections to a `.CSV` file, open our
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
+and then pass the
+[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections)
+object resulting from the inference to it. Its fields are parsed and saved on disk.
=== "Inference"
@@ -135,8 +145,6 @@ TODO
sink.append(detections, {})
```
-TODO
-
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name |
|---------|----------|---------|----------|----------|------------|------------|------------|
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car |
@@ -145,7 +153,12 @@ TODO
## Custom Fields
-TODO
+Besides regular fields in
+[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections),
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
+also allows you to add custom information to each row, which can be passed via the
+`custom_data` dictionary. Let's utilize this feature to save information about the
+frame index from which the detections originate.
=== "Inference"
@@ -209,8 +222,6 @@ TODO
sink.append(detections, {"frame_index": frame_index})
```
-TODO
-
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index |
|---------|----------|---------|----------|----------|------------|------------|------------|-------------|
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 |
@@ -219,7 +230,11 @@ TODO
## Save Detections as JSON
-TODO
+If you prefer to save the result in a `.JSON` file instead of a `.CSV` file, all you
+need to do is replace
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
+with
+[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
=== "Inference"
From c27d9eaf22cb90efd8f88c91d555e9c32935ece2 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 9 Apr 2024 21:16:18 +0000
Subject: [PATCH 022/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/how_to/save_detections.md | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/docs/how_to/save_detections.md b/docs/how_to/save_detections.md
index 2c9250ea..94de6c61 100644
--- a/docs/how_to/save_detections.md
+++ b/docs/how_to/save_detections.md
@@ -5,12 +5,12 @@ status: new
# Save Detections
-Supervision enables an easy way to save detections in .CSV and .JSON files for offline
-processing. This guide demonstrates how to perform video inference using the
+Supervision enables an easy way to save detections in .CSV and .JSON files for offline
+processing. This guide demonstrates how to perform video inference using the
[Inference](https://github.com/roboflow/inference),
[Ultralytics](https://github.com/ultralytics/ultralytics) or
-[Transformers](https://github.com/huggingface/transformers) packages and save their results with
-[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) and
+[Transformers](https://github.com/huggingface/transformers) packages and save their results with
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) and
[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
## Run Detection
@@ -77,10 +77,10 @@ model. You can learn more on this topic in our
## Save Detections as CSV
-To save detections to a `.CSV` file, open our
-[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
-and then pass the
-[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections)
+To save detections to a `.CSV` file, open our
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
+and then pass the
+[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections)
object resulting from the inference to it. Its fields are parsed and saved on disk.
=== "Inference"
@@ -153,11 +153,11 @@ object resulting from the inference to it. Its fields are parsed and saved on di
## Custom Fields
-Besides regular fields in
-[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections),
-[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
-also allows you to add custom information to each row, which can be passed via the
-`custom_data` dictionary. Let's utilize this feature to save information about the
+Besides regular fields in
+[`sv.Detections`](/latest/detection/core/#supervision.detection.core.Detections),
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
+also allows you to add custom information to each row, which can be passed via the
+`custom_data` dictionary. Let's utilize this feature to save information about the
frame index from which the detections originate.
=== "Inference"
@@ -230,10 +230,10 @@ frame index from which the detections originate.
## Save Detections as JSON
-If you prefer to save the result in a `.JSON` file instead of a `.CSV` file, all you
-need to do is replace
-[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
-with
+If you prefer to save the result in a `.JSON` file instead of a `.CSV` file, all you
+need to do is replace
+[`sv.CSVSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink)
+with
[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
=== "Inference"
From eb1c7716ef0e9835f873133dd41e3ae40c9442df Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 23:18:02 +0200
Subject: [PATCH 023/136] make ruff happy
---
supervision/utils/image.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/utils/image.py b/supervision/utils/image.py
index 44ccbeac..9f2e1783 100644
--- a/supervision/utils/image.py
+++ b/supervision/utils/image.py
@@ -320,7 +320,7 @@ def overlay_image(
```
{ align=center width="800" }
- """
+ """ # noqa E501 // docs
scene_height, scene_width = image.shape[:2]
image_height, image_width = overlay.shape[:2]
anchor_x, anchor_y = anchor
From 2fd54521815f0267f746e14782c59916180e8b95 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Apr 2024 00:14:47 +0000
Subject: [PATCH 024/136] :arrow_up: Bump mkdocs-jupyter from 0.24.3 to 0.24.7
Bumps [mkdocs-jupyter](https://github.com/danielfrg/mkdocs-jupyter) from 0.24.3 to 0.24.7.
- [Changelog](https://github.com/danielfrg/mkdocs-jupyter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/danielfrg/mkdocs-jupyter/compare/0.24.3...0.24.7)
---
updated-dependencies:
- dependency-name: mkdocs-jupyter
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 65a9122f..40e19b9c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2162,13 +2162,12 @@ pytz = "*"
[[package]]
name = "mkdocs-jupyter"
-version = "0.24.3"
+version = "0.24.7"
description = "Use Jupyter in mkdocs websites"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "mkdocs_jupyter-0.24.3-py3-none-any.whl", hash = "sha256:904262a8678a5e5920b7c3c03b5010b36301a69d0a38f2fcbf430493adf6879e"},
- {file = "mkdocs_jupyter-0.24.3.tar.gz", hash = "sha256:3d81da9aea27480e93bab22438910c4f0b9630613e74f85b576590d78e0e8b14"},
+ {file = "mkdocs_jupyter-0.24.7-py3-none-any.whl", hash = "sha256:893d04bea1e007479a46e4e72852cd4d280c4d358ce4a0445250f3f80c639723"},
]
[package.dependencies]
@@ -2179,9 +2178,6 @@ mkdocs-material = ">9.0.0"
nbconvert = ">=7.2.9,<8"
pygments = ">2.12.0"
-[package.extras]
-test = ["coverage[toml]", "pymdown-extensions", "pytest", "pytest-cov"]
-
[[package]]
name = "mkdocs-material"
version = "9.5.17"
From 98b64f5e7d23386a78017522fb35ea10207570cf Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 10 Apr 2024 09:34:44 +0300
Subject: [PATCH 025/136] Add beach drone footage asset
---
supervision/assets/list.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/supervision/assets/list.py b/supervision/assets/list.py
index 83518cdb..77b1e461 100644
--- a/supervision/assets/list.py
+++ b/supervision/assets/list.py
@@ -18,6 +18,7 @@ class VideoAssets(Enum):
| `SUBWAY` | `subway.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/subway.mp4) |
| `MARKET_SQUARE` | `market-square.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/market-square.mp4) |
| `PEOPLE_WALKING` | `people-walking.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/people-walking.mp4) |
+ | `BEACH` | `beach-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/beach-1.mp4) |
""" # noqa: E501 // docs
VEHICLES = "vehicles.mp4"
@@ -27,6 +28,7 @@ class VideoAssets(Enum):
SUBWAY = "subway.mp4"
MARKET_SQUARE = "market-square.mp4"
PEOPLE_WALKING = "people-walking.mp4"
+ BEACH = "beach-1.mp4"
@classmethod
def list(cls):
@@ -62,4 +64,8 @@ VIDEO_ASSETS: Dict[str, Tuple[str, str]] = {
f"{BASE_VIDEO_URL}{VideoAssets.PEOPLE_WALKING.value}",
"0574c053c8686c3f1dc0aa3743e45cb9",
),
+ VideoAssets.BEACH.value: (
+ f"{BASE_VIDEO_URL}{VideoAssets.BEACH.value}",
+ "4175d42fec4d450ed081523fd39e0cf8",
+ ),
}
From eaff97f9027fc9b1b67c92156d29d36407b80128 Mon Sep 17 00:00:00 2001
From: Raif Olson
Date: Wed, 10 Apr 2024 17:33:16 -0400
Subject: [PATCH 026/136] add external and internal track_ids to keep valid
track ids sequential
---
supervision/tracker/byte_tracker/core.py | 46 +++++++++++++++---------
1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index 6475bf50..bf82044a 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -12,6 +12,7 @@ from supervision.utils.internal import deprecated_parameter
class STrack(BaseTrack):
shared_kalman = KalmanFilter()
+ _external_count = 0
def __init__(self, tlwh, score, class_ids, minimum_consecutive_frames):
# wait activate
@@ -24,6 +25,8 @@ class STrack(BaseTrack):
self.class_ids = class_ids
self.tracklet_len = 0
+ self.external_track_id = -1
+
self.minimum_consecutive_frames = minimum_consecutive_frames
def predict(self):
@@ -55,7 +58,7 @@ class STrack(BaseTrack):
def activate(self, kalman_filter, frame_id):
"""Start a new tracklet"""
self.kalman_filter = kalman_filter
- self.track_id = self.next_id()
+ self.internal_track_id = self.next_id()
self.mean, self.covariance = self.kalman_filter.initiate(
self.tlwh_to_xyah(self._tlwh)
)
@@ -76,7 +79,7 @@ class STrack(BaseTrack):
self.frame_id = frame_id
if new_id:
- self.track_id = self.next_id()
+ self.internal_track_id = self.next_id()
self.score = new_track.score
def update(self, new_track, frame_id):
@@ -95,8 +98,9 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
- if self.tracklet_len >= self.minimum_consecutive_frames:
+ if self.tracklet_len == self.minimum_consecutive_frames:
self.is_activated = True
+ self.external_track_id = self.next_external_id()
self.score = new_track.score
@@ -133,6 +137,15 @@ class STrack(BaseTrack):
def to_xyah(self):
return self.tlwh_to_xyah(self.tlwh)
+
+ @staticmethod
+ def next_external_id():
+ STrack._external_count += 1
+ return STrack._external_count
+
+ @staticmethod
+ def reset_external_counter():
+ STrack._external_count = 0
@staticmethod
def tlbr_to_tlwh(tlbr):
@@ -147,7 +160,7 @@ class STrack(BaseTrack):
return ret
def __repr__(self):
- return "OT_{}_({}-{})".format(self.track_id, self.start_frame, self.end_frame)
+ return "OT_{}_({}-{})".format(self.internal_track_id, self.start_frame, self.end_frame)
def detections2boxes(detections: Detections) -> np.ndarray:
@@ -294,7 +307,7 @@ class ByteTrack:
matches, _, _ = matching.linear_assignment(iou_costs, 0.5)
detections.tracker_id = np.full(len(detections), -1, dtype=int)
for i_detection, i_track in matches:
- detections.tracker_id[i_detection] = int(tracks[i_track].track_id)
+ detections.tracker_id[i_detection] = int(tracks[i_track].external_track_id)
return detections[detections.tracker_id != -1]
@@ -318,6 +331,7 @@ class ByteTrack:
self.lost_tracks: List[STrack] = []
self.removed_tracks: List[STrack] = []
BaseTrack.reset_counter()
+ STrack.reset_external_counter()
def update_with_tensors(self, tensors: np.ndarray) -> List[STrack]:
"""
@@ -478,22 +492,22 @@ def joint_tracks(
) -> List[STrack]:
"""
Joins two lists of tracks, ensuring that the resulting list does not
- contain tracks with duplicate track_id values.
+ contain tracks with duplicate internal_track_id values.
Parameters:
- track_list_a: First list of tracks (with track_id attribute).
- track_list_b: Second list of tracks (with track_id attribute).
+ track_list_a: First list of tracks (with internal_track_id attribute).
+ track_list_b: Second list of tracks (with internal_track_id attribute).
Returns:
Combined list of tracks from track_list_a and track_list_b
- without duplicate track_id values.
+ without duplicate internal_track_id values.
"""
seen_track_ids = set()
result = []
for track in track_list_a + track_list_b:
- if track.track_id not in seen_track_ids:
- seen_track_ids.add(track.track_id)
+ if track.internal_track_id not in seen_track_ids:
+ seen_track_ids.add(track.internal_track_id)
result.append(track)
return result
@@ -502,17 +516,17 @@ def joint_tracks(
def sub_tracks(track_list_a: List, track_list_b: List) -> List[int]:
"""
Returns a list of tracks from track_list_a after removing any tracks
- that share the same track_id with tracks in track_list_b.
+ that share the same internal_track_id with tracks in track_list_b.
Parameters:
- track_list_a: List of tracks (with track_id attribute).
- track_list_b: List of tracks (with track_id attribute) to
+ track_list_a: List of tracks (with internal_track_id attribute).
+ track_list_b: List of tracks (with internal_track_id attribute) to
be subtracted from track_list_a.
Returns:
List of remaining tracks from track_list_a after subtraction.
"""
- tracks = {track.track_id: track for track in track_list_a}
- track_ids_b = {track.track_id for track in track_list_b}
+ tracks = {track.internal_track_id: track for track in track_list_a}
+ track_ids_b = {track.internal_track_id for track in track_list_b}
for track_id in track_ids_b:
tracks.pop(track_id, None)
From a2ab113f214d92e785bc5ddba5d0f64e669fe39f Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 10 Apr 2024 21:37:41 +0000
Subject: [PATCH 027/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/tracker/byte_tracker/core.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index bf82044a..8e8104f4 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -137,7 +137,7 @@ class STrack(BaseTrack):
def to_xyah(self):
return self.tlwh_to_xyah(self.tlwh)
-
+
@staticmethod
def next_external_id():
STrack._external_count += 1
@@ -160,7 +160,9 @@ class STrack(BaseTrack):
return ret
def __repr__(self):
- return "OT_{}_({}-{})".format(self.internal_track_id, self.start_frame, self.end_frame)
+ return "OT_{}_({}-{})".format(
+ self.internal_track_id, self.start_frame, self.end_frame
+ )
def detections2boxes(detections: Detections) -> np.ndarray:
@@ -307,7 +309,9 @@ class ByteTrack:
matches, _, _ = matching.linear_assignment(iou_costs, 0.5)
detections.tracker_id = np.full(len(detections), -1, dtype=int)
for i_detection, i_track in matches:
- detections.tracker_id[i_detection] = int(tracks[i_track].external_track_id)
+ detections.tracker_id[i_detection] = int(
+ tracks[i_track].external_track_id
+ )
return detections[detections.tracker_id != -1]
From 2c9a1f69c5ad1f460c2c5a413d802b2cef967d64 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 10 Apr 2024 21:54:23 +0000
Subject: [PATCH 028/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/tracker/byte_tracker/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index 8e8104f4..1fa98864 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -142,7 +142,7 @@ class STrack(BaseTrack):
def next_external_id():
STrack._external_count += 1
return STrack._external_count
-
+
@staticmethod
def reset_external_counter():
STrack._external_count = 0
From ed225187b248dad4dbcebc6385508a561d70a629 Mon Sep 17 00:00:00 2001
From: Piotr Skalski
Date: Thu, 11 Apr 2024 14:09:02 +0200
Subject: [PATCH 029/136] Update README.md
---
README.md | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index 7d280b57..beed655c 100644
--- a/README.md
+++ b/README.md
@@ -217,19 +217,23 @@ len(dataset)
## 🎬 tutorials
-
-
-Speed Estimation & Vehicle Tracking | Computer Vision | Open Source
-
Created: 11 Jan 2024 | Updated: 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.
+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](https://github.com/roboflow/supervision/tree/develop/examples), and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/)!
-
-Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking
-
Created: 6 Sep 2023 | Updated: 6 Sep 2023
-
In this video, we explore real-time traffic analysis using YOLOv8 and ByteTrack to detect and track vehicles on aerial images. Harnessing the power of Python and Supervision, we delve deep into assigning cars to specific entry zones and understanding their direction of movement. By visualizing their paths, we gain insights into traffic flow across bustling roundabouts...
+
+Dwell Time Analysis with Computer Vision | Real-Time Stream Processing
+Created: 5 Apr 2024
+
Learn how to use computer vision to analyze wait times and optimize processes. This tutorial covers object detection, tracking, and calculating time spent in designated zones. Use these techniques to improve customer experience in retail, traffic management, or other scenarios.
+
+
+
+
+
+Speed Estimation & Vehicle Tracking | Computer Vision | Open Source
+
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
From 6166d924588b8ca3fe6cf821f02a098a5fc34f67 Mon Sep 17 00:00:00 2001
From: Raif Olson
Date: Thu, 11 Apr 2024 08:21:26 -0400
Subject: [PATCH 030/136] fix reassigning track_id after a track is
re-activated.
---
supervision/tracker/byte_tracker/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index 1fa98864..f35e62df 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -98,7 +98,7 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
- if self.tracklet_len == self.minimum_consecutive_frames:
+ if self.tracklet_len == self.minimum_consecutive_frames and not self.is_activated:
self.is_activated = True
self.external_track_id = self.next_external_id()
From 755f292ed1ef87f593cf2f057b9a95e7e84a2b59 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Thu, 11 Apr 2024 12:21:44 +0000
Subject: [PATCH 031/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/tracker/byte_tracker/core.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index f35e62df..10b7f0fe 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -98,7 +98,10 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
- if self.tracklet_len == self.minimum_consecutive_frames and not self.is_activated:
+ if (
+ self.tracklet_len == self.minimum_consecutive_frames
+ and not self.is_activated
+ ):
self.is_activated = True
self.external_track_id = self.next_external_id()
From 7924a13dc59dd26f7cd2cc9e83c098fd4f453da5 Mon Sep 17 00:00:00 2001
From: Raif Olson
Date: Thu, 11 Apr 2024 08:28:08 -0400
Subject: [PATCH 032/136] only assign external track_id once
---
supervision/tracker/byte_tracker/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index 10b7f0fe..6c320073 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -100,7 +100,7 @@ class STrack(BaseTrack):
self.state = TrackState.Tracked
if (
self.tracklet_len == self.minimum_consecutive_frames
- and not self.is_activated
+ and self.external_track_id == -1
):
self.is_activated = True
self.external_track_id = self.next_external_id()
From 263421d145d2d5836714365b59e537e75f21ee30 Mon Sep 17 00:00:00 2001
From: Piotr Skalski
Date: Thu, 11 Apr 2024 14:40:43 +0200
Subject: [PATCH 033/136] Update README.md
---
examples/README.md | 26 +++-----------------------
1 file changed, 3 insertions(+), 23 deletions(-)
diff --git a/examples/README.md b/examples/README.md
index 81c8054b..b723a247 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,8 +1,8 @@
# Examples
-This repository is packed with real-world use-cases, provided through Python scripts or
-interactive notebooks. Browse through to understand how the Supervision library
-interfaces with diverse applications.
+Here, you'll find end-to-end examples that show how to solve common computer vision problems using Supervision.
+
+For more information and examples, visit our [documentation](https://supervision.roboflow.com/develop/annotators/) and explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/) and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/). Join our [Discord](https://discord.com/invite/GbfgXGJ8Bk) and meet other Supervision power users!
- [tracking](./tracking) by [@SkalskiP](https://github.com/SkalskiP)
- [count people in zone](./count_people_in_zone) by [@SkalskiP](https://github.com/SkalskiP)
@@ -10,23 +10,3 @@ interfaces with diverse applications.
- [speed estimation](./speed_estimation) by [@SkalskiP](https://github.com/SkalskiP)
- [time in zone](./time_in_zone) by [@SkalskiP](https://github.com/SkalskiP)
- [heatmap and track](./heatmap_and_track/) by [@HinePo](https://github.com/HinePo)
-
-## Contributing
-
-We welcome contributions from the community in the form of examples, applications, and
-guides. To contribute, please follow these steps:
-
-1. Create a pull request (PR) with the `[Example]` prefix in the title, adding your
-project folder to the `examples/` directory in the repository.
-2. Confirm your project aligns with the following standards:
- - Incorporates the `supervision` package.
- - Provides a `README.md` file, detailing the instructions to execute the project.
- - Showcases visual results, demonstrating the app's functionality.
- - Avoids adding large assets or dependencies unless absolutely necessary.
- - The contributor is expected to provide support for issues related to their
-examples.
- - In case the presented model has licensing complications, kindly specify them to
-circumvent potential misunderstandings.
-
-For inquiries or concerns about these prerequisites, feel free to raise a PR. We are
-committed to assist and guide you.
From 9284bfdcd900f4ebe4b30f375c0f1d4de2ef2b41 Mon Sep 17 00:00:00 2001
From: Piotr Skalski
Date: Thu, 11 Apr 2024 14:44:50 +0200
Subject: [PATCH 034/136] Update README.md
---
examples/count_people_in_zone/README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/examples/count_people_in_zone/README.md b/examples/count_people_in_zone/README.md
index 2e14a80b..d9d3cdb2 100644
--- a/examples/count_people_in_zone/README.md
+++ b/examples/count_people_in_zone/README.md
@@ -1,5 +1,8 @@
# count people in zone
+[](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-detect-and-count-objects-in-polygon-zone.ipynb)
+[](https://www.youtube.com/watch?v=l_kf9CfZ_8M)
+
## 👋 hello
This demo is a video analysis tool that counts and highlights objects in specific zones
From d3dbe7c59780307dec01ebb302f0f5762eaa3241 Mon Sep 17 00:00:00 2001
From: Piotr Skalski
Date: Thu, 11 Apr 2024 14:54:22 +0200
Subject: [PATCH 035/136] Update README.md
---
examples/time_in_zone/README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/examples/time_in_zone/README.md b/examples/time_in_zone/README.md
index 2bfd6c7a..98587999 100644
--- a/examples/time_in_zone/README.md
+++ b/examples/time_in_zone/README.md
@@ -1,5 +1,7 @@
# time in zone
+[](https://www.youtube.com/watch?v=hAWpsIuem10)
+
## 👋 hello
Practical demonstration on leveraging computer vision for analyzing wait times and
From b1d5d4faf07f28d3f99fcb36dc2e65eb65167776 Mon Sep 17 00:00:00 2001
From: Raif Olson
Date: Thu, 11 Apr 2024 09:21:32 -0400
Subject: [PATCH 036/136] fix to exactly match existing release version when
minimum_consecutive_frames = 1
---
supervision/tracker/byte_tracker/core.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index 6c320073..d7dc2461 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -65,8 +65,12 @@ class STrack(BaseTrack):
self.tracklet_len = 0
self.state = TrackState.Tracked
- if frame_id == 1 and self.minimum_consecutive_frames == 1:
+ if frame_id == 1:
self.is_activated = True
+
+ if self.minimum_consecutive_frames == 1:
+ self.external_track_id = self.next_external_id()
+
self.frame_id = frame_id
self.start_frame = frame_id
From bcb31f880958af32e2b7aa788f1afe37389458bf Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Thu, 11 Apr 2024 13:21:48 +0000
Subject: [PATCH 037/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/tracker/byte_tracker/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index d7dc2461..410c1050 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -70,7 +70,7 @@ class STrack(BaseTrack):
if self.minimum_consecutive_frames == 1:
self.external_track_id = self.next_external_id()
-
+
self.frame_id = frame_id
self.start_frame = frame_id
From f98ded49a226557bc474cbc46253689023f9664b Mon Sep 17 00:00:00 2001
From: Raif Olson
Date: Thu, 11 Apr 2024 09:27:24 -0400
Subject: [PATCH 038/136] fix
---
supervision/tracker/byte_tracker/core.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index d7dc2461..8b072fa2 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -102,12 +102,10 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
- if (
- self.tracklet_len == self.minimum_consecutive_frames
- and self.external_track_id == -1
- ):
+ if (self.tracklet_len == self.minimum_consecutive_frames):
self.is_activated = True
- self.external_track_id = self.next_external_id()
+ if (self.external_track_id == -1):
+ self.external_track_id = self.next_external_id()
self.score = new_track.score
From b7607c6f152d73ee5556c67bdb6ff94b54d29847 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Thu, 11 Apr 2024 18:57:30 +0530
Subject: [PATCH 039/136] Updated PolyZone class to compute frame_resolution_wh
based on polygon coordinates
---
supervision/detection/tools/polygon_zone.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index 00b746aa..f4825125 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -2,6 +2,7 @@ from dataclasses import replace
from typing import Iterable, Optional, Tuple
import cv2
+import warnings
import numpy as np
import numpy.typing as npt
@@ -11,7 +12,7 @@ from supervision.draw.color import Color
from supervision.draw.utils import draw_polygon, draw_text
from supervision.geometry.core import Position
from supervision.geometry.utils import get_polygon_center
-from supervision.utils.internal import deprecated_parameter
+from supervision.utils.internal import deprecated_parameter, SupervisionWarnings
class PolygonZone:
@@ -41,18 +42,24 @@ class PolygonZone:
def __init__(
self,
polygon: npt.NDArray[np.int64],
- frame_resolution_wh: Tuple[int, int],
triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),
+ frame_resolution_wh: Optional[Tuple[int, int]] = None,
):
+ if frame_resolution_wh is not None:
+ warnings.warn(
+ "The `frame_resolution_wh` parameter is no longer required and will be dropped in version supervision-0.24.0. The mask resolution is now calculated automatically based on the polygon coordinates.",
+ category=SupervisionWarnings,
+ )
+
self.polygon = polygon.astype(int)
- self.frame_resolution_wh = frame_resolution_wh
self.triggering_anchors = triggering_anchors
self.current_count = 0
- width, height = frame_resolution_wh
+ x_max, y_max = np.max(polygon, axis=0)
+ self.frame_resolution_wh = (x_max , y_max)
self.mask = polygon_to_mask(
- polygon=polygon, resolution_wh=(width + 1, height + 1)
+ polygon=polygon, resolution_wh=(x_max + 1, y_max + 1)
)
def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]:
From 361e444f53f2281e2b0297f5b267f66136292d5f Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Thu, 11 Apr 2024 13:27:40 +0000
Subject: [PATCH 040/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/tracker/byte_tracker/core.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/supervision/tracker/byte_tracker/core.py b/supervision/tracker/byte_tracker/core.py
index cfaae130..55db6293 100644
--- a/supervision/tracker/byte_tracker/core.py
+++ b/supervision/tracker/byte_tracker/core.py
@@ -102,9 +102,9 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
- if (self.tracklet_len == self.minimum_consecutive_frames):
+ if self.tracklet_len == self.minimum_consecutive_frames:
self.is_activated = True
- if (self.external_track_id == -1):
+ if self.external_track_id == -1:
self.external_track_id = self.next_external_id()
self.score = new_track.score
From 28baf18cb6dcd6005fa030e35390028adfe350e8 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Thu, 11 Apr 2024 18:59:48 +0530
Subject: [PATCH 041/136] updated PolygonZone Attributes docs
---
supervision/detection/tools/polygon_zone.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index f4825125..72306e7e 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -22,13 +22,16 @@ class PolygonZone:
Attributes:
polygon (np.ndarray): A polygon represented by a numpy array of shape
`(N, 2)`, containing the `x`, `y` coordinates of the points.
- frame_resolution_wh (Tuple[int, int]): The frame resolution (width, height)
triggering_anchors (Iterable[sv.Position]): A list of positions specifying
which anchors of the detections bounding box to consider when deciding on
whether the detection fits within the PolygonZone
(default: (sv.Position.BOTTOM_CENTER,)).
current_count (int): The current count of detected objects within the zone
mask (np.ndarray): The 2D bool mask for the polygon zone
+ frame_resolution_wh (Optional[Tuple[int, int]], optional): DEPRECATED. The frame resolution
+ (width, height). This parameter is no longer required and will be dropped in version
+ supervision-0.24.0. The mask resolution is now calculated automatically based on the
+ polygon coordinates. Defaults to None.
"""
@deprecated_parameter(
From b64c61c3885fdd449e0dfae9080c1c6073c75aed Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Thu, 11 Apr 2024 19:04:06 +0530
Subject: [PATCH 042/136] formated code
---
supervision/detection/tools/polygon_zone.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index 72306e7e..ccc6ec95 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -50,9 +50,10 @@ class PolygonZone:
):
if frame_resolution_wh is not None:
warnings.warn(
- "The `frame_resolution_wh` parameter is no longer required and will be dropped in version supervision-0.24.0. The mask resolution is now calculated automatically based on the polygon coordinates.",
- category=SupervisionWarnings,
- )
+ "The `frame_resolution_wh` parameter is no longer required and will be dropped in version "
+ "supervision-0.24.0. The mask resolution is now calculated automatically based on the polygon coordinates.",
+ category=SupervisionWarnings,
+ )
self.polygon = polygon.astype(int)
self.triggering_anchors = triggering_anchors
@@ -60,7 +61,7 @@ class PolygonZone:
self.current_count = 0
x_max, y_max = np.max(polygon, axis=0)
- self.frame_resolution_wh = (x_max , y_max)
+ self.frame_resolution_wh = (x_max, y_max)
self.mask = polygon_to_mask(
polygon=polygon, resolution_wh=(x_max + 1, y_max + 1)
)
@@ -100,6 +101,7 @@ class PolygonZone:
return is_in_zone.astype(bool)
+
class PolygonZoneAnnotator:
"""
A class for annotating a polygon-shaped zone within a
From 743e42080bcc4ba60ff7ffda03f8c50e52a6edfc Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Thu, 11 Apr 2024 13:57:48 +0000
Subject: [PATCH 043/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/detection/tools/polygon_zone.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index ccc6ec95..f78bd609 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -1,8 +1,8 @@
+import warnings
from dataclasses import replace
from typing import Iterable, Optional, Tuple
import cv2
-import warnings
import numpy as np
import numpy.typing as npt
@@ -12,7 +12,7 @@ from supervision.draw.color import Color
from supervision.draw.utils import draw_polygon, draw_text
from supervision.geometry.core import Position
from supervision.geometry.utils import get_polygon_center
-from supervision.utils.internal import deprecated_parameter, SupervisionWarnings
+from supervision.utils.internal import SupervisionWarnings, deprecated_parameter
class PolygonZone:
@@ -101,7 +101,6 @@ class PolygonZone:
return is_in_zone.astype(bool)
-
class PolygonZoneAnnotator:
"""
A class for annotating a polygon-shaped zone within a
From 7126adc74a38d080c430af76643af249d5a624b9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 12 Apr 2024 00:13:05 +0000
Subject: [PATCH 044/136] :arrow_up: Bump ruff from 0.3.5 to 0.3.6
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.5 to 0.3.6.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.3.5...v0.3.6)
---
updated-dependencies:
- dependency-name: ruff
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 40e19b9c..3bd87ec3 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3643,28 +3643,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.3.5"
+version = "0.3.6"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.3.5-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:aef5bd3b89e657007e1be6b16553c8813b221ff6d92c7526b7e0227450981eac"},
- {file = "ruff-0.3.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:89b1e92b3bd9fca249153a97d23f29bed3992cff414b222fcd361d763fc53f12"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e55771559c89272c3ebab23326dc23e7f813e492052391fe7950c1a5a139d89"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dabc62195bf54b8a7876add6e789caae0268f34582333cda340497c886111c39"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a05f3793ba25f194f395578579c546ca5d83e0195f992edc32e5907d142bfa3"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dfd3504e881082959b4160ab02f7a205f0fadc0a9619cc481982b6837b2fd4c0"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87258e0d4b04046cf1d6cc1c56fadbf7a880cc3de1f7294938e923234cf9e498"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:712e71283fc7d9f95047ed5f793bc019b0b0a29849b14664a60fd66c23b96da1"},
- {file = "ruff-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a532a90b4a18d3f722c124c513ffb5e5eaff0cc4f6d3aa4bda38e691b8600c9f"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:122de171a147c76ada00f76df533b54676f6e321e61bd8656ae54be326c10296"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d80a6b18a6c3b6ed25b71b05eba183f37d9bc8b16ace9e3d700997f00b74660b"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a7b6e63194c68bca8e71f81de30cfa6f58ff70393cf45aab4c20f158227d5936"},
- {file = "ruff-0.3.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a759d33a20c72f2dfa54dae6e85e1225b8e302e8ac655773aff22e542a300985"},
- {file = "ruff-0.3.5-py3-none-win32.whl", hash = "sha256:9d8605aa990045517c911726d21293ef4baa64f87265896e491a05461cae078d"},
- {file = "ruff-0.3.5-py3-none-win_amd64.whl", hash = "sha256:dc56bb16a63c1303bd47563c60482a1512721053d93231cf7e9e1c6954395a0e"},
- {file = "ruff-0.3.5-py3-none-win_arm64.whl", hash = "sha256:faeeae9905446b975dcf6d4499dc93439b131f1443ee264055c5716dd947af55"},
- {file = "ruff-0.3.5.tar.gz", hash = "sha256:a067daaeb1dc2baf9b82a32dae67d154d95212080c80435eb052d95da647763d"},
+ {file = "ruff-0.3.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:732ef99984275534f9466fbc01121523caf72aa8c2bdeb36fd2edf2bc294a992"},
+ {file = "ruff-0.3.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:93699d61116807edc5ca1cdf9d2d22cf8d93335d59e3ff0ca7aee62c1818a736"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc4006cbc6c11fefc25f122d2eb4731d7a3d815dc74d67c54991cc3f99c90177"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:878ef1a55ce931f3ca23b690b159cd0659f495a4c231a847b00ca55e4c688baf"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb87788284af96725643eae9ab3ac746d8cc09aad140268523b019f7ac3cd98"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b2e79f8e1b6bd5411d7ddad3f2abff3f9d371beda29daef86400d416dedb7e02"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf48ec2c4bfae7837dc325c431a2932dc23a1485e71c59591c1df471ba234e0e"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c466a52c522e6a08df0af018f550902f154f5649ad09e7f0d43da766e7399ebc"},
+ {file = "ruff-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28ccf3fb6d1162a73cd286c63a5e4d885f46a1f99f0b392924bc95ccbd18ea8f"},
+ {file = "ruff-0.3.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b11e09439d9df6cc12d9f622065834654417c40216d271f639512d80e80e3e53"},
+ {file = "ruff-0.3.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:647f1fb5128a3e24ce68878b8050bb55044c45bb3f3ae4710d4da9ca96ede5cb"},
+ {file = "ruff-0.3.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2b0c4c70578ef1871a9ac5c85ed7a8c33470e976c73ba9211a111d2771b5f787"},
+ {file = "ruff-0.3.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e3da499ded004d0b956ab04248b2ae17e54a67ffc81353514ac583af5959a255"},
+ {file = "ruff-0.3.6-py3-none-win32.whl", hash = "sha256:4056480f5cf38ad278667c31b0ef334c29acdfcea617cb89c4ccbc7d96f1637f"},
+ {file = "ruff-0.3.6-py3-none-win_amd64.whl", hash = "sha256:f1aa621beed533f46e9c7d6fe00e7f6e4570155b61d8f020387b72ace2b42e04"},
+ {file = "ruff-0.3.6-py3-none-win_arm64.whl", hash = "sha256:7c8a2a0e0cab077a07465259ffe3b3c090e747ca8097c5dc4c36ca0fdaaac90d"},
+ {file = "ruff-0.3.6.tar.gz", hash = "sha256:26071fb530038602b984e3bbe1443ef82a38450c4dcb1344a9caf67234ff9756"},
]
[[package]]
From 89d84d1f7437bb9561ef2527fea5dac4770e2d53 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 12 Apr 2024 04:14:49 +0000
Subject: [PATCH 045/136] :arrow_up: Bump idna from 3.6 to 3.7
Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7)
---
updated-dependencies:
- dependency-name: idna
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 40e19b9c..932c51bc 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1122,13 +1122,13 @@ license = ["ukkonen"]
[[package]]
name = "idna"
-version = "3.6"
+version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
- {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
- {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
From 510df5457255aa70cdb8a8daa24aedaa624d9dd9 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Fri, 12 Apr 2024 16:16:48 +0530
Subject: [PATCH 046/136] Updated order of parameters in PolygonZone __init__()
---
supervision/detection/tools/polygon_zone.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index ccc6ec95..fa71b124 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -1,8 +1,8 @@
+import warnings
from dataclasses import replace
from typing import Iterable, Optional, Tuple
import cv2
-import warnings
import numpy as np
import numpy.typing as npt
@@ -12,7 +12,7 @@ from supervision.draw.color import Color
from supervision.draw.utils import draw_polygon, draw_text
from supervision.geometry.core import Position
from supervision.geometry.utils import get_polygon_center
-from supervision.utils.internal import deprecated_parameter, SupervisionWarnings
+from supervision.utils.internal import SupervisionWarnings, deprecated_parameter
class PolygonZone:
@@ -45,8 +45,8 @@ class PolygonZone:
def __init__(
self,
polygon: npt.NDArray[np.int64],
- triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),
frame_resolution_wh: Optional[Tuple[int, int]] = None,
+ triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),
):
if frame_resolution_wh is not None:
warnings.warn(
@@ -101,7 +101,6 @@ class PolygonZone:
return is_in_zone.astype(bool)
-
class PolygonZoneAnnotator:
"""
A class for annotating a polygon-shaped zone within a
From 0ad3796571e04b3435d1a31d4adec483a18412e0 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Fri, 12 Apr 2024 16:13:09 +0200
Subject: [PATCH 047/136] bug fix
---
supervision/detection/tools/polygon_zone.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index fa71b124..244b793b 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -61,9 +61,9 @@ class PolygonZone:
self.current_count = 0
x_max, y_max = np.max(polygon, axis=0)
- self.frame_resolution_wh = (x_max, y_max)
+ self.frame_resolution_wh = (x_max + 1, y_max + 1)
self.mask = polygon_to_mask(
- polygon=polygon, resolution_wh=(x_max + 1, y_max + 1)
+ polygon=polygon, resolution_wh=(x_max + 2, y_max + 2)
)
def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]:
From 2c62f0b26dfdac73ce941e425c2325ad87f2499b Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Fri, 12 Apr 2024 16:15:10 +0200
Subject: [PATCH 048/136] drop `frame_resolution_wh` from docs
---
supervision/detection/tools/polygon_zone.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index 244b793b..561cb9ce 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -28,10 +28,6 @@ class PolygonZone:
(default: (sv.Position.BOTTOM_CENTER,)).
current_count (int): The current count of detected objects within the zone
mask (np.ndarray): The 2D bool mask for the polygon zone
- frame_resolution_wh (Optional[Tuple[int, int]], optional): DEPRECATED. The frame resolution
- (width, height). This parameter is no longer required and will be dropped in version
- supervision-0.24.0. The mask resolution is now calculated automatically based on the
- polygon coordinates. Defaults to None.
"""
@deprecated_parameter(
From dfd9fc8e6d696eec671445136b8a50a5c2c901bd Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Fri, 12 Apr 2024 16:17:29 +0200
Subject: [PATCH 049/136] fix too long string
---
supervision/detection/tools/polygon_zone.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/supervision/detection/tools/polygon_zone.py b/supervision/detection/tools/polygon_zone.py
index 561cb9ce..a1997212 100644
--- a/supervision/detection/tools/polygon_zone.py
+++ b/supervision/detection/tools/polygon_zone.py
@@ -46,8 +46,9 @@ class PolygonZone:
):
if frame_resolution_wh is not None:
warnings.warn(
- "The `frame_resolution_wh` parameter is no longer required and will be dropped in version "
- "supervision-0.24.0. The mask resolution is now calculated automatically based on the polygon coordinates.",
+ "The `frame_resolution_wh` parameter is no longer required and will be "
+ "dropped in version supervision-0.24.0. The mask resolution is now "
+ "calculated automatically based on the polygon coordinates.",
category=SupervisionWarnings,
)
From 26c2a7ef9cb17332795501de2f2f867bccd8c8e8 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Sat, 13 Apr 2024 10:18:30 +0530
Subject: [PATCH 050/136] updated examples
---
examples/count_people_in_zone/inference_example.py | 2 +-
examples/count_people_in_zone/ultralytics_example.py | 2 +-
examples/speed_estimation/inference_example.py | 4 +---
examples/speed_estimation/ultralytics_example.py | 4 +---
examples/speed_estimation/yolo_nas_example.py | 4 +---
5 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/examples/count_people_in_zone/inference_example.py b/examples/count_people_in_zone/inference_example.py
index 6fd0f340..163f1771 100644
--- a/examples/count_people_in_zone/inference_example.py
+++ b/examples/count_people_in_zone/inference_example.py
@@ -46,7 +46,7 @@ def initiate_annotators(
box_annotators = []
for index, polygon in enumerate(polygons):
- zone = sv.PolygonZone(polygon=polygon, frame_resolution_wh=resolution_wh)
+ zone = sv.PolygonZone(polygon=polygon)
zone_annotator = sv.PolygonZoneAnnotator(
zone=zone,
color=COLORS.by_idx(index),
diff --git a/examples/count_people_in_zone/ultralytics_example.py b/examples/count_people_in_zone/ultralytics_example.py
index cfd37bcc..861db951 100644
--- a/examples/count_people_in_zone/ultralytics_example.py
+++ b/examples/count_people_in_zone/ultralytics_example.py
@@ -44,7 +44,7 @@ def initiate_annotators(
box_annotators = []
for index, polygon in enumerate(polygons):
- zone = sv.PolygonZone(polygon=polygon, frame_resolution_wh=resolution_wh)
+ zone = sv.PolygonZone(polygon=polygon)
zone_annotator = sv.PolygonZoneAnnotator(
zone=zone,
color=COLORS.by_idx(index),
diff --git a/examples/speed_estimation/inference_example.py b/examples/speed_estimation/inference_example.py
index b0ff84dd..a435261c 100644
--- a/examples/speed_estimation/inference_example.py
+++ b/examples/speed_estimation/inference_example.py
@@ -116,9 +116,7 @@ if __name__ == "__main__":
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
- polygon_zone = sv.PolygonZone(
- polygon=SOURCE, frame_resolution_wh=video_info.resolution_wh
- )
+ polygon_zone = sv.PolygonZone(polygon=SOURCE)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
diff --git a/examples/speed_estimation/ultralytics_example.py b/examples/speed_estimation/ultralytics_example.py
index 4b0436a0..3218d268 100644
--- a/examples/speed_estimation/ultralytics_example.py
+++ b/examples/speed_estimation/ultralytics_example.py
@@ -94,9 +94,7 @@ if __name__ == "__main__":
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
- polygon_zone = sv.PolygonZone(
- polygon=SOURCE, frame_resolution_wh=video_info.resolution_wh
- )
+ polygon_zone = sv.PolygonZone(polygon=SOURCE)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
diff --git a/examples/speed_estimation/yolo_nas_example.py b/examples/speed_estimation/yolo_nas_example.py
index 77f1fd0f..18798166 100644
--- a/examples/speed_estimation/yolo_nas_example.py
+++ b/examples/speed_estimation/yolo_nas_example.py
@@ -95,9 +95,7 @@ if __name__ == "__main__":
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
- polygon_zone = sv.PolygonZone(
- polygon=SOURCE, frame_resolution_wh=video_info.resolution_wh
- )
+ polygon_zone = sv.PolygonZone(polygon=SOURCE)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
From 15b1473c2f71c6ff9e4c318efe95b73c168b3dc0 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Sat, 13 Apr 2024 10:28:11 +0530
Subject: [PATCH 051/136] Updated examples to remove frame_resolution_wh from
PolygonZone
---
examples/time_in_zone/inference_file_example.py | 4 ----
examples/time_in_zone/inference_naive_stream_example.py | 4 ----
examples/time_in_zone/inference_stream_example.py | 2 --
examples/time_in_zone/ultralytics_file_example.py | 4 ----
examples/time_in_zone/ultralytics_naive_stream_example.py | 4 ----
examples/time_in_zone/ultralytics_stream_example.py | 2 --
examples/traffic_analysis/inference_example.py | 6 ++----
examples/traffic_analysis/ultralytics_example.py | 6 ++----
8 files changed, 4 insertions(+), 28 deletions(-)
diff --git a/examples/time_in_zone/inference_file_example.py b/examples/time_in_zone/inference_file_example.py
index 5feb1d83..f4955464 100644
--- a/examples/time_in_zone/inference_file_example.py
+++ b/examples/time_in_zone/inference_file_example.py
@@ -29,14 +29,10 @@ def main(
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
frames_generator = sv.get_video_frames_generator(source_video_path)
- frame = next(frames_generator)
- resolution_wh = frame.shape[1], frame.shape[0]
-
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
diff --git a/examples/time_in_zone/inference_naive_stream_example.py b/examples/time_in_zone/inference_naive_stream_example.py
index dd2d68a5..21880269 100644
--- a/examples/time_in_zone/inference_naive_stream_example.py
+++ b/examples/time_in_zone/inference_naive_stream_example.py
@@ -29,14 +29,10 @@ def main(
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
fps_monitor = sv.FPSMonitor()
- frame = next(frames_generator)
- resolution_wh = frame.shape[1], frame.shape[0]
-
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
diff --git a/examples/time_in_zone/inference_stream_example.py b/examples/time_in_zone/inference_stream_example.py
index e1fae57f..604aade4 100644
--- a/examples/time_in_zone/inference_stream_example.py
+++ b/examples/time_in_zone/inference_stream_example.py
@@ -28,11 +28,9 @@ class CustomSink:
def on_prediction(self, result: dict, frame: VideoFrame) -> None:
if self.zones is None:
- resolution_wh = frame.image.shape[1], frame.image.shape[0]
self.zones = [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in self.polygons
diff --git a/examples/time_in_zone/ultralytics_file_example.py b/examples/time_in_zone/ultralytics_file_example.py
index fe8ce58d..b470b7a3 100644
--- a/examples/time_in_zone/ultralytics_file_example.py
+++ b/examples/time_in_zone/ultralytics_file_example.py
@@ -30,14 +30,10 @@ def main(
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
frames_generator = sv.get_video_frames_generator(source_video_path)
- frame = next(frames_generator)
- resolution_wh = frame.shape[1], frame.shape[0]
-
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
diff --git a/examples/time_in_zone/ultralytics_naive_stream_example.py b/examples/time_in_zone/ultralytics_naive_stream_example.py
index 1cc82b44..d6922143 100644
--- a/examples/time_in_zone/ultralytics_naive_stream_example.py
+++ b/examples/time_in_zone/ultralytics_naive_stream_example.py
@@ -30,14 +30,10 @@ def main(
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
fps_monitor = sv.FPSMonitor()
- frame = next(frames_generator)
- resolution_wh = frame.shape[1], frame.shape[0]
-
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
diff --git a/examples/time_in_zone/ultralytics_stream_example.py b/examples/time_in_zone/ultralytics_stream_example.py
index 25dc874f..6262d594 100644
--- a/examples/time_in_zone/ultralytics_stream_example.py
+++ b/examples/time_in_zone/ultralytics_stream_example.py
@@ -29,11 +29,9 @@ class CustomSink:
def on_prediction(self, detections: sv.Detections, frame: VideoFrame) -> None:
if self.zones is None:
- resolution_wh = frame.image.shape[1], frame.image.shape[0]
self.zones = [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in self.polygons
diff --git a/examples/traffic_analysis/inference_example.py b/examples/traffic_analysis/inference_example.py
index 7da5f37c..76096be2 100644
--- a/examples/traffic_analysis/inference_example.py
+++ b/examples/traffic_analysis/inference_example.py
@@ -60,13 +60,11 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
- frame_resolution_wh: Tuple[int, int],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=frame_resolution_wh,
triggering_anchors=triggering_anchors,
)
for polygon in polygons
@@ -93,10 +91,10 @@ class VideoProcessor:
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
self.zones_in = initiate_polygon_zones(
- ZONE_IN_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
+ ZONE_IN_POLYGONS, [sv.Position.CENTER]
)
self.zones_out = initiate_polygon_zones(
- ZONE_OUT_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
+ ZONE_OUT_POLYGONS, [sv.Position.CENTER]
)
self.bounding_box_annotator = sv.BoundingBoxAnnotator(color=COLORS)
diff --git a/examples/traffic_analysis/ultralytics_example.py b/examples/traffic_analysis/ultralytics_example.py
index 78189d29..0872100e 100644
--- a/examples/traffic_analysis/ultralytics_example.py
+++ b/examples/traffic_analysis/ultralytics_example.py
@@ -58,13 +58,11 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
- frame_resolution_wh: Tuple[int, int],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
- frame_resolution_wh=frame_resolution_wh,
triggering_anchors=triggering_anchors,
)
for polygon in polygons
@@ -90,10 +88,10 @@ class VideoProcessor:
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
self.zones_in = initiate_polygon_zones(
- ZONE_IN_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
+ ZONE_IN_POLYGONS, [sv.Position.CENTER]
)
self.zones_out = initiate_polygon_zones(
- ZONE_OUT_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
+ ZONE_OUT_POLYGONS, [sv.Position.CENTER]
)
self.bounding_box_annotator = sv.BoundingBoxAnnotator(color=COLORS)
From d531c59f85333fcdcb969741261c2e5b705f0fe3 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sat, 13 Apr 2024 05:39:02 +0000
Subject: [PATCH 052/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
examples/traffic_analysis/inference_example.py | 10 +++-------
examples/traffic_analysis/ultralytics_example.py | 10 +++-------
2 files changed, 6 insertions(+), 14 deletions(-)
diff --git a/examples/traffic_analysis/inference_example.py b/examples/traffic_analysis/inference_example.py
index 76096be2..3cf750ad 100644
--- a/examples/traffic_analysis/inference_example.py
+++ b/examples/traffic_analysis/inference_example.py
@@ -1,6 +1,6 @@
import argparse
import os
-from typing import Dict, Iterable, List, Set, Tuple
+from typing import Dict, Iterable, List, Set
import cv2
import numpy as np
@@ -90,12 +90,8 @@ class VideoProcessor:
self.tracker = sv.ByteTrack()
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
- self.zones_in = initiate_polygon_zones(
- ZONE_IN_POLYGONS, [sv.Position.CENTER]
- )
- self.zones_out = initiate_polygon_zones(
- ZONE_OUT_POLYGONS, [sv.Position.CENTER]
- )
+ self.zones_in = initiate_polygon_zones(ZONE_IN_POLYGONS, [sv.Position.CENTER])
+ self.zones_out = initiate_polygon_zones(ZONE_OUT_POLYGONS, [sv.Position.CENTER])
self.bounding_box_annotator = sv.BoundingBoxAnnotator(color=COLORS)
self.label_annotator = sv.LabelAnnotator(
diff --git a/examples/traffic_analysis/ultralytics_example.py b/examples/traffic_analysis/ultralytics_example.py
index 0872100e..16ba6d8c 100644
--- a/examples/traffic_analysis/ultralytics_example.py
+++ b/examples/traffic_analysis/ultralytics_example.py
@@ -1,5 +1,5 @@
import argparse
-from typing import Dict, Iterable, List, Set, Tuple
+from typing import Dict, Iterable, List, Set
import cv2
import numpy as np
@@ -87,12 +87,8 @@ class VideoProcessor:
self.tracker = sv.ByteTrack()
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
- self.zones_in = initiate_polygon_zones(
- ZONE_IN_POLYGONS, [sv.Position.CENTER]
- )
- self.zones_out = initiate_polygon_zones(
- ZONE_OUT_POLYGONS, [sv.Position.CENTER]
- )
+ self.zones_in = initiate_polygon_zones(ZONE_IN_POLYGONS, [sv.Position.CENTER])
+ self.zones_out = initiate_polygon_zones(ZONE_OUT_POLYGONS, [sv.Position.CENTER])
self.bounding_box_annotator = sv.BoundingBoxAnnotator(color=COLORS)
self.label_annotator = sv.LabelAnnotator(
From 77d3276ad6a94dcbd728254153f3741397fc33aa Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Apr 2024 01:13:38 +0000
Subject: [PATCH 053/136] :arrow_up: Bump ruff from 0.3.6 to 0.3.7
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.6 to 0.3.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.3.6...v0.3.7)
---
updated-dependencies:
- dependency-name: ruff
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 7910b9f6..76edc310 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3643,28 +3643,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.3.6"
+version = "0.3.7"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.3.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:732ef99984275534f9466fbc01121523caf72aa8c2bdeb36fd2edf2bc294a992"},
- {file = "ruff-0.3.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:93699d61116807edc5ca1cdf9d2d22cf8d93335d59e3ff0ca7aee62c1818a736"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc4006cbc6c11fefc25f122d2eb4731d7a3d815dc74d67c54991cc3f99c90177"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:878ef1a55ce931f3ca23b690b159cd0659f495a4c231a847b00ca55e4c688baf"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb87788284af96725643eae9ab3ac746d8cc09aad140268523b019f7ac3cd98"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b2e79f8e1b6bd5411d7ddad3f2abff3f9d371beda29daef86400d416dedb7e02"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf48ec2c4bfae7837dc325c431a2932dc23a1485e71c59591c1df471ba234e0e"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c466a52c522e6a08df0af018f550902f154f5649ad09e7f0d43da766e7399ebc"},
- {file = "ruff-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28ccf3fb6d1162a73cd286c63a5e4d885f46a1f99f0b392924bc95ccbd18ea8f"},
- {file = "ruff-0.3.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b11e09439d9df6cc12d9f622065834654417c40216d271f639512d80e80e3e53"},
- {file = "ruff-0.3.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:647f1fb5128a3e24ce68878b8050bb55044c45bb3f3ae4710d4da9ca96ede5cb"},
- {file = "ruff-0.3.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2b0c4c70578ef1871a9ac5c85ed7a8c33470e976c73ba9211a111d2771b5f787"},
- {file = "ruff-0.3.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e3da499ded004d0b956ab04248b2ae17e54a67ffc81353514ac583af5959a255"},
- {file = "ruff-0.3.6-py3-none-win32.whl", hash = "sha256:4056480f5cf38ad278667c31b0ef334c29acdfcea617cb89c4ccbc7d96f1637f"},
- {file = "ruff-0.3.6-py3-none-win_amd64.whl", hash = "sha256:f1aa621beed533f46e9c7d6fe00e7f6e4570155b61d8f020387b72ace2b42e04"},
- {file = "ruff-0.3.6-py3-none-win_arm64.whl", hash = "sha256:7c8a2a0e0cab077a07465259ffe3b3c090e747ca8097c5dc4c36ca0fdaaac90d"},
- {file = "ruff-0.3.6.tar.gz", hash = "sha256:26071fb530038602b984e3bbe1443ef82a38450c4dcb1344a9caf67234ff9756"},
+ {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"},
+ {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"},
+ {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"},
+ {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"},
+ {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"},
+ {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"},
+ {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"},
+ {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"},
]
[[package]]
From 2af86e56f09c675db2f89471c60abe17d040a3c3 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 15 Apr 2024 17:52:01 +0000
Subject: [PATCH 054/136] =?UTF-8?q?chore(pre=5Fcommit):=20=E2=AC=86=20pre?=
=?UTF-8?q?=5Fcommit=20autoupdate?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.3.5 → v0.3.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.5...v0.3.7)
---
.pre-commit-config.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4c2b1a7e..b016ca8d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -45,7 +45,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.3.5
+ rev: v0.3.7
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
From b3f022b15abc5644aa122a8ddfd527ef99f1bb0d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 17 Apr 2024 00:29:27 +0000
Subject: [PATCH 055/136] :arrow_up: Bump mkdocs-material from 9.5.17 to 9.5.18
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.17 to 9.5.18.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.17...9.5.18)
---
updated-dependencies:
- dependency-name: mkdocs-material
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 76edc310..b5ce75b4 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2180,13 +2180,13 @@ pygments = ">2.12.0"
[[package]]
name = "mkdocs-material"
-version = "9.5.17"
+version = "9.5.18"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.17-py3-none-any.whl", hash = "sha256:14a2a60119a785e70e765dd033e6211367aca9fc70230e577c1cf6a326949571"},
- {file = "mkdocs_material-9.5.17.tar.gz", hash = "sha256:06ae1275a72db1989cf6209de9e9ecdfbcfdbc24c58353877b2bb927dbe413e4"},
+ {file = "mkdocs_material-9.5.18-py3-none-any.whl", hash = "sha256:1e0e27fc9fe239f9064318acf548771a4629d5fd5dfd45444fd80a953fe21eb4"},
+ {file = "mkdocs_material-9.5.18.tar.gz", hash = "sha256:a43f470947053fa2405c33995f282d24992c752a50114f23f30da9d8d0c57e62"},
]
[package.dependencies]
From 53eb76922de4783c41502e41c967d1fb30efdd8a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 18 Apr 2024 00:42:40 +0000
Subject: [PATCH 056/136] :arrow_up: Bump notebook from 7.1.2 to 7.1.3
Bumps [notebook](https://github.com/jupyter/notebook) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/jupyter/notebook/releases)
- [Changelog](https://github.com/jupyter/notebook/blob/@jupyter-notebook/tree@7.1.3/CHANGELOG.md)
- [Commits](https://github.com/jupyter/notebook/compare/@jupyter-notebook/tree@7.1.2...@jupyter-notebook/tree@7.1.3)
---
updated-dependencies:
- dependency-name: notebook
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 76edc310..33e1f935 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2466,13 +2466,13 @@ setuptools = "*"
[[package]]
name = "notebook"
-version = "7.1.2"
+version = "7.1.3"
description = "Jupyter Notebook - A web-based notebook environment for interactive computing"
optional = false
python-versions = ">=3.8"
files = [
- {file = "notebook-7.1.2-py3-none-any.whl", hash = "sha256:fc6c24b9aef18d0cd57157c9c47e95833b9b0bdc599652639acf0bdb61dc7d5f"},
- {file = "notebook-7.1.2.tar.gz", hash = "sha256:efc2c80043909e0faa17fce9e9b37c059c03af0ec99a4d4db84cb21d9d2e936a"},
+ {file = "notebook-7.1.3-py3-none-any.whl", hash = "sha256:919b911e59f41f6e3857ce93c9d93535ba66bb090059712770e5968c07e1004d"},
+ {file = "notebook-7.1.3.tar.gz", hash = "sha256:41fcebff44cf7bb9377180808bcbae066629b55d8c7722f1ebbe75ca44f9cfc1"},
]
[package.dependencies]
From a1a66e7078fb32e5c4eba0ffa755d3a39c068e32 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 19 Apr 2024 00:17:17 +0000
Subject: [PATCH 057/136] :arrow_up: Bump ruff from 0.3.7 to 0.4.0
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.7 to 0.4.0.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.3.7...v0.4.0)
---
updated-dependencies:
- dependency-name: ruff
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index ed0facf0..04cb6e95 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3643,28 +3643,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.3.7"
+version = "0.4.0"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"},
- {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"},
- {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"},
- {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"},
- {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"},
- {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"},
- {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"},
- {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"},
- {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"},
- {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"},
- {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"},
+ {file = "ruff-0.4.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:70b8c620cf2212744eabd6d69c4f839f2be0d8880d27beaeb0adb6aa0b316aa8"},
+ {file = "ruff-0.4.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfa3e3ff53be05a8c5570c1585ea1e089f6b399ca99fcb78598d4a8234f248db"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5616cca501d1d16b932b7e607d7e1fd1b8c8c51d6ee484b7940fc1adc5bea541"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46eff08dd480b5d9b540846159fe134d70e3c45a3c913c600047cbf7f0e4e308"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d546f511431fff2b17adcf7110f3b2c2c0c8d33b0e10e5fd27fd340bc617efc"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c7b6b6b38e216036284c5779b6aa14acbf5664e3b5872533219cf93daf40ddfb"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e1cf8b064bb2a6b4922af7274fe2dffcb552d96ba716b2fbe5e2c970ed7de18"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9911c9046b94253e1fa844c0192bb764b86866a881502dee324686474d498c17"},
+ {file = "ruff-0.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ca7a971c8f1a0b6f5ff4a819c0d1c2619536530bbd5a289af725d8b2ef1013d"},
+ {file = "ruff-0.4.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:752e0f77f421141dd470a0b1bed4fd8f763aebabe32c80ed3580f740ef4ba807"},
+ {file = "ruff-0.4.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:84f2a5dd8f33964d826c5377e094f7ce11e55e432cd42d3bf64efe4384224a03"},
+ {file = "ruff-0.4.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b20e7db4a672495320a8a18149b7febf4e4f97509a4657367144569ce0915fd"},
+ {file = "ruff-0.4.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0b0eddd339e24dc4f7719b1cde4967f6b6bc0ad948cc183711ba8910f14aeafe"},
+ {file = "ruff-0.4.0-py3-none-win32.whl", hash = "sha256:e70befd488271a2c28c80bd427f73d8855dd222fc549fa1e9967d287c5cfe781"},
+ {file = "ruff-0.4.0-py3-none-win_amd64.whl", hash = "sha256:8584b9361900997ccf8d7aaa4dc4ab43e258a853ca7189d98ac929dc9ee50875"},
+ {file = "ruff-0.4.0-py3-none-win_arm64.whl", hash = "sha256:fea4ec813c965e40af29ee627a1579ee1d827d77e81d54b85bdd7b42d1540cdd"},
+ {file = "ruff-0.4.0.tar.gz", hash = "sha256:7457308d9ebf00d6a1c9a26aa755e477787a636c90b823f91cd7d4bea9e89260"},
]
[[package]]
From 98493c2ae1d15ee773f22b98a517101394d7769a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Apr 2024 00:45:54 +0000
Subject: [PATCH 058/136] :arrow_up: Bump ruff from 0.4.0 to 0.4.1
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.0...v0.4.1)
---
updated-dependencies:
- dependency-name: ruff
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 04cb6e95..95cbef01 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3643,28 +3643,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.4.0"
+version = "0.4.1"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.4.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:70b8c620cf2212744eabd6d69c4f839f2be0d8880d27beaeb0adb6aa0b316aa8"},
- {file = "ruff-0.4.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfa3e3ff53be05a8c5570c1585ea1e089f6b399ca99fcb78598d4a8234f248db"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5616cca501d1d16b932b7e607d7e1fd1b8c8c51d6ee484b7940fc1adc5bea541"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46eff08dd480b5d9b540846159fe134d70e3c45a3c913c600047cbf7f0e4e308"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d546f511431fff2b17adcf7110f3b2c2c0c8d33b0e10e5fd27fd340bc617efc"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c7b6b6b38e216036284c5779b6aa14acbf5664e3b5872533219cf93daf40ddfb"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e1cf8b064bb2a6b4922af7274fe2dffcb552d96ba716b2fbe5e2c970ed7de18"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9911c9046b94253e1fa844c0192bb764b86866a881502dee324686474d498c17"},
- {file = "ruff-0.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ca7a971c8f1a0b6f5ff4a819c0d1c2619536530bbd5a289af725d8b2ef1013d"},
- {file = "ruff-0.4.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:752e0f77f421141dd470a0b1bed4fd8f763aebabe32c80ed3580f740ef4ba807"},
- {file = "ruff-0.4.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:84f2a5dd8f33964d826c5377e094f7ce11e55e432cd42d3bf64efe4384224a03"},
- {file = "ruff-0.4.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b20e7db4a672495320a8a18149b7febf4e4f97509a4657367144569ce0915fd"},
- {file = "ruff-0.4.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0b0eddd339e24dc4f7719b1cde4967f6b6bc0ad948cc183711ba8910f14aeafe"},
- {file = "ruff-0.4.0-py3-none-win32.whl", hash = "sha256:e70befd488271a2c28c80bd427f73d8855dd222fc549fa1e9967d287c5cfe781"},
- {file = "ruff-0.4.0-py3-none-win_amd64.whl", hash = "sha256:8584b9361900997ccf8d7aaa4dc4ab43e258a853ca7189d98ac929dc9ee50875"},
- {file = "ruff-0.4.0-py3-none-win_arm64.whl", hash = "sha256:fea4ec813c965e40af29ee627a1579ee1d827d77e81d54b85bdd7b42d1540cdd"},
- {file = "ruff-0.4.0.tar.gz", hash = "sha256:7457308d9ebf00d6a1c9a26aa755e477787a636c90b823f91cd7d4bea9e89260"},
+ {file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2d9ef6231e3fbdc0b8c72404a1a0c46fd0dcea84efca83beb4681c318ea6a953"},
+ {file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9485f54a7189e6f7433e0058cf8581bee45c31a25cd69009d2a040d1bd4bfaef"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2921ac03ce1383e360e8a95442ffb0d757a6a7ddd9a5be68561a671e0e5807e"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eec8d185fe193ad053eda3a6be23069e0c8ba8c5d20bc5ace6e3b9e37d246d3f"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa27d9d72a94574d250f42b7640b3bd2edc4c58ac8ac2778a8c82374bb27984"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f1ee41580bff1a651339eb3337c20c12f4037f6110a36ae4a2d864c52e5ef954"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0926cefb57fc5fced629603fbd1a23d458b25418681d96823992ba975f050c2b"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6e37f2e3cd74496a74af9a4fa67b547ab3ca137688c484749189bf3a686ceb"},
+ {file = "ruff-0.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd703a5975ac1998c2cc5e9494e13b28f31e66c616b0a76e206de2562e0843c"},
+ {file = "ruff-0.4.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b92f03b4aa9fa23e1799b40f15f8b95cdc418782a567d6c43def65e1bbb7f1cf"},
+ {file = "ruff-0.4.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c859f294f8633889e7d77de228b203eb0e9a03071b72b5989d89a0cf98ee262"},
+ {file = "ruff-0.4.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b34510141e393519a47f2d7b8216fec747ea1f2c81e85f076e9f2910588d4b64"},
+ {file = "ruff-0.4.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e68d248ed688b9d69fd4d18737edcbb79c98b251bba5a2b031ce2470224bdf9"},
+ {file = "ruff-0.4.1-py3-none-win32.whl", hash = "sha256:b90506f3d6d1f41f43f9b7b5ff845aeefabed6d2494307bc7b178360a8805252"},
+ {file = "ruff-0.4.1-py3-none-win_amd64.whl", hash = "sha256:c7d391e5936af5c9e252743d767c564670dc3889aff460d35c518ee76e4b26d7"},
+ {file = "ruff-0.4.1-py3-none-win_arm64.whl", hash = "sha256:a1eaf03d87e6a7cd5e661d36d8c6e874693cb9bc3049d110bc9a97b350680c43"},
+ {file = "ruff-0.4.1.tar.gz", hash = "sha256:d592116cdbb65f8b1b7e2a2b48297eb865f6bdc20641879aa9d7b9c11d86db79"},
]
[[package]]
From feae6d3f78844a32ec03723eb91d6d37d6258d02 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 22 Apr 2024 17:43:51 +0000
Subject: [PATCH 059/136] =?UTF-8?q?chore(pre=5Fcommit):=20=E2=AC=86=20pre?=
=?UTF-8?q?=5Fcommit=20autoupdate?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.3.7 → v0.4.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.7...v0.4.1)
---
.pre-commit-config.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b016ca8d..ef3620b1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -45,7 +45,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.3.7
+ rev: v0.4.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
From a6b563cf4def3dbdca8dfeae60a5cfdbf87ff8b5 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Tue, 23 Apr 2024 17:18:34 +0300
Subject: [PATCH 060/136] Add keypoints skeleton, 2x annotators
---
supervision/keypoints/annotate.py | 102 ++++++++++++++++++++++++++++++
supervision/keypoints/core.py | 43 ++++++++++++-
2 files changed, 142 insertions(+), 3 deletions(-)
create mode 100644 supervision/keypoints/annotate.py
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
new file mode 100644
index 00000000..4c37b248
--- /dev/null
+++ b/supervision/keypoints/annotate.py
@@ -0,0 +1,102 @@
+from abc import ABC, abstractmethod
+
+import cv2
+import numpy as np
+
+from supervision.annotators.base import ImageType
+from supervision.annotators.utils import scene_to_annotator_img_type
+from supervision.draw.color import Color
+from supervision.keypoints.core import KeyPoints, Skeleton
+
+
+class BaseKeyPointAnnotator(ABC):
+ @abstractmethod
+ def annotate(self, scene: ImageType, detections: KeyPoints) -> ImageType:
+ pass
+
+
+class PointAnnotator(BaseKeyPointAnnotator):
+ def __init__(
+ self,
+ color: Color = Color.GREEN,
+ radius: int = 4,
+ ) -> None:
+ """
+ Most basic keypoint annotator.
+ """
+ self.color = color
+ self.radius = radius
+
+ @scene_to_annotator_img_type
+ def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
+ # TODO: I'm getting shape [1, N, 2] here - not [N, 2].
+ # Seems like it accounts for more than 1 person in an image.
+ if len(keypoints) == 0:
+ return scene
+
+ xy = keypoints.xy[0]
+ for i, (x, y) in enumerate(xy):
+ cv2.circle(
+ img=scene,
+ center=(int(x), int(y)),
+ radius=self.radius,
+ color=self.color.as_bgr(),
+ thickness=-1,
+ )
+
+ return scene
+
+
+class SkeletonAnnotator(BaseKeyPointAnnotator):
+ def __init__(
+ self,
+ point_color: Color = Color.GREEN,
+ point_radius: int = 4,
+ limb_color: Color = Color.GREEN,
+ limb_thickness: int = 2,
+ ) -> None:
+ self.point_color = point_color
+ self.point_radius = point_radius
+ self.limb_color = limb_color
+ self.limb_thickness = limb_thickness
+
+ @scene_to_annotator_img_type
+ def annotate(
+ self, scene: ImageType, keypoints: KeyPoints, skeleton: Skeleton
+ ) -> ImageType:
+ if len(keypoints) == 0:
+ return scene
+ if keypoints.class_id is None:
+ raise ValueError("KeyPoints must have class_id to annotate a skeleton")
+
+ # print(keypoints)
+
+ xy = keypoints.xy[0]
+ class_id = keypoints.class_id[0]
+
+ for i, (x, y) in enumerate(xy):
+ cv2.circle(
+ img=scene,
+ center=(int(x), int(y)),
+ radius=self.point_radius,
+ color=self.point_color.as_bgr(),
+ thickness=-1,
+ )
+
+ for class_a, class_b in skeleton.limbs:
+ xy_a = xy[class_a - 1]
+ xy_b = xy[class_b - 1]
+ missing_a = np.allclose(xy_a, 0)
+ missing_b = np.allclose(xy_b, 0)
+ if missing_a or missing_b:
+ continue
+
+ cv2.line(
+ img=scene,
+ pt1=(int(xy_a[0]), int(xy_a[1])),
+ pt2=(int(xy_b[0]), int(xy_b[1])),
+ color=self.limb_color.as_bgr(),
+ thickness=self.limb_thickness,
+ )
+
+ return scene
diff --git a/supervision/keypoints/core.py b/supervision/keypoints/core.py
index 646b99db..25f14fae 100644
--- a/supervision/keypoints/core.py
+++ b/supervision/keypoints/core.py
@@ -132,14 +132,15 @@ class KeyPoints:
result = model(image)[0]
keypoints = sv.KeyPoints.from_ultralytics(result)
```
- """ #
+ """
+ if len(ultralytics_results.keypoints.xy) == 0:
+ return cls.empty()
+ xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
class_names = np.array([ultralytics_results.names[i] for i in class_id])
- xy = ultralytics_results.keypoints.xy.cpu().numpy()
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
- class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
data = {CLASS_NAME_DATA_FIELD: class_names}
return cls(xy, class_id, confidence, data)
@@ -238,3 +239,39 @@ class KeyPoints:
```
"""
return cls(xy=np.empty((0, 0, 2), dtype=np.float32))
+
+
+class Skeleton:
+ """
+ The `Skeleton` class connects keypoints to form a skeleton.
+
+ It provides utility methods to compute angles within itself, compare to skeletons, etc.
+ """
+
+ limbs: List[Tuple[int, int]]
+
+ def __init__(self, limbs: List[Tuple[int, int]]):
+ self.limbs = limbs
+
+
+YOLO_V8_SKELETON = Skeleton(
+ [
+ (1, 2),
+ (1, 3),
+ (2, 3),
+ (2, 4),
+ (3, 5),
+ (6, 12),
+ (6, 7),
+ (6, 8),
+ (7, 13),
+ (7, 9),
+ (8, 10),
+ (9, 11),
+ (12, 13),
+ (14, 12),
+ (15, 13),
+ (16, 14),
+ (17, 15),
+ ]
+)
From d079ce92df781fb43ee46d8047faa795fb066983 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Tue, 23 Apr 2024 19:26:12 +0300
Subject: [PATCH 061/136] Keypoints: Separate-out skeleton annotator
---
supervision/keypoints/annotate.py | 31 ++++++++-----------------------
1 file changed, 8 insertions(+), 23 deletions(-)
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
index 4c37b248..b82ea071 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoints/annotate.py
@@ -50,15 +50,11 @@ class PointAnnotator(BaseKeyPointAnnotator):
class SkeletonAnnotator(BaseKeyPointAnnotator):
def __init__(
self,
- point_color: Color = Color.GREEN,
- point_radius: int = 4,
- limb_color: Color = Color.GREEN,
- limb_thickness: int = 2,
+ color: Color = Color.GREEN,
+ thickness: int = 2,
) -> None:
- self.point_color = point_color
- self.point_radius = point_radius
- self.limb_color = limb_color
- self.limb_thickness = limb_thickness
+ self.color = color
+ self.thickness = thickness
@scene_to_annotator_img_type
def annotate(
@@ -67,21 +63,10 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
if len(keypoints) == 0:
return scene
if keypoints.class_id is None:
- raise ValueError("KeyPoints must have class_id to annotate a skeleton")
-
- # print(keypoints)
+ raise ValueError(
+ "KeyPoints must have class_id to annotate a skeleton")
xy = keypoints.xy[0]
- class_id = keypoints.class_id[0]
-
- for i, (x, y) in enumerate(xy):
- cv2.circle(
- img=scene,
- center=(int(x), int(y)),
- radius=self.point_radius,
- color=self.point_color.as_bgr(),
- thickness=-1,
- )
for class_a, class_b in skeleton.limbs:
xy_a = xy[class_a - 1]
@@ -95,8 +80,8 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
img=scene,
pt1=(int(xy_a[0]), int(xy_a[1])),
pt2=(int(xy_b[0]), int(xy_b[1])),
- color=self.limb_color.as_bgr(),
- thickness=self.limb_thickness,
+ color=self.color.as_bgr(),
+ thickness=self.thickness,
)
return scene
From a00604bfb24c609727a4b26cea923d62602a07fc Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Tue, 23 Apr 2024 19:28:34 +0300
Subject: [PATCH 062/136] Keypoints: Expose KeyPoints, annotators in __init__
---
supervision/__init__.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/supervision/__init__.py b/supervision/__init__.py
index df04534d..ad02202d 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -56,6 +56,8 @@ from supervision.detection.utils import (
polygon_to_xyxy,
scale_boxes,
)
+from supervision.keypoints.core import KeyPoints, YOLO_V8_SKELETON
+from supervision.keypoints.annotate import PointAnnotator, SkeletonAnnotator
from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import (
calculate_optimal_line_thickness,
From 7f0ecbbd0a2f76f9a54a36449a48033d87df83fc Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Tue, 23 Apr 2024 20:19:28 +0300
Subject: [PATCH 063/136] Keypoints: Smart skellies
---
supervision/__init__.py | 5 +-
supervision/keypoints/annotate.py | 30 +++++++--
supervision/keypoints/core.py | 42 ++-----------
supervision/keypoints/skeletons.py | 97 ++++++++++++++++++++++++++++++
4 files changed, 129 insertions(+), 45 deletions(-)
create mode 100644 supervision/keypoints/skeletons.py
diff --git a/supervision/__init__.py b/supervision/__init__.py
index ad02202d..549e870e 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -56,8 +56,9 @@ from supervision.detection.utils import (
polygon_to_xyxy,
scale_boxes,
)
-from supervision.keypoints.core import KeyPoints, YOLO_V8_SKELETON
-from supervision.keypoints.annotate import PointAnnotator, SkeletonAnnotator
+from supervision.keypoints.core import KeyPoints
+from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
+from supervision.keypoints.annotate import KeyPointAnnotator, SkeletonAnnotator
from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import (
calculate_optimal_line_thickness,
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
index b82ea071..0511def4 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoints/annotate.py
@@ -1,4 +1,5 @@
from abc import ABC, abstractmethod
+from typing import Optional
import cv2
import numpy as np
@@ -6,7 +7,8 @@ import numpy as np
from supervision.annotators.base import ImageType
from supervision.annotators.utils import scene_to_annotator_img_type
from supervision.draw.color import Color
-from supervision.keypoints.core import KeyPoints, Skeleton
+from supervision.keypoints.core import KeyPoints
+from supervision.keypoints.skeletons import Skeleton, KnownSkeletons
class BaseKeyPointAnnotator(ABC):
@@ -15,14 +17,18 @@ class BaseKeyPointAnnotator(ABC):
pass
-class PointAnnotator(BaseKeyPointAnnotator):
+class KeyPointAnnotator(BaseKeyPointAnnotator):
def __init__(
self,
- color: Color = Color.GREEN,
+ color: Color = Color.ROBOFLOW,
radius: int = 4,
) -> None:
"""
Most basic keypoint annotator.
+
+ Args:
+ color (Color, optional): The color of the keypoint.
+ radius (int, optional): The radius of the keypoint.
"""
self.color = color
self.radius = radius
@@ -50,15 +56,26 @@ class PointAnnotator(BaseKeyPointAnnotator):
class SkeletonAnnotator(BaseKeyPointAnnotator):
def __init__(
self,
- color: Color = Color.GREEN,
+ color: Color = Color.ROBOFLOW,
thickness: int = 2,
+ skeleton: Optional[Skeleton] = None,
) -> None:
+ """
+ Draw the lines between points of the image.
+
+ Args:
+ color (Color, optional): The color of the lines.
+ thickness (int, optional): The thickness of the lines.
+ skeleton (Skeleton, optional): The skeleton to draw.
+ If set to `None`, will attempt to select automatically.
+ """
self.color = color
self.thickness = thickness
+ self.skeleton = skeleton
@scene_to_annotator_img_type
def annotate(
- self, scene: ImageType, keypoints: KeyPoints, skeleton: Skeleton
+ self, scene: ImageType, keypoints: KeyPoints
) -> ImageType:
if len(keypoints) == 0:
return scene
@@ -67,6 +84,9 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
"KeyPoints must have class_id to annotate a skeleton")
xy = keypoints.xy[0]
+ skeleton = self.skeleton
+ if not skeleton:
+ skeleton = KnownSkeletons().get_skeleton(len(xy))
for class_a, class_b in skeleton.limbs:
xy_a = xy[class_a - 1]
diff --git a/supervision/keypoints/core.py b/supervision/keypoints/core.py
index 25f14fae..5aa0a4d1 100644
--- a/supervision/keypoints/core.py
+++ b/supervision/keypoints/core.py
@@ -58,7 +58,8 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]
+ ] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -138,7 +139,8 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i] for i in class_id])
+ class_names = np.array([ultralytics_results.names[i]
+ for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
@@ -239,39 +241,3 @@ class KeyPoints:
```
"""
return cls(xy=np.empty((0, 0, 2), dtype=np.float32))
-
-
-class Skeleton:
- """
- The `Skeleton` class connects keypoints to form a skeleton.
-
- It provides utility methods to compute angles within itself, compare to skeletons, etc.
- """
-
- limbs: List[Tuple[int, int]]
-
- def __init__(self, limbs: List[Tuple[int, int]]):
- self.limbs = limbs
-
-
-YOLO_V8_SKELETON = Skeleton(
- [
- (1, 2),
- (1, 3),
- (2, 3),
- (2, 4),
- (3, 5),
- (6, 12),
- (6, 7),
- (6, 8),
- (7, 13),
- (7, 9),
- (8, 10),
- (9, 11),
- (12, 13),
- (14, 12),
- (15, 13),
- (16, 14),
- (17, 15),
- ]
-)
diff --git a/supervision/keypoints/skeletons.py b/supervision/keypoints/skeletons.py
new file mode 100644
index 00000000..a9d8d01f
--- /dev/null
+++ b/supervision/keypoints/skeletons.py
@@ -0,0 +1,97 @@
+
+
+from typing import List, Tuple
+
+
+class Skeleton:
+ """
+ The `Skeleton` class connects keypoints to form a skeleton.
+
+ Args:
+ limbs: (List[Tuple[int, int]]): List of (`class_id_1`,
+ `class_id_2`) representing connected points of the skeleton.
+ """
+
+ limbs: List[Tuple[int, int]]
+
+ def __init__(self, limbs: List[Tuple[int, int]]):
+ self.limbs = limbs
+
+
+NO_SKELETON = Skeleton([])
+YOLO_V8_SKELETON = Skeleton(
+ [
+ (1, 2),
+ (1, 3),
+ (2, 3),
+ (2, 4),
+ (3, 5),
+ (6, 12),
+ (6, 7),
+ (6, 8),
+ (7, 13),
+ (7, 9),
+ (8, 10),
+ (9, 11),
+ (12, 13),
+ (14, 12),
+ (15, 13),
+ (16, 14),
+ (17, 15),
+ ]
+)
+
+
+def _make_sequential_skeleton(limb_count: int) -> Skeleton:
+ """
+ Create a skeleton where neighboring keypoints are connected sequentially.
+
+ Args:
+ limb_count (int): The number of limbs to connect.
+
+ Returns:
+ Skeleton: The skeleton connecting all keypoints.
+ """
+ limbs = []
+ for i in range(0, limb_count - 1):
+ limbs.append((i, i + 1))
+ limbs.append((0, limb_count - 1))
+ return Skeleton(limbs)
+
+
+class KnownSkeletons:
+ """
+ Helps automatically determine which skeleton to use.
+
+ The `KnownSkeletons` class contains a collection of predefined skeletons
+ for various keypoint models, to be distinguished by limb counts.
+
+ Example:
+ # Suppose you have a model returning 15 keypoints.
+ skeleton = KnownSkeletons().get_skeleton(15)
+ """
+ _instance = None
+ _skeletons: dict[int, Skeleton] = {}
+
+ def __new__(cls):
+ if cls._instance is None:
+ cls._instance = super(KnownSkeletons, cls).__new__(cls)
+ return cls._instance
+
+ def add_skeleton(self, skeleton: Skeleton) -> None:
+ len_limbs = len(skeleton.limbs)
+ if len_limbs in self._skeletons:
+ raise ValueError(
+ f"A skeleton with {len_limbs} limbs already exists")
+ self._skeletons[len_limbs] = skeleton
+
+ def get_skeleton(self, limb_count: int) -> Skeleton:
+ if limb_count not in self._skeletons:
+ print(
+ f"Warning: No skeleton found with {limb_count} limbs. Will create one now.")
+ self.add_skeleton(_make_sequential_skeleton(limb_count))
+ return self._skeletons[limb_count]
+
+
+KnownSkeletons().add_skeleton(NO_SKELETON)
+KnownSkeletons().add_skeleton(YOLO_V8_SKELETON)
From 07482a347cfcbbfb7bf7d130541146c211c8f896 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Tue, 23 Apr 2024 20:43:14 +0300
Subject: [PATCH 064/136] Keypoints: Multi-person annotators
---
supervision/keypoints/annotate.py | 56 ++++++++++++++++---------------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
index 0511def4..e051fa8e 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoints/annotate.py
@@ -40,15 +40,16 @@ class KeyPointAnnotator(BaseKeyPointAnnotator):
if len(keypoints) == 0:
return scene
- xy = keypoints.xy[0]
- for i, (x, y) in enumerate(xy):
- cv2.circle(
- img=scene,
- center=(int(x), int(y)),
- radius=self.radius,
- color=self.color.as_bgr(),
- thickness=-1,
- )
+ xy_all = keypoints.xy
+ for xy in xy_all:
+ for x, y in xy:
+ cv2.circle(
+ img=scene,
+ center=(int(x), int(y)),
+ radius=self.radius,
+ color=self.color.as_bgr(),
+ thickness=-1,
+ )
return scene
@@ -83,25 +84,26 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
raise ValueError(
"KeyPoints must have class_id to annotate a skeleton")
- xy = keypoints.xy[0]
- skeleton = self.skeleton
- if not skeleton:
- skeleton = KnownSkeletons().get_skeleton(len(xy))
+ xy_all = keypoints.xy
+ for xy in xy_all:
+ skeleton = self.skeleton
+ if not skeleton:
+ skeleton = KnownSkeletons().get_skeleton(len(xy))
- for class_a, class_b in skeleton.limbs:
- xy_a = xy[class_a - 1]
- xy_b = xy[class_b - 1]
- missing_a = np.allclose(xy_a, 0)
- missing_b = np.allclose(xy_b, 0)
- if missing_a or missing_b:
- continue
+ for class_a, class_b in skeleton.limbs:
+ xy_a = xy[class_a - 1]
+ xy_b = xy[class_b - 1]
+ missing_a = np.allclose(xy_a, 0)
+ missing_b = np.allclose(xy_b, 0)
+ if missing_a or missing_b:
+ continue
- cv2.line(
- img=scene,
- pt1=(int(xy_a[0]), int(xy_a[1])),
- pt2=(int(xy_b[0]), int(xy_b[1])),
- color=self.color.as_bgr(),
- thickness=self.thickness,
- )
+ cv2.line(
+ img=scene,
+ pt1=(int(xy_a[0]), int(xy_a[1])),
+ pt2=(int(xy_b[0]), int(xy_b[1])),
+ color=self.color.as_bgr(),
+ thickness=self.thickness,
+ )
return scene
From 409dcbd9da5d1aeb5c3a7746163be87bfaf3628d Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 23 Apr 2024 20:49:03 +0000
Subject: [PATCH 065/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/__init__.py | 5 ++---
supervision/keypoints/annotate.py | 9 +++------
supervision/keypoints/core.py | 6 ++----
supervision/keypoints/skeletons.py | 9 ++++-----
4 files changed, 11 insertions(+), 18 deletions(-)
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 549e870e..c4883335 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -56,9 +56,6 @@ from supervision.detection.utils import (
polygon_to_xyxy,
scale_boxes,
)
-from supervision.keypoints.core import KeyPoints
-from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
-from supervision.keypoints.annotate import KeyPointAnnotator, SkeletonAnnotator
from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import (
calculate_optimal_line_thickness,
@@ -72,7 +69,9 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
+from supervision.keypoints.annotate import KeyPointAnnotator, SkeletonAnnotator
from supervision.keypoints.core import KeyPoints
+from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
from supervision.utils.file import list_files_with_extensions
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
index e051fa8e..978c1e8d 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoints/annotate.py
@@ -8,7 +8,7 @@ from supervision.annotators.base import ImageType
from supervision.annotators.utils import scene_to_annotator_img_type
from supervision.draw.color import Color
from supervision.keypoints.core import KeyPoints
-from supervision.keypoints.skeletons import Skeleton, KnownSkeletons
+from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
class BaseKeyPointAnnotator(ABC):
@@ -75,14 +75,11 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
self.skeleton = skeleton
@scene_to_annotator_img_type
- def annotate(
- self, scene: ImageType, keypoints: KeyPoints
- ) -> ImageType:
+ def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
if len(keypoints) == 0:
return scene
if keypoints.class_id is None:
- raise ValueError(
- "KeyPoints must have class_id to annotate a skeleton")
+ raise ValueError("KeyPoints must have class_id to annotate a skeleton")
xy_all = keypoints.xy
for xy in xy_all:
diff --git a/supervision/keypoints/core.py b/supervision/keypoints/core.py
index 5aa0a4d1..2b432be4 100644
--- a/supervision/keypoints/core.py
+++ b/supervision/keypoints/core.py
@@ -58,8 +58,7 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]
- ] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -139,8 +138,7 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i]
- for i in class_id])
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
diff --git a/supervision/keypoints/skeletons.py b/supervision/keypoints/skeletons.py
index a9d8d01f..03a4f335 100644
--- a/supervision/keypoints/skeletons.py
+++ b/supervision/keypoints/skeletons.py
@@ -1,5 +1,3 @@
-
-
from typing import List, Tuple
@@ -70,6 +68,7 @@ class KnownSkeletons:
# Suppose you have a model returning 15 keypoints.
skeleton = KnownSkeletons().get_skeleton(15)
"""
+
_instance = None
_skeletons: dict[int, Skeleton] = {}
@@ -81,14 +80,14 @@ class KnownSkeletons:
def add_skeleton(self, skeleton: Skeleton) -> None:
len_limbs = len(skeleton.limbs)
if len_limbs in self._skeletons:
- raise ValueError(
- f"A skeleton with {len_limbs} limbs already exists")
+ raise ValueError(f"A skeleton with {len_limbs} limbs already exists")
self._skeletons[len_limbs] = skeleton
def get_skeleton(self, limb_count: int) -> Skeleton:
if limb_count not in self._skeletons:
print(
- f"Warning: No skeleton found with {limb_count} limbs. Will create one now.")
+ f"Warning: No skeleton found with {limb_count} limbs. Will create one now."
+ )
self.add_skeleton(_make_sequential_skeleton(limb_count))
return self._skeletons[limb_count]
From 0274c45dbc086b75144a85a30af0630adf94fadf Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 23 Apr 2024 23:12:54 +0200
Subject: [PATCH 066/136] solve CI tests
---
supervision/keypoints/annotate.py | 6 +++---
supervision/keypoints/skeletons.py | 3 ++-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
index 978c1e8d..0a837946 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoints/annotate.py
@@ -5,10 +5,10 @@ import cv2
import numpy as np
from supervision.annotators.base import ImageType
-from supervision.annotators.utils import scene_to_annotator_img_type
from supervision.draw.color import Color
from supervision.keypoints.core import KeyPoints
from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
+from supervision.utils.conversion import convert_for_annotation_method
class BaseKeyPointAnnotator(ABC):
@@ -33,7 +33,7 @@ class KeyPointAnnotator(BaseKeyPointAnnotator):
self.color = color
self.radius = radius
- @scene_to_annotator_img_type
+ @convert_for_annotation_method
def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
# TODO: I'm getting shape [1, N, 2] here - not [N, 2].
# Seems like it accounts for more than 1 person in an image.
@@ -74,7 +74,7 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
self.thickness = thickness
self.skeleton = skeleton
- @scene_to_annotator_img_type
+ @convert_for_annotation_method
def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
if len(keypoints) == 0:
return scene
diff --git a/supervision/keypoints/skeletons.py b/supervision/keypoints/skeletons.py
index 03a4f335..d150b661 100644
--- a/supervision/keypoints/skeletons.py
+++ b/supervision/keypoints/skeletons.py
@@ -86,7 +86,8 @@ class KnownSkeletons:
def get_skeleton(self, limb_count: int) -> Skeleton:
if limb_count not in self._skeletons:
print(
- f"Warning: No skeleton found with {limb_count} limbs. Will create one now."
+ f"Warning: No skeleton found with {limb_count} limbs. "
+ f"Will create one now."
)
self.add_skeleton(_make_sequential_skeleton(limb_count))
return self._skeletons[limb_count]
From 9789ff4043b0c6cf7a70b62a9deac1bceb807be5 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 23 Apr 2024 23:12:54 +0200
Subject: [PATCH 067/136] solve CI tests
---
supervision/keypoints/annotate.py | 6 +++---
supervision/keypoints/skeletons.py | 7 ++++---
supervision/keypoints/utils.py | 0
3 files changed, 7 insertions(+), 6 deletions(-)
delete mode 100644 supervision/keypoints/utils.py
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoints/annotate.py
index 978c1e8d..0a837946 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoints/annotate.py
@@ -5,10 +5,10 @@ import cv2
import numpy as np
from supervision.annotators.base import ImageType
-from supervision.annotators.utils import scene_to_annotator_img_type
from supervision.draw.color import Color
from supervision.keypoints.core import KeyPoints
from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
+from supervision.utils.conversion import convert_for_annotation_method
class BaseKeyPointAnnotator(ABC):
@@ -33,7 +33,7 @@ class KeyPointAnnotator(BaseKeyPointAnnotator):
self.color = color
self.radius = radius
- @scene_to_annotator_img_type
+ @convert_for_annotation_method
def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
# TODO: I'm getting shape [1, N, 2] here - not [N, 2].
# Seems like it accounts for more than 1 person in an image.
@@ -74,7 +74,7 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
self.thickness = thickness
self.skeleton = skeleton
- @scene_to_annotator_img_type
+ @convert_for_annotation_method
def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
if len(keypoints) == 0:
return scene
diff --git a/supervision/keypoints/skeletons.py b/supervision/keypoints/skeletons.py
index 03a4f335..5b072275 100644
--- a/supervision/keypoints/skeletons.py
+++ b/supervision/keypoints/skeletons.py
@@ -1,4 +1,4 @@
-from typing import List, Tuple
+from typing import List, Tuple, Dict
class Skeleton:
@@ -70,7 +70,7 @@ class KnownSkeletons:
"""
_instance = None
- _skeletons: dict[int, Skeleton] = {}
+ _skeletons: Dict[int, Skeleton] = {}
def __new__(cls):
if cls._instance is None:
@@ -86,7 +86,8 @@ class KnownSkeletons:
def get_skeleton(self, limb_count: int) -> Skeleton:
if limb_count not in self._skeletons:
print(
- f"Warning: No skeleton found with {limb_count} limbs. Will create one now."
+ f"Warning: No skeleton found with {limb_count} limbs. "
+ f"Will create one now."
)
self.add_skeleton(_make_sequential_skeleton(limb_count))
return self._skeletons[limb_count]
diff --git a/supervision/keypoints/utils.py b/supervision/keypoints/utils.py
deleted file mode 100644
index e69de29b..00000000
From e2fabc1cfe38ea815100b6651c6ea5d359c138f5 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 23 Apr 2024 21:57:28 +0000
Subject: [PATCH 068/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoints/skeletons.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/keypoints/skeletons.py b/supervision/keypoints/skeletons.py
index 5b072275..dd8c7ec8 100644
--- a/supervision/keypoints/skeletons.py
+++ b/supervision/keypoints/skeletons.py
@@ -1,4 +1,4 @@
-from typing import List, Tuple, Dict
+from typing import Dict, List, Tuple
class Skeleton:
From 2745c70ae0e558c18afef7a0c67d0d88d6311f89 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 00:21:19 +0200
Subject: [PATCH 069/136] docs plugged into `mkdocs.yml`
---
docs/keypoint/annotate.md | 10 ++++++++++
docs/keypoint/core.md | 8 ++++++++
mkdocs.yml | 3 +++
supervision/__init__.py | 6 +++---
supervision/{keypoints => keypoint}/__init__.py | 0
supervision/{keypoints => keypoint}/annotate.py | 8 +++-----
supervision/{keypoints => keypoint}/core.py | 8 +++++---
supervision/{keypoints => keypoint}/skeletons.py | 0
8 files changed, 32 insertions(+), 11 deletions(-)
create mode 100644 docs/keypoint/annotate.md
create mode 100644 docs/keypoint/core.md
rename supervision/{keypoints => keypoint}/__init__.py (100%)
rename supervision/{keypoints => keypoint}/annotate.py (90%)
rename supervision/{keypoints => keypoint}/core.py (97%)
rename supervision/{keypoints => keypoint}/skeletons.py (100%)
diff --git a/docs/keypoint/annotate.md b/docs/keypoint/annotate.md
new file mode 100644
index 00000000..8507f54e
--- /dev/null
+++ b/docs/keypoint/annotate.md
@@ -0,0 +1,10 @@
+---
+comments: true
+status: new
+---
+
+# Annotate
+
+:::supervision.keypoint.annotate.VertexAnnotator
+
+:::supervision.keypoint.annotate.SkeletonAnnotator
\ No newline at end of file
diff --git a/docs/keypoint/core.md b/docs/keypoint/core.md
new file mode 100644
index 00000000..7354baba
--- /dev/null
+++ b/docs/keypoint/core.md
@@ -0,0 +1,8 @@
+---
+comments: true
+status: new
+---
+
+# Keypoint Detection
+
+:::supervision.keypoint.core.KeyPoints
diff --git a/mkdocs.yml b/mkdocs.yml
index dd6feb8d..256c6bef 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -50,6 +50,9 @@ nav:
- Detections:
- Core: detection/core.md
- Utils: detection/utils.md
+ - KeyPoints:
+ - Core: keypoint/core.md
+ - Annotators: keypoint/annotate.md
- Tools:
- Line Zone: detection/tools/line_zone.md
- Polygon Zone: detection/tools/polygon_zone.md
diff --git a/supervision/__init__.py b/supervision/__init__.py
index c25ec791..70c912c0 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -69,9 +69,9 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
-from supervision.keypoints.annotate import KeyPointAnnotator, SkeletonAnnotator
-from supervision.keypoints.core import KeyPoints
-from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
+from supervision.keypoint.annotate import VertexAnnotator, SkeletonAnnotator
+from supervision.keypoint.core import KeyPoints
+from supervision.keypoint.skeletons import KnownSkeletons, Skeleton
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
from supervision.utils.conversion import cv2_to_pillow, pillow_to_cv2
diff --git a/supervision/keypoints/__init__.py b/supervision/keypoint/__init__.py
similarity index 100%
rename from supervision/keypoints/__init__.py
rename to supervision/keypoint/__init__.py
diff --git a/supervision/keypoints/annotate.py b/supervision/keypoint/annotate.py
similarity index 90%
rename from supervision/keypoints/annotate.py
rename to supervision/keypoint/annotate.py
index 0a837946..802199f3 100644
--- a/supervision/keypoints/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -6,8 +6,8 @@ import numpy as np
from supervision.annotators.base import ImageType
from supervision.draw.color import Color
-from supervision.keypoints.core import KeyPoints
-from supervision.keypoints.skeletons import KnownSkeletons, Skeleton
+from supervision.keypoint.core import KeyPoints
+from supervision.keypoint.skeletons import KnownSkeletons, Skeleton
from supervision.utils.conversion import convert_for_annotation_method
@@ -17,7 +17,7 @@ class BaseKeyPointAnnotator(ABC):
pass
-class KeyPointAnnotator(BaseKeyPointAnnotator):
+class VertexAnnotator(BaseKeyPointAnnotator):
def __init__(
self,
color: Color = Color.ROBOFLOW,
@@ -35,8 +35,6 @@ class KeyPointAnnotator(BaseKeyPointAnnotator):
@convert_for_annotation_method
def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
- # TODO: I'm getting shape [1, N, 2] here - not [N, 2].
- # Seems like it accounts for more than 1 person in an image.
if len(keypoints) == 0:
return scene
diff --git a/supervision/keypoints/core.py b/supervision/keypoint/core.py
similarity index 97%
rename from supervision/keypoints/core.py
rename to supervision/keypoint/core.py
index 2b432be4..aca50ad5 100644
--- a/supervision/keypoints/core.py
+++ b/supervision/keypoint/core.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
from dataclasses import dataclass, field
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
@@ -98,7 +100,7 @@ class KeyPoints:
get_data_item(self.data, i),
)
- def __eq__(self, other: "KeyPoints") -> bool:
+ def __eq__(self, other: KeyPoints) -> bool:
return all(
[
np.array_equal(self.xy, other.xy),
@@ -109,7 +111,7 @@ class KeyPoints:
)
@classmethod
- def from_ultralytics(cls, ultralytics_results) -> "KeyPoints":
+ def from_ultralytics(cls, ultralytics_results) -> KeyPoints:
"""
Creates a Keypoints instance from a
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
@@ -225,7 +227,7 @@ class KeyPoints:
self.data[key] = value
@classmethod
- def empty(cls) -> "KeyPoints":
+ def empty(cls) -> KeyPoints:
"""
Create an empty Keypoints object with no keypoints.
diff --git a/supervision/keypoints/skeletons.py b/supervision/keypoint/skeletons.py
similarity index 100%
rename from supervision/keypoints/skeletons.py
rename to supervision/keypoint/skeletons.py
From e2865ecda73e99ed072ebdae3dd65147e551f2be Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 23 Apr 2024 22:21:47 +0000
Subject: [PATCH 070/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/keypoint/annotate.md | 2 +-
supervision/__init__.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/keypoint/annotate.md b/docs/keypoint/annotate.md
index 8507f54e..476ae918 100644
--- a/docs/keypoint/annotate.md
+++ b/docs/keypoint/annotate.md
@@ -7,4 +7,4 @@ status: new
:::supervision.keypoint.annotate.VertexAnnotator
-:::supervision.keypoint.annotate.SkeletonAnnotator
\ No newline at end of file
+:::supervision.keypoint.annotate.SkeletonAnnotator
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 70c912c0..53948d1a 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -69,7 +69,7 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
-from supervision.keypoint.annotate import VertexAnnotator, SkeletonAnnotator
+from supervision.keypoint.annotate import SkeletonAnnotator, VertexAnnotator
from supervision.keypoint.core import KeyPoints
from supervision.keypoint.skeletons import KnownSkeletons, Skeleton
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
From 397b2249a776c2321d18b89b5a984ec38773b498 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 00:24:21 +0200
Subject: [PATCH 071/136] fix typo
---
test/test_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test_utils.py b/test/test_utils.py
index d3624845..b676cb54 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional
import numpy as np
from supervision.detection.core import Detections
-from supervision.keypoints.core import KeyPoints
+from supervision.keypoint.core import KeyPoints
def mock_detections(
From 102c5adc386c12a53ef7ddec583bfe68f171ccd1 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 00:43:46 +0200
Subject: [PATCH 072/136] test simplified skeleton API
---
supervision/__init__.py | 2 +-
supervision/keypoint/annotate.py | 10 ++--
supervision/keypoint/skeletons.py | 88 ++++++-------------------------
3 files changed, 21 insertions(+), 79 deletions(-)
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 53948d1a..63235df8 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -71,7 +71,7 @@ from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
from supervision.keypoint.annotate import SkeletonAnnotator, VertexAnnotator
from supervision.keypoint.core import KeyPoints
-from supervision.keypoint.skeletons import KnownSkeletons, Skeleton
+from supervision.keypoint.skeletons import Skeleton
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
from supervision.utils.conversion import cv2_to_pillow, pillow_to_cv2
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index 802199f3..b3a29b4a 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
-from typing import Optional
+from typing import Optional, List, Tuple
import cv2
import numpy as np
@@ -7,7 +7,7 @@ import numpy as np
from supervision.annotators.base import ImageType
from supervision.draw.color import Color
from supervision.keypoint.core import KeyPoints
-from supervision.keypoint.skeletons import KnownSkeletons, Skeleton
+from supervision.keypoint.skeletons import resolve_skeleton_by_vertex_count
from supervision.utils.conversion import convert_for_annotation_method
@@ -57,7 +57,7 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
self,
color: Color = Color.ROBOFLOW,
thickness: int = 2,
- skeleton: Optional[Skeleton] = None,
+ skeleton: Optional[List[Tuple[int, int]]] = None,
) -> None:
"""
Draw the lines between points of the image.
@@ -83,9 +83,9 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
for xy in xy_all:
skeleton = self.skeleton
if not skeleton:
- skeleton = KnownSkeletons().get_skeleton(len(xy))
+ skeleton = resolve_skeleton_by_vertex_count(len(xy))
- for class_a, class_b in skeleton.limbs:
+ for class_a, class_b in skeleton:
xy_a = xy[class_a - 1]
xy_b = xy[class_b - 1]
missing_a = np.allclose(xy_a, 0)
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index dd8c7ec8..31ddbab7 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -1,24 +1,9 @@
-from typing import Dict, List, Tuple
+from enum import Enum
+from typing import Optional, Tuple, List
-class Skeleton:
- """
- The `Skeleton` class connects keypoints to form a skeleton.
-
- Args:
- limbs: (List[Tuple[int, int]]): List of (`class_id_1`,
- `class_id_2`) representing connected points of the skeleton.
- """
-
- limbs: List[Tuple[int, int]]
-
- def __init__(self, limbs: List[Tuple[int, int]]):
- self.limbs = limbs
-
-
-NO_SKELETON = Skeleton([])
-YOLO_V8_SKELETON = Skeleton(
- [
+class Skeleton(Enum):
+ COCO = [
(1, 2),
(1, 3),
(2, 3),
@@ -37,61 +22,18 @@ YOLO_V8_SKELETON = Skeleton(
(16, 14),
(17, 15),
]
-)
-def _make_sequential_skeleton(limb_count: int) -> Skeleton:
- """
- Create a skeleton where neighboring keypoints are connected sequentially.
-
- Args:
- limb_count (int): The number of limbs to connect.
-
- Returns:
- Skeleton: The skeleton connecting all keypoints.
- """
- limbs = []
- for i in range(0, limb_count - 1):
- limbs.append((i, i + 1))
- limbs.append((0, limb_count - 1))
- return Skeleton(limbs)
+def resolve_skeleton_by_vertex_count(count: int) -> Optional[List[Tuple[int, int]]]:
+ for skeleton in Skeleton:
+ unique_vertices = set(vertex for edge in skeleton.value for vertex in edge)
+ if len(unique_vertices) == count:
+ return skeleton.value
+ return None
-class KnownSkeletons:
- """
- Helps automatically determine which skeleton to use.
-
- The `KnownSkeletons` class contains a collection of predefined skeletons
- for various keypoint models, to be distinguished by limb counts.
-
- Example:
- # Suppose you have a model returning 15 keypoints.
- skeleton = KnownSkeletons().get_skeleton(15)
- """
-
- _instance = None
- _skeletons: Dict[int, Skeleton] = {}
-
- def __new__(cls):
- if cls._instance is None:
- cls._instance = super(KnownSkeletons, cls).__new__(cls)
- return cls._instance
-
- def add_skeleton(self, skeleton: Skeleton) -> None:
- len_limbs = len(skeleton.limbs)
- if len_limbs in self._skeletons:
- raise ValueError(f"A skeleton with {len_limbs} limbs already exists")
- self._skeletons[len_limbs] = skeleton
-
- def get_skeleton(self, limb_count: int) -> Skeleton:
- if limb_count not in self._skeletons:
- print(
- f"Warning: No skeleton found with {limb_count} limbs. "
- f"Will create one now."
- )
- self.add_skeleton(_make_sequential_skeleton(limb_count))
- return self._skeletons[limb_count]
-
-
-KnownSkeletons().add_skeleton(NO_SKELETON)
-KnownSkeletons().add_skeleton(YOLO_V8_SKELETON)
+def resolve_skeleton_by_edge_count(count: int) -> Optional[List[Tuple[int, int]]]:
+ for skeleton in Skeleton:
+ if len(skeleton.value) == count:
+ return skeleton.value
+ return None
From 9ad35bcfb9b46daa47ece8a8c815217048a15d07 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 23 Apr 2024 22:44:00 +0000
Subject: [PATCH 073/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/annotate.py | 2 +-
supervision/keypoint/skeletons.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index b3a29b4a..adae6745 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
-from typing import Optional, List, Tuple
+from typing import List, Optional, Tuple
import cv2
import numpy as np
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index 31ddbab7..d2882f1b 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -1,5 +1,5 @@
from enum import Enum
-from typing import Optional, Tuple, List
+from typing import List, Optional, Tuple
class Skeleton(Enum):
From d8681b8b4f1277b9f5da5a43b97c2561136717b2 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 24 Apr 2024 09:51:40 +0300
Subject: [PATCH 074/136] remove xy_all, turn to `for xy in keypoints.xy`
---
supervision/keypoint/annotate.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index adae6745..15f0eb4e 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -38,8 +38,7 @@ class VertexAnnotator(BaseKeyPointAnnotator):
if len(keypoints) == 0:
return scene
- xy_all = keypoints.xy
- for xy in xy_all:
+ for xy in keypoints.xy:
for x, y in xy:
cv2.circle(
img=scene,
@@ -77,10 +76,10 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
if len(keypoints) == 0:
return scene
if keypoints.class_id is None:
- raise ValueError("KeyPoints must have class_id to annotate a skeleton")
+ raise ValueError(
+ "KeyPoints must have class_id to annotate a skeleton")
- xy_all = keypoints.xy
- for xy in xy_all:
+ for xy in keypoints.xy:
skeleton = self.skeleton
if not skeleton:
skeleton = resolve_skeleton_by_vertex_count(len(xy))
From 0749efed8f38273ab03b66274091d149746953f6 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 06:52:17 +0000
Subject: [PATCH 075/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/annotate.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index 15f0eb4e..1589f02e 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -76,8 +76,7 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
if len(keypoints) == 0:
return scene
if keypoints.class_id is None:
- raise ValueError(
- "KeyPoints must have class_id to annotate a skeleton")
+ raise ValueError("KeyPoints must have class_id to annotate a skeleton")
for xy in keypoints.xy:
skeleton = self.skeleton
From 1c27bf7fea1173f2a72088036925f5802ff3b604 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 24 Apr 2024 12:44:29 +0300
Subject: [PATCH 076/136] EdgeAnnotator, warn and not crash when None
---
supervision/__init__.py | 2 +-
supervision/keypoint/annotate.py | 22 ++++++++++++----------
supervision/keypoint/skeletons.py | 3 ++-
3 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 63235df8..b716275d 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -69,7 +69,7 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
-from supervision.keypoint.annotate import SkeletonAnnotator, VertexAnnotator
+from supervision.keypoint.annotate import EdgeAnnotator, VertexAnnotator
from supervision.keypoint.core import KeyPoints
from supervision.keypoint.skeletons import Skeleton
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index 1589f02e..49c07bd0 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple
+from logging import warn
import cv2
import numpy as np
@@ -51,12 +52,12 @@ class VertexAnnotator(BaseKeyPointAnnotator):
return scene
-class SkeletonAnnotator(BaseKeyPointAnnotator):
+class EdgeAnnotator(BaseKeyPointAnnotator):
def __init__(
self,
color: Color = Color.ROBOFLOW,
thickness: int = 2,
- skeleton: Optional[List[Tuple[int, int]]] = None,
+ edges: Optional[List[Tuple[int, int]]] = None,
) -> None:
"""
Draw the lines between points of the image.
@@ -64,26 +65,27 @@ class SkeletonAnnotator(BaseKeyPointAnnotator):
Args:
color (Color, optional): The color of the lines.
thickness (int, optional): The thickness of the lines.
- skeleton (Skeleton, optional): The skeleton to draw.
+ edge (Optional[List[Tuple[int, int]]]): The edges to draw.
If set to `None`, will attempt to select automatically.
"""
self.color = color
self.thickness = thickness
- self.skeleton = skeleton
+ self.edges = edges
@convert_for_annotation_method
def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
if len(keypoints) == 0:
return scene
- if keypoints.class_id is None:
- raise ValueError("KeyPoints must have class_id to annotate a skeleton")
for xy in keypoints.xy:
- skeleton = self.skeleton
- if not skeleton:
- skeleton = resolve_skeleton_by_vertex_count(len(xy))
+ edges = self.edges
+ if not edges:
+ edges = resolve_skeleton_by_vertex_count(len(xy))
+ if not edges:
+ warn(f"No skeleton found with {len(xy)} vertices")
+ return scene
- for class_a, class_b in skeleton:
+ for class_a, class_b in edges:
xy_a = xy[class_a - 1]
xy_b = xy[class_b - 1]
missing_a = np.allclose(xy_a, 0)
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index d2882f1b..750580dd 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -26,7 +26,8 @@ class Skeleton(Enum):
def resolve_skeleton_by_vertex_count(count: int) -> Optional[List[Tuple[int, int]]]:
for skeleton in Skeleton:
- unique_vertices = set(vertex for edge in skeleton.value for vertex in edge)
+ unique_vertices = set(
+ vertex for edge in skeleton.value for vertex in edge)
if len(unique_vertices) == count:
return skeleton.value
return None
From 0f6ad236ebf304d7ebb86d3902c076edb1c000c4 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 09:45:44 +0000
Subject: [PATCH 077/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/annotate.py | 2 +-
supervision/keypoint/skeletons.py | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index 49c07bd0..b683b45f 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
-from typing import List, Optional, Tuple
from logging import warn
+from typing import List, Optional, Tuple
import cv2
import numpy as np
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index 750580dd..d2882f1b 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -26,8 +26,7 @@ class Skeleton(Enum):
def resolve_skeleton_by_vertex_count(count: int) -> Optional[List[Tuple[int, int]]]:
for skeleton in Skeleton:
- unique_vertices = set(
- vertex for edge in skeleton.value for vertex in edge)
+ unique_vertices = set(vertex for edge in skeleton.value for vertex in edge)
if len(unique_vertices) == count:
return skeleton.value
return None
From d1c103ee16742dc7e09e486412385f99fad6970a Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 24 Apr 2024 12:50:19 +0300
Subject: [PATCH 078/136] Best skeleton cache ever, obviously
---
supervision/keypoint/annotate.py | 4 ++--
supervision/keypoint/skeletons.py | 22 ++++++++++------------
2 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index b683b45f..1e7cf193 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -8,7 +8,7 @@ import numpy as np
from supervision.annotators.base import ImageType
from supervision.draw.color import Color
from supervision.keypoint.core import KeyPoints
-from supervision.keypoint.skeletons import resolve_skeleton_by_vertex_count
+from supervision.keypoint.skeletons import skeletons_by_vertex_count
from supervision.utils.conversion import convert_for_annotation_method
@@ -80,7 +80,7 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
for xy in keypoints.xy:
edges = self.edges
if not edges:
- edges = resolve_skeleton_by_vertex_count(len(xy))
+ edges = skeletons_by_vertex_count.get(len(xy))
if not edges:
warn(f"No skeleton found with {len(xy)} vertices")
return scene
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index d2882f1b..373b8fcc 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -1,5 +1,7 @@
from enum import Enum
-from typing import List, Optional, Tuple
+from typing import Dict, List, Tuple
+
+Edges = List[Tuple[int, int]]
class Skeleton(Enum):
@@ -24,16 +26,12 @@ class Skeleton(Enum):
]
-def resolve_skeleton_by_vertex_count(count: int) -> Optional[List[Tuple[int, int]]]:
- for skeleton in Skeleton:
- unique_vertices = set(vertex for edge in skeleton.value for vertex in edge)
- if len(unique_vertices) == count:
- return skeleton.value
- return None
+skeletons_by_edge_count: Dict[int, Edges] = {}
+skeletons_by_vertex_count: Dict[int, Edges] = {}
+for skeleton in Skeleton:
+ skeletons_by_edge_count[len(skeleton.value)] = skeleton.value
-def resolve_skeleton_by_edge_count(count: int) -> Optional[List[Tuple[int, int]]]:
- for skeleton in Skeleton:
- if len(skeleton.value) == count:
- return skeleton.value
- return None
+ unique_vertices = set(
+ vertex for edge in skeleton.value for vertex in edge)
+ skeletons_by_vertex_count[len(unique_vertices)] = skeleton.value
From 08d7a6b4cdc5646888ebfe032e4f1a432898380b Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 09:53:33 +0000
Subject: [PATCH 079/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/skeletons.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index 373b8fcc..1c785d78 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -32,6 +32,5 @@ skeletons_by_vertex_count: Dict[int, Edges] = {}
for skeleton in Skeleton:
skeletons_by_edge_count[len(skeleton.value)] = skeleton.value
- unique_vertices = set(
- vertex for edge in skeleton.value for vertex in edge)
+ unique_vertices = set(vertex for edge in skeleton.value for vertex in edge)
skeletons_by_vertex_count[len(unique_vertices)] = skeleton.value
From 741309fd099f47e89a748a00784b8f9c2c6d3612 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 24 Apr 2024 13:13:10 +0300
Subject: [PATCH 080/136] Remove Skeleton from __init__, small renames, consts
---
docs/keypoint/annotate.md | 2 +-
supervision/__init__.py | 1 -
supervision/keypoint/annotate.py | 4 ++--
supervision/keypoint/skeletons.py | 8 ++++----
4 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/docs/keypoint/annotate.md b/docs/keypoint/annotate.md
index 476ae918..4f9cd861 100644
--- a/docs/keypoint/annotate.md
+++ b/docs/keypoint/annotate.md
@@ -7,4 +7,4 @@ status: new
:::supervision.keypoint.annotate.VertexAnnotator
-:::supervision.keypoint.annotate.SkeletonAnnotator
+:::supervision.keypoint.annotate.EdgeAnnotator
diff --git a/supervision/__init__.py b/supervision/__init__.py
index b716275d..053e3718 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -71,7 +71,6 @@ from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
from supervision.keypoint.annotate import EdgeAnnotator, VertexAnnotator
from supervision.keypoint.core import KeyPoints
-from supervision.keypoint.skeletons import Skeleton
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
from supervision.utils.conversion import cv2_to_pillow, pillow_to_cv2
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
index 1e7cf193..b1f3530f 100644
--- a/supervision/keypoint/annotate.py
+++ b/supervision/keypoint/annotate.py
@@ -8,7 +8,7 @@ import numpy as np
from supervision.annotators.base import ImageType
from supervision.draw.color import Color
from supervision.keypoint.core import KeyPoints
-from supervision.keypoint.skeletons import skeletons_by_vertex_count
+from supervision.keypoint.skeletons import SKELETONS_BY_VERTEX_COUNT
from supervision.utils.conversion import convert_for_annotation_method
@@ -80,7 +80,7 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
for xy in keypoints.xy:
edges = self.edges
if not edges:
- edges = skeletons_by_vertex_count.get(len(xy))
+ edges = SKELETONS_BY_VERTEX_COUNT.get(len(xy))
if not edges:
warn(f"No skeleton found with {len(xy)} vertices")
return scene
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index 1c785d78..6c110854 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -26,11 +26,11 @@ class Skeleton(Enum):
]
-skeletons_by_edge_count: Dict[int, Edges] = {}
-skeletons_by_vertex_count: Dict[int, Edges] = {}
+SKELETONS_BY_EDGE_COUNT: Dict[int, Edges] = {}
+SKELETONS_BY_VERTEX_COUNT: Dict[int, Edges] = {}
for skeleton in Skeleton:
- skeletons_by_edge_count[len(skeleton.value)] = skeleton.value
+ SKELETONS_BY_EDGE_COUNT[len(skeleton.value)] = skeleton.value
unique_vertices = set(vertex for edge in skeleton.value for vertex in edge)
- skeletons_by_vertex_count[len(unique_vertices)] = skeleton.value
+ SKELETONS_BY_VERTEX_COUNT[len(unique_vertices)] = skeleton.value
From 4b89bece4fdc849a7ed7f0e1e1bf49bb1c12f17e Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 24 Apr 2024 14:58:52 +0300
Subject: [PATCH 081/136] Fix keypoint crash with no detections
---
supervision/keypoint/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index aca50ad5..6749f845 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -135,7 +135,7 @@ class KeyPoints:
keypoints = sv.KeyPoints.from_ultralytics(result)
```
"""
- if len(ultralytics_results.keypoints.xy) == 0:
+ if ultralytics_results.keypoints.xy.numel() == 0:
return cls.empty()
xy = ultralytics_results.keypoints.xy.cpu().numpy()
From 48809c13322553ab6b87bf05aa10987e472e3f08 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 24 Apr 2024 16:37:41 +0300
Subject: [PATCH 082/136] Add a basic from_supervision function to KeyPoints
---
supervision/keypoint/core.py | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 6749f845..87f0a20b 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -110,6 +110,37 @@ class KeyPoints:
]
)
+ @classmethod
+ def from_inference(cls, inference_result: dict) -> KeyPoints:
+ if not inference_result.get("predictions"):
+ return cls.empty()
+
+ xy = []
+ confidence = []
+ class_id = []
+ class_names = []
+
+ for prediction in inference_result["predictions"]:
+ prediction_xy = []
+ prediction_confidence = []
+ for keypoint in prediction["keypoints"]:
+ prediction_xy.append([keypoint["x"], keypoint["y"]])
+ prediction_confidence.append(keypoint["confidence"])
+ xy.append(prediction_xy)
+ confidence.append(prediction_confidence)
+
+ class_id.append(prediction["class_id"])
+ class_names.append(prediction["class"])
+
+ data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
+
+ return cls(
+ xy=np.array(xy, dtype=np.float32),
+ confidence=np.array(confidence, dtype=np.float32),
+ class_id=np.array(class_id, dtype=int),
+ data=data,
+ )
+
@classmethod
def from_ultralytics(cls, ultralytics_results) -> KeyPoints:
"""
From 77793571b094f646eaec2bebd99369e203b87159 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 15:39:08 +0200
Subject: [PATCH 083/136] major docs refactor
---
README.md | 7 +-
docs/deprecated.md | 2 +-
docs/{ => detection}/annotators.md | 0
docs/detection/metrics.md | 21 +++
docs/draw/color.md | 13 --
docs/how_to/detect_and_annotate.md | 28 +--
docs/how_to/detect_small_objects.md | 16 +-
docs/keypoint/annotate.md | 10 --
docs/keypoint/annotators.md | 10 ++
docs/metrics/detection.md | 22 ---
docs/{draw/utils.md => utils/draw.md} | 13 ++
docs/{geometry/core.md => utils/geometry.md} | 0
mkdocs.yml | 22 +--
supervision/__init__.py | 2 +-
supervision/assets/list.py | 5 +
supervision/detection/core.py | 127 ++++++++------
supervision/keypoint/annotate.py | 104 ------------
supervision/keypoint/annotators.py | 170 +++++++++++++++++++
supervision/keypoint/core.py | 43 ++---
19 files changed, 348 insertions(+), 267 deletions(-)
rename docs/{ => detection}/annotators.md (100%)
create mode 100644 docs/detection/metrics.md
delete mode 100644 docs/draw/color.md
delete mode 100644 docs/keypoint/annotate.md
create mode 100644 docs/keypoint/annotators.md
delete mode 100644 docs/metrics/detection.md
rename docs/{draw/utils.md => utils/draw.md} (83%)
rename docs/{geometry/core.md => utils/geometry.md} (100%)
delete mode 100644 supervision/keypoint/annotate.py
create mode 100644 supervision/keypoint/annotators.py
diff --git a/README.md b/README.md
index beed655c..0894a6a4 100644
--- a/README.md
+++ b/README.md
@@ -71,16 +71,15 @@ len(detections)
```python
import cv2
import supervision as sv
- from inference.models.utils import get_roboflow_model
+ from inference import get_model
image = cv2.imread(...)
- model = get_roboflow_model(model_id="yolov8s-640", api_key=)
+ model = get_model(model_id="yolov8s-640", api_key=)
result = model.infer(image)[0]
detections = sv.Detections.from_inference(result)
len(detections)
- # 5
-
+ # 5
```
diff --git a/docs/deprecated.md b/docs/deprecated.md
index 35c332e4..cc1e9649 100644
--- a/docs/deprecated.md
+++ b/docs/deprecated.md
@@ -12,7 +12,7 @@ These features are phased out due to better alternatives or potential issues in
- The method `Color.green()` is deprecated and will be removed in `supervision-0.22.0`. Use the constant `Color.GREEN` instead.
- The method `Color.blue()` is deprecated and will be removed in `supervision-0.22.0`. Use the constant `Color.BLUE` instead.
- The method [`ColorPalette.default()`](draw/color.md/#supervision.draw.color.ColorPalette.default) is deprecated and will be removed in `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](draw/color.md/#supervision.draw.color.ColorPalette.DEFAULT) instead.
-- `BoxAnnotator` is deprecated and will be removed in `supervision-0.22.0`. Use [`BoundingBoxAnnotator`](annotators.md/#supervision.annotators.core.BoundingBoxAnnotator) and [`LabelAnnotator`](annotators.md/#supervision.annotators.core.LabelAnnotator) instead.
+- `BoxAnnotator` is deprecated and will be removed in `supervision-0.22.0`. Use [`BoundingBoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoundingBoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead.
- The method [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) is deprecated and will be removed in `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
- The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByterTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
- The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) is deprecated and will be removed in `supervision-0.23.0`. Use `triggering_anchors ` instead.
diff --git a/docs/annotators.md b/docs/detection/annotators.md
similarity index 100%
rename from docs/annotators.md
rename to docs/detection/annotators.md
diff --git a/docs/detection/metrics.md b/docs/detection/metrics.md
new file mode 100644
index 00000000..981f399c
--- /dev/null
+++ b/docs/detection/metrics.md
@@ -0,0 +1,21 @@
+---
+comments: true
+---
+
+# Metrics
+
+!!! warning
+
+ Evaluation API is still fluid and may change. If you use Evaluation API in your project until further notice, freeze the `supervision` version in your `requirements.txt` or `setup.py`.
+
+
+
+:::supervision.metrics.detection.ConfusionMatrix
+
+
+
+:::supervision.metrics.detection.MeanAveragePrecision
diff --git a/docs/draw/color.md b/docs/draw/color.md
deleted file mode 100644
index c702814a..00000000
--- a/docs/draw/color.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-comments: true
----
-
-# Color
-
-:::supervision.draw.color.Color
-
-
-
ColorPalette
-
-
-:::supervision.draw.color.ColorPalette
diff --git a/docs/how_to/detect_and_annotate.md b/docs/how_to/detect_and_annotate.md
index adea95cf..a9a4405e 100644
--- a/docs/how_to/detect_and_annotate.md
+++ b/docs/how_to/detect_and_annotate.md
@@ -27,7 +27,7 @@ model.
from inference import get_model
model = get_model(model_id="yolov8n-640")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
```
@@ -38,7 +38,7 @@ model.
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image)[0]
```
@@ -52,7 +52,7 @@ model.
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
- image = Image.open()
+ image = Image.open()
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
@@ -78,7 +78,7 @@ Now that we have predictions from a model, we can load them into Supervision.
from inference import get_model
model = get_model(model_id="yolov8n-640")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
```
@@ -93,7 +93,7 @@ Now that we have predictions from a model, we can load them into Supervision.
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
```
@@ -111,7 +111,7 @@ Now that we have predictions from a model, we can load them into Supervision.
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
- image = Image.open()
+ image = Image.open()
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
@@ -146,7 +146,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi
from inference import get_model
model = get_model(model_id="yolov8n-640")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
@@ -167,7 +167,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
@@ -191,7 +191,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
- image = Image.open()
+ image = Image.open()
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
@@ -230,7 +230,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
from inference import get_model
model = get_model(model_id="yolov8n-640")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
@@ -257,7 +257,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
@@ -287,7 +287,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
- image = Image.open()
+ image = Image.open()
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
@@ -334,7 +334,7 @@ that will allow you to draw masks instead of boxes.
from inference import get_model
model = get_model(model_id="yolov8n-seg-640")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
@@ -355,7 +355,7 @@ that will allow you to draw masks instead of boxes.
from ultralytics import YOLO
model = YOLO("yolov8n-seg.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
diff --git a/docs/how_to/detect_small_objects.md b/docs/how_to/detect_small_objects.md
index 9f35a96c..e2d02328 100644
--- a/docs/how_to/detect_small_objects.md
+++ b/docs/how_to/detect_small_objects.md
@@ -28,7 +28,7 @@ size relative to the image resolution.
from inference import get_model
model = get_model(model_id="yolov8x-640")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
@@ -49,7 +49,7 @@ size relative to the image resolution.
from ultralytics import YOLO
model = YOLO("yolov8x.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
@@ -73,7 +73,7 @@ size relative to the image resolution.
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
- image = Image.open()
+ image = Image.open()
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
@@ -116,7 +116,7 @@ is less effective for ultra-high-resolution images (4K and above).
from inference import get_model
model = get_model(model_id="yolov8x-1280")
- image = cv2.imread()
+ image = cv2.imread()
results = model.infer(image)[0]
detections = sv.Detections.from_inference(results)
@@ -137,7 +137,7 @@ is less effective for ultra-high-resolution images (4K and above).
from ultralytics import YOLO
model = YOLO("yolov8x.pt")
- image = cv2.imread()
+ image = cv2.imread()
results = model(image, imgsz=1280)[0]
detections = sv.Detections.from_ultralytics(results)
@@ -171,7 +171,7 @@ objects within each, and aggregating the results.
from inference import get_model
model = get_model(model_id="yolov8x-640")
- image = cv2.imread()
+ image = cv2.imread()
def callback(image_slice: np.ndarray) -> sv.Detections:
results = model.infer(image_slice)[0]
@@ -198,7 +198,7 @@ objects within each, and aggregating the results.
from ultralytics import YOLO
model = YOLO("yolov8x.pt")
- image = cv2.imread()
+ image = cv2.imread()
def callback(image_slice: np.ndarray) -> sv.Detections:
result = model(image_slice)[0]
@@ -229,7 +229,7 @@ objects within each, and aggregating the results.
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
- image = cv2.imread()
+ image = cv2.imread()
def callback(image_slice: np.ndarray) -> sv.Detections:
image_slice = cv2.cvtColor(image_slice, cv2.COLOR_BGR2RGB)
diff --git a/docs/keypoint/annotate.md b/docs/keypoint/annotate.md
deleted file mode 100644
index 4f9cd861..00000000
--- a/docs/keypoint/annotate.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-comments: true
-status: new
----
-
-# Annotate
-
-:::supervision.keypoint.annotate.VertexAnnotator
-
-:::supervision.keypoint.annotate.EdgeAnnotator
diff --git a/docs/keypoint/annotators.md b/docs/keypoint/annotators.md
new file mode 100644
index 00000000..3efc5b03
--- /dev/null
+++ b/docs/keypoint/annotators.md
@@ -0,0 +1,10 @@
+---
+comments: true
+status: new
+---
+
+# Annotators
+
+:::supervision.keypoint.annotators.VertexAnnotator
+
+:::supervision.keypoint.annotators.EdgeAnnotator
diff --git a/docs/metrics/detection.md b/docs/metrics/detection.md
deleted file mode 100644
index a953ba9b..00000000
--- a/docs/metrics/detection.md
+++ /dev/null
@@ -1,22 +0,0 @@
----
-comments: true
----
-
-# Detection Metrics
-
-!!! warning
-
- Evaluation API is still fluid and may change. If you use Evaluation API in your project until further notice, freeze the
- `supervision` version in your `requirements.txt` or `setup.py`.
-
-
-
-:::supervision.metrics.detection.ConfusionMatrix
-
-
-
-:::supervision.metrics.detection.MeanAveragePrecision
diff --git a/docs/draw/utils.md b/docs/utils/draw.md
similarity index 83%
rename from docs/draw/utils.md
rename to docs/utils/draw.md
index 7975952c..7b47a6a7 100644
--- a/docs/draw/utils.md
+++ b/docs/utils/draw.md
@@ -51,3 +51,16 @@ comments: true
:::supervision.draw.utils.calculate_optimal_line_thickness
+
+
+
Color
+
+
+:::supervision.draw.color.Color
+
+
+
ColorPalette
+
+
+:::supervision.draw.color.ColorPalette
+
diff --git a/docs/geometry/core.md b/docs/utils/geometry.md
similarity index 100%
rename from docs/geometry/core.md
rename to docs/utils/geometry.md
diff --git a/mkdocs.yml b/mkdocs.yml
index 256c6bef..cf206a82 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -44,15 +44,16 @@ nav:
- Track Objects: how_to/track_objects.md
- API:
- - Annotators: annotators.md
- - Classifications:
- - Core: classification/core.md
- - Detections:
+ - Detection and Segmentation:
- Core: detection/core.md
+ - Annotators: detection/annotators.md
+ - Metrics: detection/metrics.md
- Utils: detection/utils.md
- - KeyPoints:
+ - Keypoint Detection:
- Core: keypoint/core.md
- - Annotators: keypoint/annotate.md
+ - Annotators: keypoint/annotators.md
+ - Classification:
+ - Core: classification/core.md
- Tools:
- Line Zone: detection/tools/line_zone.md
- Polygon Zone: detection/tools/polygon_zone.md
@@ -61,19 +62,14 @@ nav:
- Save Detections: detection/tools/save_detections.md
- Trackers: trackers.md
- Datasets: datasets.md
- - Metrics:
- - Object Detection: metrics/detection.md
- - Draw:
- - Color: draw/color.md
- - Utils: draw/utils.md
- - Geometry:
- - Position: geometry/core.md
- Utils:
- Video: utils/video.md
- Image: utils/image.md
- Iterables: utils/iterables.md
- Notebook: utils/notebook.md
- File: utils/file.md
+ - Draw: utils/draw.md
+ - Geometry: utils/geometry.md
- Assets: assets.md
- Cookbooks: cookbooks.md
- Contribute:
diff --git a/supervision/__init__.py b/supervision/__init__.py
index 053e3718..bb526514 100644
--- a/supervision/__init__.py
+++ b/supervision/__init__.py
@@ -69,7 +69,7 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
-from supervision.keypoint.annotate import EdgeAnnotator, VertexAnnotator
+from supervision.keypoint.annotators import EdgeAnnotator, VertexAnnotator
from supervision.keypoint.core import KeyPoints
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
diff --git a/supervision/assets/list.py b/supervision/assets/list.py
index 77b1e461..0866431a 100644
--- a/supervision/assets/list.py
+++ b/supervision/assets/list.py
@@ -29,6 +29,7 @@ class VideoAssets(Enum):
MARKET_SQUARE = "market-square.mp4"
PEOPLE_WALKING = "people-walking.mp4"
BEACH = "beach-1.mp4"
+ BASKETBALL = "basketball-1.mp4"
@classmethod
def list(cls):
@@ -68,4 +69,8 @@ VIDEO_ASSETS: Dict[str, Tuple[str, str]] = {
f"{BASE_VIDEO_URL}{VideoAssets.BEACH.value}",
"4175d42fec4d450ed081523fd39e0cf8",
),
+ VideoAssets.BASKETBALL.value: (
+ f"{BASE_VIDEO_URL}{VideoAssets.BASKETBALL.value}",
+ "60d94a3c7c47d16f09d342b088012ecc",
+ ),
}
diff --git a/supervision/detection/core.py b/supervision/detection/core.py
index 53286375..3e3d0114 100644
--- a/supervision/detection/core.py
+++ b/supervision/detection/core.py
@@ -27,34 +27,68 @@ from supervision.validators import validate_detections_fields
@dataclass
class Detections:
"""
- The `sv.Detections` allows you to convert results from a variety of object detection
- and segmentation models into a single, unified format. The `sv.Detections` class
- enables easy data manipulation and filtering, and provides a consistent API for
- Supervision's tools like trackers, annotators, and zones.
+ The `sv.Detections` class in the Supervision library standardizes results from
+ various object detection and segmentation models into a consistent format. This
+ class simplifies data manipulation and filtering, providing a uniform API for
+ integration with Supervision [trackers](/trackers/), [annotators](/detection/annotators/), and [tools](/detection/tools/line_zone/).
- ```python
- import cv2
- import supervision as sv
- from ultralytics import YOLO
+ === "Inference"
- image = cv2.imread()
- model = YOLO('yolov8s.pt')
- annotator = sv.BoundingBoxAnnotator()
+ Use [`sv.Detections.from_inference`](/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
- result = model(image)[0]
- detections = sv.Detections.from_ultralytics(result)
+ ```python
+ import cv2
+ import supervision as sv
+ from inference import get_model
- annotated_image = annotator.annotate(image, detections)
- ```
+ model = get_model(model_id="yolov8n-640")
+ image = cv2.imread()
+ results = model.infer(image)[0]
+ detections = sv.Detections.from_inference(results)
+ ```
- !!! tip
+ === "Ultralytics"
- In `sv.Detections`, detection data is categorized into two main field types:
- fixed and custom. The fixed fields include `xyxy`, `mask`, `confidence`,
- `class_id`, and `tracker_id`. For any additional data requirements, custom
- fields come into play, stored in the data field. These custom fields are easily
- accessible using the `detections[]` syntax, providing flexibility
- for diverse data handling needs.
+ Use [`sv.Detections.from_ultralytics`](/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
+
+ ```python
+ import cv2
+ import supervision as sv
+ from ultralytics import YOLO
+
+ model = YOLO("yolov8n.pt")
+ image = cv2.imread()
+ results = model(image)[0]
+ detections = sv.Detections.from_ultralytics(results)
+ ```
+
+ === "Transformers"
+
+ Use [`sv.Detections.from_transformers`](/detection/core/#supervision.detection.core.Detections.from_transformers) method, which accepts model results from both detection and segmentation models.
+
+ ```python
+ import torch
+ import supervision as sv
+ from PIL import Image
+ from transformers import DetrImageProcessor, DetrForObjectDetection
+
+ processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+
+ image = Image.open()
+ inputs = processor(images=image, return_tensors="pt")
+
+ with torch.no_grad():
+ outputs = model(**inputs)
+
+ width, height = image.size
+ target_size = torch.tensor([[height, width]])
+ results = processor.post_process_object_detection(
+ outputs=outputs, target_sizes=target_size)[0]
+ detections = sv.Detections.from_transformers(
+ transformers_results=results,
+ id2label=model.config.id2label)
+ ```
Attributes:
xyxy (np.ndarray): An array of shape `(n, 4)` containing
@@ -70,14 +104,6 @@ class Detections:
data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
data where each key is a string representing the data type, and the value
is either a NumPy array or a list of corresponding data.
-
- !!! warning
-
- The `data` field in the `sv.Detections` class is currently in an experimental
- phase. Please be aware that its API and functionality are subject to change in
- future updates as we continue to refine and improve its capabilities.
- We encourage users to experiment with this feature and provide feedback, but
- also to be prepared for potential modifications in upcoming releases.
"""
xyxy: np.ndarray
@@ -177,8 +203,8 @@ class Detections:
@classmethod
def from_ultralytics(cls, ultralytics_results) -> Detections:
"""
- Creates a Detections instance from a
- [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
+ Creates a `sv.Detections` instance from a
+ [YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
!!! Note
@@ -202,10 +228,13 @@ class Detections:
image = cv2.imread()
model = YOLO('yolov8s.pt')
-
- result = model(image)[0]
- detections = sv.Detections.from_ultralytics(result)
+ results = model(image)[0]
+ detections = sv.Detections.from_ultralytics(results)
```
+
+ !!! tip
+
+ Class names values can be accessed using `detections["class_name"]`.
""" # noqa: E501 // docs
if "obb" in ultralytics_results and ultralytics_results.obb is not None:
@@ -396,11 +425,6 @@ class Detections:
Creates a Detections instance from object detection or segmentation
[Transformer](https://github.com/huggingface/transformers) inference result.
- !!! note
-
- Class names can be accessed using the key `class_name` in the returned
- object's data attribute.
-
Args:
transformers_results (dict): The output of Transformers model inference. A
dictionary containing the `scores`, `labels`, `boxes` and `masks` keys.
@@ -437,6 +461,10 @@ class Detections:
id2label=model.config.id2label
)
```
+
+ !!! tip
+
+ Class names values can be accessed using `detections["class_name"]`.
""" # noqa: E501 // docs
class_ids = transformers_results["labels"].cpu().detach().numpy().astype(int)
@@ -510,17 +538,12 @@ class Detections:
@classmethod
def from_inference(cls, roboflow_result: Union[dict, Any]) -> Detections:
"""
- Create a Detections object from the [Roboflow](https://roboflow.com/)
+ Create a `sv.Detections` object from the [Roboflow](https://roboflow.com/)
API inference result or the [Inference](https://inference.roboflow.com/)
package results. This method extracts bounding boxes, class IDs,
confidences, and class names from the Roboflow API result and encapsulates
them into a Detections object.
- !!! note
-
- Class names can be accessed using the key `class_name` in the returned
- object's data attribute.
-
Args:
roboflow_result (dict, any): The result from the
Roboflow API or Inference package containing predictions.
@@ -533,14 +556,18 @@ class Detections:
```python
import cv2
import supervision as sv
- from inference.models.utils import get_roboflow_model
+ from inference import get_model
image = cv2.imread()
- model = get_roboflow_model(model_id="yolov8s-640")
+ model = get_model(model_id="yolov8s-640")
result = model.infer(image)[0]
detections = sv.Detections.from_inference(result)
```
+
+ !!! tip
+
+ Class names values can be accessed using `detections["class_name"]`.
"""
with suppress(AttributeError):
roboflow_result = roboflow_result.dict(exclude_none=True, by_alias=True)
@@ -590,10 +617,10 @@ class Detections:
```python
import cv2
import supervision as sv
- from inference.models.utils import get_roboflow_model
+ from inference import get_model
image = cv2.imread()
- model = get_roboflow_model(model_id="yolov8s-640")
+ model = get_model(model_id="yolov8s-640")
result = model.infer(image)[0]
detections = sv.Detections.from_roboflow(result)
diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py
deleted file mode 100644
index b1f3530f..00000000
--- a/supervision/keypoint/annotate.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from abc import ABC, abstractmethod
-from logging import warn
-from typing import List, Optional, Tuple
-
-import cv2
-import numpy as np
-
-from supervision.annotators.base import ImageType
-from supervision.draw.color import Color
-from supervision.keypoint.core import KeyPoints
-from supervision.keypoint.skeletons import SKELETONS_BY_VERTEX_COUNT
-from supervision.utils.conversion import convert_for_annotation_method
-
-
-class BaseKeyPointAnnotator(ABC):
- @abstractmethod
- def annotate(self, scene: ImageType, detections: KeyPoints) -> ImageType:
- pass
-
-
-class VertexAnnotator(BaseKeyPointAnnotator):
- def __init__(
- self,
- color: Color = Color.ROBOFLOW,
- radius: int = 4,
- ) -> None:
- """
- Most basic keypoint annotator.
-
- Args:
- color (Color, optional): The color of the keypoint.
- radius (int, optional): The radius of the keypoint.
- """
- self.color = color
- self.radius = radius
-
- @convert_for_annotation_method
- def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
- if len(keypoints) == 0:
- return scene
-
- for xy in keypoints.xy:
- for x, y in xy:
- cv2.circle(
- img=scene,
- center=(int(x), int(y)),
- radius=self.radius,
- color=self.color.as_bgr(),
- thickness=-1,
- )
-
- return scene
-
-
-class EdgeAnnotator(BaseKeyPointAnnotator):
- def __init__(
- self,
- color: Color = Color.ROBOFLOW,
- thickness: int = 2,
- edges: Optional[List[Tuple[int, int]]] = None,
- ) -> None:
- """
- Draw the lines between points of the image.
-
- Args:
- color (Color, optional): The color of the lines.
- thickness (int, optional): The thickness of the lines.
- edge (Optional[List[Tuple[int, int]]]): The edges to draw.
- If set to `None`, will attempt to select automatically.
- """
- self.color = color
- self.thickness = thickness
- self.edges = edges
-
- @convert_for_annotation_method
- def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType:
- if len(keypoints) == 0:
- return scene
-
- for xy in keypoints.xy:
- edges = self.edges
- if not edges:
- edges = SKELETONS_BY_VERTEX_COUNT.get(len(xy))
- if not edges:
- warn(f"No skeleton found with {len(xy)} vertices")
- return scene
-
- for class_a, class_b in edges:
- xy_a = xy[class_a - 1]
- xy_b = xy[class_b - 1]
- missing_a = np.allclose(xy_a, 0)
- missing_b = np.allclose(xy_b, 0)
- if missing_a or missing_b:
- continue
-
- cv2.line(
- img=scene,
- pt1=(int(xy_a[0]), int(xy_a[1])),
- pt2=(int(xy_b[0]), int(xy_b[1])),
- color=self.color.as_bgr(),
- thickness=self.thickness,
- )
-
- return scene
diff --git a/supervision/keypoint/annotators.py b/supervision/keypoint/annotators.py
new file mode 100644
index 00000000..5cadc377
--- /dev/null
+++ b/supervision/keypoint/annotators.py
@@ -0,0 +1,170 @@
+from abc import ABC, abstractmethod
+from logging import warn
+from typing import List, Optional, Tuple
+
+import cv2
+import numpy as np
+
+from supervision.annotators.base import ImageType
+from supervision.draw.color import Color
+from supervision.keypoint.core import KeyPoints
+from supervision.keypoint.skeletons import SKELETONS_BY_VERTEX_COUNT
+from supervision.utils.conversion import convert_for_annotation_method
+
+
+class BaseKeyPointAnnotator(ABC):
+ @abstractmethod
+ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
+ pass
+
+
+class VertexAnnotator(BaseKeyPointAnnotator):
+ """
+ A class that specializes in drawing skeleton vertices on images. It uses
+ specified key points to determine the locations where the vertices should be
+ drawn.
+ """
+
+ def __init__(
+ self,
+ color: Color = Color.ROBOFLOW,
+ radius: int = 4,
+ ) -> None:
+ """
+ Args:
+ color (Color, optional): The color to use for annotating key points.
+ radius (int, optional): The radius of the circles used to represent the key
+ points.
+ """
+ self.color = color
+ self.radius = radius
+
+ @convert_for_annotation_method
+ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
+ """
+ Annotates the given scene with skeleton vertices based on the provided key
+ points. It draws circles at each key point location.
+
+ Args:
+ scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
+ is a flexible type, accepting either `numpy.ndarray` or
+ `PIL.Image.Image`.
+ key_points (KeyPoints): A collection of key points where each key point
+ consists of x and y coordinates.
+
+ Returns:
+ The annotated image, matching the type of `scene` (`numpy.ndarray`
+ or `PIL.Image.Image`)
+
+ Example:
+ ```python
+ import supervision as sv
+
+ image = ...
+ key_points = sv.KeyPoints(...)
+
+ vertex_annotator = sv.VertexAnnotator()
+ annotated_frame = vertex_annotator.annotate(
+ scene=image.copy(),
+ key_points=key_points
+ )
+ ```
+ """
+ if len(key_points) == 0:
+ return scene
+
+ for xy in key_points.xy:
+ for x, y in xy:
+ cv2.circle(
+ img=scene,
+ center=(int(x), int(y)),
+ radius=self.radius,
+ color=self.color.as_bgr(),
+ thickness=-1,
+ )
+
+ return scene
+
+
+class EdgeAnnotator(BaseKeyPointAnnotator):
+ """
+ A class that specializes in drawing skeleton edges on images using specified key
+ points. It connects key points with lines to form the skeleton structure.
+ """
+ def __init__(
+ self,
+ color: Color = Color.ROBOFLOW,
+ thickness: int = 2,
+ edges: Optional[List[Tuple[int, int]]] = None,
+ ) -> None:
+ """
+ Args:
+ color (Color, optional): The color to use for the edges.
+ thickness (int, optional): The thickness of the edges.
+ edges (Optional[List[Tuple[int, int]]]): The edges to draw.
+ If set to `None`, will attempt to select automatically.
+ """
+ self.color = color
+ self.thickness = thickness
+ self.edges = edges
+
+ @convert_for_annotation_method
+ def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
+ """
+ Annotates the given scene by drawing lines between specified key points to form
+ edges.
+
+ Args:
+ scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
+ is a flexible type, accepting either `numpy.ndarray` or
+ `PIL.Image.Image`.
+ key_points (KeyPoints): A collection of key points where each key point
+ consists of x and y coordinates.
+
+ Returns:
+ Returns:
+ The annotated image, matching the type of `scene` (`numpy.ndarray`
+ or `PIL.Image.Image`)
+
+ Example:
+ ```python
+ import supervision as sv
+
+ image = ...
+ key_points = sv.KeyPoints(...)
+
+ edge_annotator = sv.EdgeAnnotator()
+ annotated_frame = edge_annotator.annotate(
+ scene=image.copy(),
+ key_points=key_points
+ )
+ ```
+ """
+ if len(key_points) == 0:
+ return scene
+
+ for xy in key_points.xy:
+ edges = self.edges
+ if not edges:
+ edges = SKELETONS_BY_VERTEX_COUNT.get(len(xy))
+ if not edges:
+ warn(f"No skeleton found with {len(xy)} vertices")
+ return scene
+
+ for class_a, class_b in edges:
+ xy_a = xy[class_a - 1]
+ xy_b = xy[class_b - 1]
+ missing_a = np.allclose(xy_a, 0)
+ missing_b = np.allclose(xy_b, 0)
+ if missing_a or missing_b:
+ continue
+
+ cv2.line(
+ img=scene,
+ pt1=(int(xy_a[0]), int(xy_a[1])),
+ pt2=(int(xy_b[0]), int(xy_b[1])),
+ color=self.color.as_bgr(),
+ thickness=self.thickness,
+ )
+
+ return scene
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 6749f845..b44d3b7c 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -14,28 +14,25 @@ from supervision.validators import validate_keypoints_fields
@dataclass
class KeyPoints:
"""
- The `sv.KeyPoints` allows you to convert results from a variety of keypoint
- keypoints models into a single, unified format.
+ The `sv.KeyPoints` class in the Supervision library standardizes results from
+ various keypoint detection and pose estimation models into a consistent format. This
+ class simplifies data manipulation and filtering, providing a uniform API for
+ integration with Supervision annotators.
- ```python
- import cv2
- import supervision as sv
- from ultralytics import YOLO
+ === "Ultralytics"
- image = cv2.imread()
- model = YOLO('yolov8s-pose.pt')
- result = model(image)[0]
- keypoints = sv.KeyPoints.from_ultralytics(result)
- ```
+ Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics) method, which accepts model results.
- !!! tip
+ ```python
+ import cv2
+ import supervision as sv
+ from ultralytics import YOLO
- In `sv.KeyPoints`, detection data is categorized into two main field types:
- fixed and custom. The fixed fields include `xy`, `confidence`,
- `class_id`. For any additional data requirements, custom
- fields come into play, stored in the data field. These custom fields are easily
- accessible using the `keypoints[]` syntax, providing flexibility
- for diverse data handling needs.
+ image = cv2.imread()
+ model = YOLO('yolov8s-pose.pt')
+ result = model(image)[0]
+ key_points = sv.KeyPoints.from_ultralytics(result)
+ ```
Attributes:
xy (np.ndarray): An array of shape `(n, 2)` containing
@@ -47,14 +44,6 @@ class KeyPoints:
data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
data where each key is a string representing the data type, and the value
is either a NumPy array or a list of corresponding data.
-
- !!! warning
-
- The `data` field in the `sv.KeyPoints` class is currently in an experimental
- phase. Please be aware that its API and functionality are subject to change in
- future updates as we continue to refine and improve its capabilities.
- We encourage users to experiment with this feature and provide feedback, but
- also to be prepared for potential modifications in upcoming releases.
"""
xy: npt.NDArray[np.float32]
@@ -232,7 +221,7 @@ class KeyPoints:
Create an empty Keypoints object with no keypoints.
Returns:
- (Keypoints): An empty Keypoints object.
+ (KeyPoints): An empty Keypoints object.
Example:
```python
From 389453777ec94dab77e8d6e474907314ef5bbb55 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 16:18:21 +0200
Subject: [PATCH 084/136] vertex and edge annotators visualizations added
---
docs/assets.md | 4 +--
docs/detection/metrics.md | 8 ++---
docs/detection/utils.md | 22 ++++++-------
docs/keypoint/annotators.md | 50 ++++++++++++++++++++++++++++++
supervision/assets/list.py | 1 +
supervision/keypoint/annotators.py | 10 ++++--
6 files changed, 74 insertions(+), 21 deletions(-)
diff --git a/docs/assets.md b/docs/assets.md
index 7e0d7dff..3fd293ee 100644
--- a/docs/assets.md
+++ b/docs/assets.md
@@ -19,13 +19,13 @@ as an extra within the Supervision package.
```
:::supervision.assets.downloader.download_assets
:::supervision.assets.list.VideoAssets
diff --git a/docs/detection/metrics.md b/docs/detection/metrics.md
index 981f399c..a68f0f96 100644
--- a/docs/detection/metrics.md
+++ b/docs/detection/metrics.md
@@ -4,18 +4,14 @@ comments: true
# Metrics
-!!! warning
-
- Evaluation API is still fluid and may change. If you use Evaluation API in your project until further notice, freeze the `supervision` version in your `requirements.txt` or `setup.py`.
-
:::supervision.metrics.detection.ConfusionMatrix
:::supervision.metrics.detection.MeanAveragePrecision
diff --git a/docs/detection/utils.md b/docs/detection/utils.md
index cc4eff5d..abacdc21 100644
--- a/docs/detection/utils.md
+++ b/docs/detection/utils.md
@@ -6,67 +6,67 @@ status: new
# Detection Utils
:::supervision.detection.utils.box_iou_batch
:::supervision.detection.utils.mask_iou_batch
-
box_non_max_suppression
+
:::supervision.detection.utils.box_non_max_suppression
-
mask_non_max_suppression
+
:::supervision.detection.utils.mask_non_max_suppression
:::supervision.detection.utils.polygon_to_mask
:::supervision.detection.utils.mask_to_xyxy
:::supervision.detection.utils.mask_to_polygons
:::supervision.detection.utils.polygon_to_xyxy
-
filter_polygons_by_area
+
:::supervision.detection.utils.filter_polygons_by_area
:::supervision.detection.utils.move_boxes
:::supervision.detection.utils.scale_boxes
diff --git a/docs/keypoint/annotators.md b/docs/keypoint/annotators.md
index 3efc5b03..b5f998bc 100644
--- a/docs/keypoint/annotators.md
+++ b/docs/keypoint/annotators.md
@@ -5,6 +5,56 @@ status: new
# Annotators
+=== "VertexAnnotator"
+
+ ```python
+ import supervision as sv
+
+ image = ...
+ key_points = sv.KeyPoints(...)
+
+ vertex_annotator = sv.VertexAnnotator(color=sv.Color.GREEN, radius=10)
+ annotated_frame = vertex_annotator.annotate(
+ scene=image.copy(),
+ key_points=key_points
+ )
+ ```
+
+
+
+ { align=center width="800" }
+
+
+
+=== "EdgeAnnotator"
+
+ ```python
+ import supervision as sv
+
+ image = ...
+ key_points = sv.KeyPoints(...)
+
+ edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5)
+ annotated_frame = edge_annotator.annotate(
+ scene=image.copy(),
+ key_points=key_points
+ )
+ ```
+
+
+
+ { align=center width="800" }
+
+
+
+
+
:::supervision.keypoint.annotators.VertexAnnotator
+
+
:::supervision.keypoint.annotators.EdgeAnnotator
diff --git a/supervision/assets/list.py b/supervision/assets/list.py
index 0866431a..8a01b758 100644
--- a/supervision/assets/list.py
+++ b/supervision/assets/list.py
@@ -19,6 +19,7 @@ class VideoAssets(Enum):
| `MARKET_SQUARE` | `market-square.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/market-square.mp4) |
| `PEOPLE_WALKING` | `people-walking.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/people-walking.mp4) |
| `BEACH` | `beach-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/beach-1.mp4) |
+ | `BASKETBALL` | `basketball-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/basketball-1.mp4) |
""" # noqa: E501 // docs
VEHICLES = "vehicles.mp4"
diff --git a/supervision/keypoint/annotators.py b/supervision/keypoint/annotators.py
index 5cadc377..4d6df72c 100644
--- a/supervision/keypoint/annotators.py
+++ b/supervision/keypoint/annotators.py
@@ -63,12 +63,15 @@ class VertexAnnotator(BaseKeyPointAnnotator):
image = ...
key_points = sv.KeyPoints(...)
- vertex_annotator = sv.VertexAnnotator()
+ vertex_annotator = sv.VertexAnnotator(color=sv.Color.GREEN, radius=10)
annotated_frame = vertex_annotator.annotate(
scene=image.copy(),
key_points=key_points
)
```
+
+ 
"""
if len(key_points) == 0:
return scene
@@ -133,12 +136,15 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
image = ...
key_points = sv.KeyPoints(...)
- edge_annotator = sv.EdgeAnnotator()
+ edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5)
annotated_frame = edge_annotator.annotate(
scene=image.copy(),
key_points=key_points
)
```
+
+ 
"""
if len(key_points) == 0:
return scene
From 908f3d14c6d0dd622211434c23bfd63a3e2fa720 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 16:28:48 +0200
Subject: [PATCH 085/136] improved docs linking
---
docs/utils/image.md | 12 ++++++------
docs/utils/iterables.md | 4 ++--
docs/utils/notebook.md | 5 ++---
docs/utils/video.md | 10 +++++-----
4 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/docs/utils/image.md b/docs/utils/image.md
index 87fd1943..8f170d35 100644
--- a/docs/utils/image.md
+++ b/docs/utils/image.md
@@ -6,37 +6,37 @@ status: new
# Image Utils
:::supervision.utils.image.crop_image
:::supervision.utils.image.scale_image
:::supervision.utils.image.resize_image
:::supervision.utils.image.letterbox_image
:::supervision.utils.image.overlay_image
:::supervision.utils.image.ImageSink
diff --git a/docs/utils/iterables.md b/docs/utils/iterables.md
index 3ccf98e5..b65cd954 100644
--- a/docs/utils/iterables.md
+++ b/docs/utils/iterables.md
@@ -6,13 +6,13 @@ status: new
# Iterables Utils
:::supervision.utils.iterables.create_batches
:::supervision.utils.iterables.fill
diff --git a/docs/utils/notebook.md b/docs/utils/notebook.md
index d09b9bbd..3eab046a 100644
--- a/docs/utils/notebook.md
+++ b/docs/utils/notebook.md
@@ -5,14 +5,13 @@ comments: true
# Notebooks Utils
:::supervision.utils.notebook.plot_image
:::supervision.utils.notebook.plot_images_grid
diff --git a/docs/utils/video.md b/docs/utils/video.md
index 1f58450f..f9a5821d 100644
--- a/docs/utils/video.md
+++ b/docs/utils/video.md
@@ -5,31 +5,31 @@ comments: true
# Video Utils
:::supervision.utils.video.VideoInfo
:::supervision.utils.video.VideoSink
:::supervision.utils.video.FPSMonitor
-
get_video_frames_generator
+
:::supervision.utils.video.get_video_frames_generator
:::supervision.utils.video.process_video
From 1c42838ca1c529532af7583d7979d647e04a1045 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 16:35:34 +0200
Subject: [PATCH 086/136] improved docs linking
---
docs/utils/draw.md | 21 ++++++++++-----------
docs/utils/file.md | 2 +-
docs/utils/geometry.md | 4 +++-
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/docs/utils/draw.md b/docs/utils/draw.md
index 7b47a6a7..84758e06 100644
--- a/docs/utils/draw.md
+++ b/docs/utils/draw.md
@@ -5,62 +5,61 @@ comments: true
# Draw Utils
:::supervision.draw.utils.draw_line
:::supervision.draw.utils.draw_rectangle
-
draw_filled_rectangle
+
:::supervision.draw.utils.draw_filled_rectangle
:::supervision.draw.utils.draw_polygon
:::supervision.draw.utils.draw_text
:::supervision.draw.utils.draw_image
-
calculate_optimal_font_scale
+
:::supervision.draw.utils.calculate_optimal_text_scale
-
calculate_optimal_line_thickness
+
:::supervision.draw.utils.calculate_optimal_line_thickness
:::supervision.draw.color.Color
:::supervision.draw.color.ColorPalette
-
diff --git a/docs/utils/file.md b/docs/utils/file.md
index 2ba5a015..a2a08185 100644
--- a/docs/utils/file.md
+++ b/docs/utils/file.md
@@ -5,7 +5,7 @@ comments: true
# File Utils
-
list_files_with_extensions
+
:::supervision.utils.file.list_files_with_extensions
diff --git a/docs/utils/geometry.md b/docs/utils/geometry.md
index ca99b626..8c92f562 100644
--- a/docs/utils/geometry.md
+++ b/docs/utils/geometry.md
@@ -2,6 +2,8 @@
comments: true
---
-# Position
+
:::supervision.geometry.core.Position
From 263420758e6b509c069f29005fd7d1b75e2d8817 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 14:36:15 +0000
Subject: [PATCH 087/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/detection/core.py | 4 ++--
supervision/keypoint/annotators.py | 5 +++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/supervision/detection/core.py b/supervision/detection/core.py
index 3e3d0114..8df11eca 100644
--- a/supervision/detection/core.py
+++ b/supervision/detection/core.py
@@ -231,7 +231,7 @@ class Detections:
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
```
-
+
!!! tip
Class names values can be accessed using `detections["class_name"]`.
@@ -461,7 +461,7 @@ class Detections:
id2label=model.config.id2label
)
```
-
+
!!! tip
Class names values can be accessed using `detections["class_name"]`.
diff --git a/supervision/keypoint/annotators.py b/supervision/keypoint/annotators.py
index 4d6df72c..4b43765c 100644
--- a/supervision/keypoint/annotators.py
+++ b/supervision/keypoint/annotators.py
@@ -91,9 +91,10 @@ class VertexAnnotator(BaseKeyPointAnnotator):
class EdgeAnnotator(BaseKeyPointAnnotator):
"""
- A class that specializes in drawing skeleton edges on images using specified key
- points. It connects key points with lines to form the skeleton structure.
+ A class that specializes in drawing skeleton edges on images using specified key
+ points. It connects key points with lines to form the skeleton structure.
"""
+
def __init__(
self,
color: Color = Color.ROBOFLOW,
From 895562d444959e140bf8f82a3e8dd4b2a40c4d5e Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 16:37:41 +0200
Subject: [PATCH 088/136] make pre-commit happy
---
supervision/detection/core.py | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/supervision/detection/core.py b/supervision/detection/core.py
index 3e3d0114..b1ded4d6 100644
--- a/supervision/detection/core.py
+++ b/supervision/detection/core.py
@@ -34,7 +34,8 @@ class Detections:
=== "Inference"
- Use [`sv.Detections.from_inference`](/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
+ Use [`sv.Detections.from_inference`](/detection/core/#supervision.detection.core.Detections.from_inference)
+ method, which accepts model results from both detection and segmentation models.
```python
import cv2
@@ -49,7 +50,8 @@ class Detections:
=== "Ultralytics"
- Use [`sv.Detections.from_ultralytics`](/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
+ Use [`sv.Detections.from_ultralytics`](/detection/core/#supervision.detection.core.Detections.from_ultralytics)
+ method, which accepts model results from both detection and segmentation models.
```python
import cv2
@@ -64,7 +66,8 @@ class Detections:
=== "Transformers"
- Use [`sv.Detections.from_transformers`](/detection/core/#supervision.detection.core.Detections.from_transformers) method, which accepts model results from both detection and segmentation models.
+ Use [`sv.Detections.from_transformers`](/detection/core/#supervision.detection.core.Detections.from_transformers)
+ method, which accepts model results from both detection and segmentation models.
```python
import torch
@@ -104,7 +107,7 @@ class Detections:
data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
data where each key is a string representing the data type, and the value
is either a NumPy array or a list of corresponding data.
- """
+ """ # noqa: E501 // docs
xyxy: np.ndarray
mask: Optional[np.ndarray] = None
From 383c1a1d8d0fae16fac712667551b01ed4695d5f Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 14:39:01 +0000
Subject: [PATCH 089/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/detection/core.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/supervision/detection/core.py b/supervision/detection/core.py
index f305385d..1900954d 100644
--- a/supervision/detection/core.py
+++ b/supervision/detection/core.py
@@ -34,7 +34,7 @@ class Detections:
=== "Inference"
- Use [`sv.Detections.from_inference`](/detection/core/#supervision.detection.core.Detections.from_inference)
+ Use [`sv.Detections.from_inference`](/detection/core/#supervision.detection.core.Detections.from_inference)
method, which accepts model results from both detection and segmentation models.
```python
@@ -50,7 +50,7 @@ class Detections:
=== "Ultralytics"
- Use [`sv.Detections.from_ultralytics`](/detection/core/#supervision.detection.core.Detections.from_ultralytics)
+ Use [`sv.Detections.from_ultralytics`](/detection/core/#supervision.detection.core.Detections.from_ultralytics)
method, which accepts model results from both detection and segmentation models.
```python
@@ -66,7 +66,7 @@ class Detections:
=== "Transformers"
- Use [`sv.Detections.from_transformers`](/detection/core/#supervision.detection.core.Detections.from_transformers)
+ Use [`sv.Detections.from_transformers`](/detection/core/#supervision.detection.core.Detections.from_transformers)
method, which accepts model results from both detection and segmentation models.
```python
From d7da00c55b8dc47034f5f4e49d0493fa1a98c1b9 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 16:43:40 +0200
Subject: [PATCH 090/136] make pre-commit happy
---
supervision/keypoint/core.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index b44d3b7c..ed5d698e 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -21,7 +21,8 @@ class KeyPoints:
=== "Ultralytics"
- Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics) method, which accepts model results.
+ Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics)
+ method, which accepts model results.
```python
import cv2
@@ -44,7 +45,7 @@ class KeyPoints:
data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional
data where each key is a string representing the data type, and the value
is either a NumPy array or a list of corresponding data.
- """
+ """ # noqa: E501 // docs
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
From f4fca3339ded4c33379a6609b0f8c441ac4e6808 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 14:45:18 +0000
Subject: [PATCH 091/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index ed5d698e..8a97b51c 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -21,7 +21,7 @@ class KeyPoints:
=== "Ultralytics"
- Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics)
+ Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics)
method, which accepts model results.
```python
From 7a90eefd5b63f7aafba3adf90a839178e3a8684b Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 19:22:59 +0200
Subject: [PATCH 092/136] changelog updated version bump from `0.20.0rc2` to
`0.20.0`
---
docs/changelog.md | 65 +++++++++++++++++++++++++++++++
docs/deprecated.md | 1 +
docs/utils/geometry.md | 6 +++
pyproject.toml | 2 +-
supervision/detection/annotate.py | 8 ++--
supervision/geometry/utils.py | 15 +++----
6 files changed, 82 insertions(+), 15 deletions(-)
diff --git a/docs/changelog.md b/docs/changelog.md
index 2c5d7ae1..f8d301cd 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,3 +1,68 @@
+### 0.20.0 April 24, 2024
+
+- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.KeyPoints`](/0.20.0/keypoint/core/#supervision.keypoint.core.KeyPoints) to provide initial support for pose estimation and broader keypoint detection models.
+
+- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.EdgeAnnotator`](/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator) and [`sv.VertexAnnotator`](/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.VertexAnnotator) to enable rendering of results from keypoint detection models.
+
+```python
+import cv2
+import supervision as sv
+from ultralytics import YOLO
+
+image = cv2.imread()
+model = YOLO('yolov8l-pose')
+
+result = model(image, verbose=False)[0]
+keypoints = sv.KeyPoints.from_ultralytics(result)
+
+edge_annotators = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5)
+annotated_image = edge_annotators.annotate(image.copy(), keypoints)
+```
+
+- Changed [#1037](https://github.com/roboflow/supervision/pull/1037): [`sv.LabelAnnotator`](/0.20.0/annotators/#supervision.annotators.core.LabelAnnotator) by adding an additional `corner_radius` argument that allows for rounding the corners of the bounding box.
+
+- Changed [#1109](https://github.com/roboflow/supervision/pull/1109): [`sv.PolygonZone`](/0.20.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) such that the `frame_resolution_wh` argument is no longer required to initialize `sv.PolygonZone`.
+
+!!! failure "Deprecated"
+
+ The `frame_resolution_wh` parameter in `sv.PolygonZone` is deprecated and will be removed in `supervision-0.24.0`.
+
+- Changed [#1084](https://github.com/roboflow/supervision/pull/1084): [`sv.get_polygon_center`](/0.20.0/utils/geometry/#supervision.geometry.core.utils.get_polygon_center) to calculate a more accurate polygon centroid.
+
+- Changed [#1069](https://github.com/roboflow/supervision/pull/1069): [`sv.Detections.from_transformers`](/0.20.0/detection/core/#supervision.detection.core.Detections.from_transformers) by adding support for Transformers segmentation models and extract class names values.
+
+```python
+import torch
+import supervision as sv
+from PIL import Image
+from transformers import DetrImageProcessor, DetrForSegmentation
+
+processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50-panoptic")
+model = DetrForSegmentation.from_pretrained("facebook/detr-resnet-50-panoptic")
+
+image = Image.open()
+inputs = processor(images=image, return_tensors="pt")
+
+with torch.no_grad():
+ outputs = model(**inputs)
+
+width, height = image.size
+target_size = torch.tensor([[height, width]])
+results = processor.post_process_segmentation(
+ outputs=outputs, target_sizes=target_size)[0]
+detections = sv.Detections.from_transformers(results, id2label=model.config.id2label)
+
+mask_annotator = sv.MaskAnnotator()
+label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
+
+annotated_image = mask_annotator.annotate(
+ scene=image, detections=detections)
+annotated_image = label_annotator.annotate(
+ scene=annotated_image, detections=detections)
+```
+
+- Fixed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack.update_with_detections`](/0.20.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack.update_with_detections) which was removing segmentation masks while tracking. Now, `ByteTrack` can be used alongside segmentation models.
+
### 0.19.0 March 15, 2024
- Added [#818](https://github.com/roboflow/supervision/pull/818): [`sv.CSVSink`](/0.19.0/detection/tools/save_detections/#supervision.detection.tools.csv_sink.CSVSink) allowing for the straightforward saving of image, video, or stream inference results in a `.csv` file.
diff --git a/docs/deprecated.md b/docs/deprecated.md
index cc1e9649..5834b05f 100644
--- a/docs/deprecated.md
+++ b/docs/deprecated.md
@@ -16,3 +16,4 @@ These features are phased out due to better alternatives or potential issues in
- The method [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) is deprecated and will be removed in `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
- The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByterTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
- The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) is deprecated and will be removed in `supervision-0.23.0`. Use `triggering_anchors ` instead.
+- The `frame_resolution_wh ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) is deprecated and will be removed in `supervision-0.24.0`.
diff --git a/docs/utils/geometry.md b/docs/utils/geometry.md
index 8c92f562..d79bcfeb 100644
--- a/docs/utils/geometry.md
+++ b/docs/utils/geometry.md
@@ -2,6 +2,12 @@
comments: true
---
+
+
+:::supervision.geometry.utils.get_polygon_center
+
diff --git a/pyproject.toml b/pyproject.toml
index 7be53b6c..de0df748 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
-version = "0.20.0rc2"
+version = "0.20.0"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
authors = ["Piotr Skalski "]
maintainers = ["Piotr Skalski "]
diff --git a/supervision/detection/annotate.py b/supervision/detection/annotate.py
index f32c7344..f496b248 100644
--- a/supervision/detection/annotate.py
+++ b/supervision/detection/annotate.py
@@ -9,10 +9,6 @@ from supervision.utils.conversion import convert_for_annotation_method
from supervision.utils.internal import deprecated
-@deprecated(
- "`BoxAnnotator` is deprecated and will be removed in "
- "`supervision-0.22.0`. Use `BoundingBoxAnnotator` and `LabelAnnotator` instead"
-)
class BoxAnnotator:
"""
A class for drawing bounding boxes on an image using detections provided.
@@ -46,6 +42,10 @@ class BoxAnnotator:
self.text_thickness: int = text_thickness
self.text_padding: int = text_padding
+ @deprecated(
+ "`BoxAnnotator` is deprecated and will be removed in "
+ "`supervision-0.22.0`. Use `BoundingBoxAnnotator` and `LabelAnnotator` instead"
+ )
@convert_for_annotation_method
def annotate(
self,
diff --git a/supervision/geometry/utils.py b/supervision/geometry/utils.py
index 9cf717e4..8a0ca35c 100644
--- a/supervision/geometry/utils.py
+++ b/supervision/geometry/utils.py
@@ -5,12 +5,7 @@ from supervision.geometry.core import Point
def get_polygon_center(polygon: np.ndarray) -> Point:
"""
- Calculate the center of a polygon.
-
- This function takes in a polygon as a 2-dimensional numpy ndarray and
- returns the center of the polygon as a Point object.
-
- The center is calculated as the center
+ Calculate the center of a polygon. The center is calculated as the center
of the solid figure formed by the points of the polygon
Parameters:
@@ -23,12 +18,12 @@ def get_polygon_center(polygon: np.ndarray) -> Point:
Examples:
```python
- from supervision.geometry.utils import get_polygon_center
import numpy as np
+ import supervision as sv
- vertices = np.array([[0, 0], [0, 2], [2, 2], [2, 0]])
- get_polygon_center(vertices)
- Point(x=1, y=1)
+ polygon = np.array([[0, 0], [0, 2], [2, 2], [2, 0]])
+ sv.get_polygon_center(polygon=polygon)
+ # Point(x=1, y=1)
```
"""
From 6ae99444b543d005c2b48104b90f826c37cfa02d Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 24 Apr 2024 17:24:16 +0000
Subject: [PATCH 093/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/changelog.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/changelog.md b/docs/changelog.md
index f8d301cd..47d160aa 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,6 +1,6 @@
### 0.20.0 April 24, 2024
-- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.KeyPoints`](/0.20.0/keypoint/core/#supervision.keypoint.core.KeyPoints) to provide initial support for pose estimation and broader keypoint detection models.
+- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.KeyPoints`](/0.20.0/keypoint/core/#supervision.keypoint.core.KeyPoints) to provide initial support for pose estimation and broader keypoint detection models.
- Added [#1128](https://github.com/roboflow/supervision/pull/1128): [`sv.EdgeAnnotator`](/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator) and [`sv.VertexAnnotator`](/0.20.0/keypoint/annotators/#supervision.keypoint.annotators.VertexAnnotator) to enable rendering of results from keypoint detection models.
From 5c4ed2650e0a6b1e2ef3d3de36b3c9ae0fafb5f6 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 19:25:45 +0200
Subject: [PATCH 094/136] drop accidentally committed `from_inference`
---
supervision/keypoint/core.py | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 7019aa95..8a97b51c 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -100,37 +100,6 @@ class KeyPoints:
]
)
- @classmethod
- def from_inference(cls, inference_result: dict) -> KeyPoints:
- if not inference_result.get("predictions"):
- return cls.empty()
-
- xy = []
- confidence = []
- class_id = []
- class_names = []
-
- for prediction in inference_result["predictions"]:
- prediction_xy = []
- prediction_confidence = []
- for keypoint in prediction["keypoints"]:
- prediction_xy.append([keypoint["x"], keypoint["y"]])
- prediction_confidence.append(keypoint["confidence"])
- xy.append(prediction_xy)
- confidence.append(prediction_confidence)
-
- class_id.append(prediction["class_id"])
- class_names.append(prediction["class"])
-
- data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
-
- return cls(
- xy=np.array(xy, dtype=np.float32),
- confidence=np.array(confidence, dtype=np.float32),
- class_id=np.array(class_id, dtype=int),
- data=data,
- )
-
@classmethod
def from_ultralytics(cls, ultralytics_results) -> KeyPoints:
"""
From a45341ce63d98d982ce7e975ba6df94a5468cdb0 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Wed, 24 Apr 2024 22:41:59 +0200
Subject: [PATCH 095/136] final cleanup; setting `supervision>=0.20.0` in
`requirements.txt` and refactoring stream processing scripts
---
examples/count_people_in_zone/requirements.txt | 2 +-
examples/speed_estimation/requirements.txt | 2 +-
.../time_in_zone/inference_stream_example.py | 17 +++++++----------
examples/time_in_zone/requirements.txt | 2 +-
.../time_in_zone/ultralytics_stream_example.py | 17 +++++++----------
examples/traffic_analysis/requirements.txt | 2 +-
6 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/examples/count_people_in_zone/requirements.txt b/examples/count_people_in_zone/requirements.txt
index d9e27264..114447fc 100644
--- a/examples/count_people_in_zone/requirements.txt
+++ b/examples/count_people_in_zone/requirements.txt
@@ -1,5 +1,5 @@
gdown
inference
-supervision==0.19.0
+supervision>=0.20.0
tqdm
ultralytics
diff --git a/examples/speed_estimation/requirements.txt b/examples/speed_estimation/requirements.txt
index 36de970d..af2e1c13 100644
--- a/examples/speed_estimation/requirements.txt
+++ b/examples/speed_estimation/requirements.txt
@@ -1,4 +1,4 @@
-supervision==0.19.0
+supervision>=0.20.0
tqdm==4.66.1
requests
ultralytics==8.0.237
diff --git a/examples/time_in_zone/inference_stream_example.py b/examples/time_in_zone/inference_stream_example.py
index 604aade4..0dfdf660 100644
--- a/examples/time_in_zone/inference_stream_example.py
+++ b/examples/time_in_zone/inference_stream_example.py
@@ -24,18 +24,15 @@ class CustomSink:
self.fps_monitor = sv.FPSMonitor()
self.polygons = load_zones_config(file_path=zone_configuration_path)
self.timers = [ClockBasedTimer() for _ in self.polygons]
- self.zones = None
+ self.zones = [
+ sv.PolygonZone(
+ polygon=polygon,
+ triggering_anchors=(sv.Position.CENTER,),
+ )
+ for polygon in self.polygons
+ ]
def on_prediction(self, result: dict, frame: VideoFrame) -> None:
- if self.zones is None:
- self.zones = [
- sv.PolygonZone(
- polygon=polygon,
- triggering_anchors=(sv.Position.CENTER,),
- )
- for polygon in self.polygons
- ]
-
self.fps_monitor.tick()
fps = self.fps_monitor.fps
diff --git a/examples/time_in_zone/requirements.txt b/examples/time_in_zone/requirements.txt
index fa17b986..752b4a8f 100644
--- a/examples/time_in_zone/requirements.txt
+++ b/examples/time_in_zone/requirements.txt
@@ -1,5 +1,5 @@
opencv-python
-supervision
+supervision>=0.20.0
ultralytics
inference
pytube
diff --git a/examples/time_in_zone/ultralytics_stream_example.py b/examples/time_in_zone/ultralytics_stream_example.py
index 6262d594..8b8ecd60 100644
--- a/examples/time_in_zone/ultralytics_stream_example.py
+++ b/examples/time_in_zone/ultralytics_stream_example.py
@@ -25,18 +25,15 @@ class CustomSink:
self.fps_monitor = sv.FPSMonitor()
self.polygons = load_zones_config(file_path=zone_configuration_path)
self.timers = [ClockBasedTimer() for _ in self.polygons]
- self.zones = None
+ self.zones = [
+ sv.PolygonZone(
+ polygon=polygon,
+ triggering_anchors=(sv.Position.CENTER,),
+ )
+ for polygon in self.polygons
+ ]
def on_prediction(self, detections: sv.Detections, frame: VideoFrame) -> None:
- if self.zones is None:
- self.zones = [
- sv.PolygonZone(
- polygon=polygon,
- triggering_anchors=(sv.Position.CENTER,),
- )
- for polygon in self.polygons
- ]
-
self.fps_monitor.tick()
fps = self.fps_monitor.fps
diff --git a/examples/traffic_analysis/requirements.txt b/examples/traffic_analysis/requirements.txt
index 6e72dd55..114447fc 100644
--- a/examples/traffic_analysis/requirements.txt
+++ b/examples/traffic_analysis/requirements.txt
@@ -1,5 +1,5 @@
gdown
inference
-supervision>=0.19.0
+supervision>=0.20.0
tqdm
ultralytics
From e253f86e8f2a68fbed76d273092d6529972076e9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Apr 2024 00:46:24 +0000
Subject: [PATCH 096/136] :arrow_up: Bump mypy from 1.9.0 to 1.10.0
Bumps [mypy](https://github.com/python/mypy) from 1.9.0 to 1.10.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/1.9.0...v1.10.0)
---
updated-dependencies:
- dependency-name: mypy
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 56 ++++++++++++++++++++++++++---------------------------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 95cbef01..e279ed9a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2277,38 +2277,38 @@ files = [
[[package]]
name = "mypy"
-version = "1.9.0"
+version = "1.10.0"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
- {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
- {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
- {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
- {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
- {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
- {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
- {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
- {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
- {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
- {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
- {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
- {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
- {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
- {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
- {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
- {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
- {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
- {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
- {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
- {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
- {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
- {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
- {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
- {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
- {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
- {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
+ {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
+ {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
+ {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
+ {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
+ {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
+ {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
+ {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
+ {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
+ {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
+ {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
+ {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
+ {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
+ {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
+ {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
+ {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
+ {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
+ {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
]
[package.dependencies]
From cab06f2c2b424e547b7c4e80f142b0ba61aeb70d Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Thu, 25 Apr 2024 12:29:08 +0300
Subject: [PATCH 097/136] Implementation of from_yolo_nas for keypoints
* Missing batch image support
---
supervision/keypoint/core.py | 55 ++++++++++++++++++++++++++++---
supervision/keypoint/skeletons.py | 24 ++++++++++++++
2 files changed, 74 insertions(+), 5 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 8a97b51c..587d459c 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,7 +50,8 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]
+ ] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -103,7 +104,7 @@ class KeyPoints:
@classmethod
def from_ultralytics(cls, ultralytics_results) -> KeyPoints:
"""
- Creates a Keypoints instance from a
+ Creates a KeyPoints instance from a
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
Args:
@@ -111,7 +112,7 @@ class KeyPoints:
The output Results instance from YOLOv8
Returns:
- KeyPoints: A new Keypoints object.
+ KeyPoints: A new KeyPoints object.
Example:
```python
@@ -130,12 +131,56 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i] for i in class_id])
+ class_names = np.array([ultralytics_results.names[i]
+ for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
return cls(xy, class_id, confidence, data)
+ @classmethod
+ def from_yolo_nas(cls, yolo_nas_results) -> KeyPoints:
+ """
+ Create a KeyPoints instance from a YOLO NAS results.
+
+ Args:
+ yolo_nas_results (ImagePoseEstimationPrediction): The output object from YOLO NAS.
+
+ Returns:
+ KeyPoints: A new KeyPoints object.
+
+ Example:
+ ```python
+ import cv2
+ import supervision as sv
+ import super_gradients
+
+ image = cv2.imread()
+
+ yolo_nas = super_gradients.training.models.get(
+ "yolo_nas_pose_s", pretrained_weights="coco_pose").to("cuda")
+
+ results = yolo_nas.predict(image, conf=0.1)
+ keypoints = sv.KeyPoints.from_yolo_nas(results)
+ ```
+ """
+ if len(yolo_nas_results.prediction.poses) == 0:
+ return cls.empty()
+
+ # TODO: multi-image input
+
+ xy = yolo_nas_results.prediction.poses[:, :, :2]
+ confidence = yolo_nas_results.prediction.poses[:, :, 2]
+ class_id = [0] * len(xy)
+ data = {CLASS_NAME_DATA_FIELD: [f"person" for _ in xy]}
+
+ return cls(
+ xy=xy,
+ confidence=confidence,
+ class_id=np.array(class_id),
+ data=data,
+ )
+
def __getitem__(
self, index: Union[int, slice, List[int], np.ndarray, str]
) -> Union["KeyPoints", List, np.ndarray, None]:
@@ -216,7 +261,7 @@ class KeyPoints:
self.data[key] = value
- @classmethod
+ @ classmethod
def empty(cls) -> KeyPoints:
"""
Create an empty Keypoints object with no keypoints.
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index 6c110854..01f768b1 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -25,6 +25,30 @@ class Skeleton(Enum):
(17, 15),
]
+ # Hardcoding here, but it also comes within model results
+ # Note: the classes in the model are 0-indexed, while the skeleton is 1-indexed
+ YOLO_NAS = [
+ (0, 1),
+ (0, 2),
+ (1, 2),
+ (1, 3),
+ (2, 4),
+ (3, 5),
+ (4, 6),
+ (5, 6),
+ (5, 7),
+ (5, 11),
+ (6, 8),
+ (6, 12),
+ (7, 9),
+ (8, 10),
+ (11, 12),
+ (11, 13),
+ (12, 14),
+ (13, 15),
+ (14, 16)
+ ]
+
SKELETONS_BY_EDGE_COUNT: Dict[int, Edges] = {}
SKELETONS_BY_VERTEX_COUNT: Dict[int, Edges] = {}
From 18b3c17ed423649ff933826f72ed68c44b15b89a Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Thu, 25 Apr 2024 12:42:33 +0300
Subject: [PATCH 098/136] Styling
---
supervision/keypoint/core.py | 13 ++++++-------
supervision/keypoint/skeletons.py | 2 +-
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 587d459c..2f2e9aa4 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,8 +50,7 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]
- ] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -131,8 +130,7 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i]
- for i in class_id])
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
@@ -144,7 +142,8 @@ class KeyPoints:
Create a KeyPoints instance from a YOLO NAS results.
Args:
- yolo_nas_results (ImagePoseEstimationPrediction): The output object from YOLO NAS.
+ yolo_nas_results (ImagePoseEstimationPrediction):
+ The output object from YOLO NAS.
Returns:
KeyPoints: A new KeyPoints object.
@@ -172,7 +171,7 @@ class KeyPoints:
xy = yolo_nas_results.prediction.poses[:, :, :2]
confidence = yolo_nas_results.prediction.poses[:, :, 2]
class_id = [0] * len(xy)
- data = {CLASS_NAME_DATA_FIELD: [f"person" for _ in xy]}
+ data = {CLASS_NAME_DATA_FIELD: ["person" for _ in xy]}
return cls(
xy=xy,
@@ -261,7 +260,7 @@ class KeyPoints:
self.data[key] = value
- @ classmethod
+ @classmethod
def empty(cls) -> KeyPoints:
"""
Create an empty Keypoints object with no keypoints.
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index 01f768b1..beb11281 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -46,7 +46,7 @@ class Skeleton(Enum):
(11, 13),
(12, 14),
(13, 15),
- (14, 16)
+ (14, 16),
]
From c8c2ff5c454f36d5caf4823e9c680aa1bc8c8ca5 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Thu, 25 Apr 2024 12:53:09 +0300
Subject: [PATCH 099/136] Minor styling
---
supervision/keypoint/core.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 2f2e9aa4..968ee3d4 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -166,8 +166,6 @@ class KeyPoints:
if len(yolo_nas_results.prediction.poses) == 0:
return cls.empty()
- # TODO: multi-image input
-
xy = yolo_nas_results.prediction.poses[:, :, :2]
confidence = yolo_nas_results.prediction.poses[:, :, 2]
class_id = [0] * len(xy)
From 399342513ab321c8b800aa41b566643ec5bd6b6d Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Thu, 25 Apr 2024 15:00:01 +0300
Subject: [PATCH 100/136] Keypoints: rm NAS skeleton, improve docs examples
---
supervision/keypoint/core.py | 12 ++++++++----
supervision/keypoint/skeletons.py | 24 ------------------------
2 files changed, 8 insertions(+), 28 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 968ee3d4..cc02f84b 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,7 +50,8 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]
+ ] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -130,7 +131,8 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i] for i in class_id])
+ class_names = np.array([ultralytics_results.names[i]
+ for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
@@ -151,13 +153,15 @@ class KeyPoints:
Example:
```python
import cv2
+ import torch
import supervision as sv
import super_gradients
image = cv2.imread()
+ device = "cuda" if torch.cuda.is_available() else "cpu"
yolo_nas = super_gradients.training.models.get(
- "yolo_nas_pose_s", pretrained_weights="coco_pose").to("cuda")
+ "yolo_nas_pose_s", pretrained_weights="coco_pose").to(device)
results = yolo_nas.predict(image, conf=0.1)
keypoints = sv.KeyPoints.from_yolo_nas(results)
@@ -180,7 +184,7 @@ class KeyPoints:
def __getitem__(
self, index: Union[int, slice, List[int], np.ndarray, str]
- ) -> Union["KeyPoints", List, np.ndarray, None]:
+ ) -> Union[KeyPoints, List, np.ndarray, None]:
"""
Get a subset of the KeyPoints object or access an item from its data field.
diff --git a/supervision/keypoint/skeletons.py b/supervision/keypoint/skeletons.py
index beb11281..6c110854 100644
--- a/supervision/keypoint/skeletons.py
+++ b/supervision/keypoint/skeletons.py
@@ -25,30 +25,6 @@ class Skeleton(Enum):
(17, 15),
]
- # Hardcoding here, but it also comes within model results
- # Note: the classes in the model are 0-indexed, while the skeleton is 1-indexed
- YOLO_NAS = [
- (0, 1),
- (0, 2),
- (1, 2),
- (1, 3),
- (2, 4),
- (3, 5),
- (4, 6),
- (5, 6),
- (5, 7),
- (5, 11),
- (6, 8),
- (6, 12),
- (7, 9),
- (8, 10),
- (11, 12),
- (11, 13),
- (12, 14),
- (13, 15),
- (14, 16),
- ]
-
SKELETONS_BY_EDGE_COUNT: Dict[int, Edges] = {}
SKELETONS_BY_VERTEX_COUNT: Dict[int, Edges] = {}
From 4bd593fe3c0376bd57aa974be3e8da919268f33c Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Thu, 25 Apr 2024 12:02:20 +0000
Subject: [PATCH 101/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/core.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index cc02f84b..4790acd7 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,8 +50,7 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]
- ] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -131,8 +130,7 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i]
- for i in class_id])
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
From 059583e2ba1d8cab60308a5801ce8a32ea8b5a47 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 26 Apr 2024 00:31:25 +0000
Subject: [PATCH 102/136] :arrow_up: Bump mkdocs-material from 9.5.18 to 9.5.19
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.18 to 9.5.19.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.18...9.5.19)
---
updated-dependencies:
- dependency-name: mkdocs-material
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 41 +++++++++++++++++++++++++++++------------
1 file changed, 29 insertions(+), 12 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index e279ed9a..f9411209 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2083,34 +2083,34 @@ files = [
[[package]]
name = "mkdocs"
-version = "1.5.3"
+version = "1.6.0"
description = "Project documentation with Markdown."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"},
- {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"},
+ {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"},
+ {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"},
]
[package.dependencies]
click = ">=7.0"
colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""}
ghp-import = ">=1.0"
-importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
+importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
jinja2 = ">=2.11.1"
-markdown = ">=3.2.1"
+markdown = ">=3.3.6"
markupsafe = ">=2.0.1"
mergedeep = ">=1.3.4"
+mkdocs-get-deps = ">=0.2.0"
packaging = ">=20.5"
pathspec = ">=0.11.1"
-platformdirs = ">=2.2.0"
pyyaml = ">=5.1"
pyyaml-env-tag = ">=0.1"
watchdog = ">=2.0"
[package.extras]
i18n = ["babel (>=2.9.0)"]
-min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"]
+min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
[[package]]
name = "mkdocs-autorefs"
@@ -2127,6 +2127,23 @@ files = [
Markdown = ">=3.3"
mkdocs = ">=1.1"
+[[package]]
+name = "mkdocs-get-deps"
+version = "0.2.0"
+description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"},
+ {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
+mergedeep = ">=1.3.4"
+platformdirs = ">=2.2.0"
+pyyaml = ">=5.1"
+
[[package]]
name = "mkdocs-git-committers-plugin-2"
version = "2.3.0"
@@ -2180,13 +2197,13 @@ pygments = ">2.12.0"
[[package]]
name = "mkdocs-material"
-version = "9.5.18"
+version = "9.5.19"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.18-py3-none-any.whl", hash = "sha256:1e0e27fc9fe239f9064318acf548771a4629d5fd5dfd45444fd80a953fe21eb4"},
- {file = "mkdocs_material-9.5.18.tar.gz", hash = "sha256:a43f470947053fa2405c33995f282d24992c752a50114f23f30da9d8d0c57e62"},
+ {file = "mkdocs_material-9.5.19-py3-none-any.whl", hash = "sha256:ea96e150b6c95f5e4ffe47d78bb712c7bacdd91d2a0bec47f46b6fa0705a86ec"},
+ {file = "mkdocs_material-9.5.19.tar.gz", hash = "sha256:7473e06e17e23af608a30ef583fdde8f36389dd3ef56b1d503eed54c89c9618c"},
]
[package.dependencies]
@@ -2195,7 +2212,7 @@ cairosvg = {version = ">=2.6,<3.0", optional = true, markers = "extra == \"imagi
colorama = ">=0.4,<1.0"
jinja2 = ">=3.0,<4.0"
markdown = ">=3.2,<4.0"
-mkdocs = ">=1.5.3,<1.6.0"
+mkdocs = ">=1.6,<2.0"
mkdocs-material-extensions = ">=1.3,<2.0"
paginate = ">=0.5,<1.0"
pillow = {version = ">=10.2,<11.0", optional = true, markers = "extra == \"imaging\""}
From 8436f52f8a1d02f7ff7c0251ee2d1b3962684981 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 26 Apr 2024 00:33:10 +0000
Subject: [PATCH 103/136] :arrow_up: Bump ruff from 0.4.1 to 0.4.2
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.1...v0.4.2)
---
updated-dependencies:
- dependency-name: ruff
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index e279ed9a..ebe49f10 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3643,28 +3643,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.4.1"
+version = "0.4.2"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2d9ef6231e3fbdc0b8c72404a1a0c46fd0dcea84efca83beb4681c318ea6a953"},
- {file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9485f54a7189e6f7433e0058cf8581bee45c31a25cd69009d2a040d1bd4bfaef"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2921ac03ce1383e360e8a95442ffb0d757a6a7ddd9a5be68561a671e0e5807e"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eec8d185fe193ad053eda3a6be23069e0c8ba8c5d20bc5ace6e3b9e37d246d3f"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa27d9d72a94574d250f42b7640b3bd2edc4c58ac8ac2778a8c82374bb27984"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f1ee41580bff1a651339eb3337c20c12f4037f6110a36ae4a2d864c52e5ef954"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0926cefb57fc5fced629603fbd1a23d458b25418681d96823992ba975f050c2b"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6e37f2e3cd74496a74af9a4fa67b547ab3ca137688c484749189bf3a686ceb"},
- {file = "ruff-0.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd703a5975ac1998c2cc5e9494e13b28f31e66c616b0a76e206de2562e0843c"},
- {file = "ruff-0.4.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b92f03b4aa9fa23e1799b40f15f8b95cdc418782a567d6c43def65e1bbb7f1cf"},
- {file = "ruff-0.4.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c859f294f8633889e7d77de228b203eb0e9a03071b72b5989d89a0cf98ee262"},
- {file = "ruff-0.4.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b34510141e393519a47f2d7b8216fec747ea1f2c81e85f076e9f2910588d4b64"},
- {file = "ruff-0.4.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e68d248ed688b9d69fd4d18737edcbb79c98b251bba5a2b031ce2470224bdf9"},
- {file = "ruff-0.4.1-py3-none-win32.whl", hash = "sha256:b90506f3d6d1f41f43f9b7b5ff845aeefabed6d2494307bc7b178360a8805252"},
- {file = "ruff-0.4.1-py3-none-win_amd64.whl", hash = "sha256:c7d391e5936af5c9e252743d767c564670dc3889aff460d35c518ee76e4b26d7"},
- {file = "ruff-0.4.1-py3-none-win_arm64.whl", hash = "sha256:a1eaf03d87e6a7cd5e661d36d8c6e874693cb9bc3049d110bc9a97b350680c43"},
- {file = "ruff-0.4.1.tar.gz", hash = "sha256:d592116cdbb65f8b1b7e2a2b48297eb865f6bdc20641879aa9d7b9c11d86db79"},
+ {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"},
+ {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"},
+ {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"},
+ {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"},
+ {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"},
+ {file = "ruff-0.4.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e"},
+ {file = "ruff-0.4.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd"},
+ {file = "ruff-0.4.2-py3-none-win32.whl", hash = "sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe"},
+ {file = "ruff-0.4.2-py3-none-win_amd64.whl", hash = "sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798"},
+ {file = "ruff-0.4.2-py3-none-win_arm64.whl", hash = "sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf"},
+ {file = "ruff-0.4.2.tar.gz", hash = "sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc"},
]
[[package]]
From 25bdb560eba3081386028bfcfd32a15d604ec24b Mon Sep 17 00:00:00 2001
From: LinasKo
Date: Fri, 26 Apr 2024 17:53:19 +0300
Subject: [PATCH 104/136] from_inference, support direct inference results
---
supervision/keypoint/core.py | 48 ++++++++++++++++++++++++++++++++++--
1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 7019aa95..b3704929 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+from contextlib import suppress
from dataclasses import dataclass, field
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
@@ -101,7 +102,44 @@ class KeyPoints:
)
@classmethod
- def from_inference(cls, inference_result: dict) -> KeyPoints:
+ def from_inference(cls, inference_result: Union[dict, Any]) -> KeyPoints:
+ """
+ Create a `sv.KeyPoints` object from the [Roboflow](https://roboflow.com/)
+ API inference result or the [Inference](https://inference.roboflow.com/)
+ package results. When a keypoint detection model is used, this method
+ extracts the keypoint coordinates, class IDs, confidences, and class names.
+
+ Args:
+ inference_result (dict, any): The result from the
+ Roboflow API or Inference package containing predictions with keypoints.
+
+ Returns:
+ (KeyPoints): A KeyPoints object containing the keypoint coordinates,
+ class IDs, and confidences of each keypoint.
+
+ Example:
+ ```python
+ import cv2
+ import supervision as sv
+ from inference import get_model
+
+ image = cv2.imread()
+ model = get_model(model_id="yolov8s-640")
+
+ result = model.infer(image)[0]
+ keypoints = sv.KeyPoints.from_inference(result)
+ ```
+
+ """
+ if isinstance(inference_result, list):
+ raise ValueError("from_inference() operates on a single result at a time."
+ "You can retrieve it like so: inference_result = model.infer(image)[0]")
+
+ # Unpack the result if received from inference.get_model,
+ # rather than inference_sdk.InferenceHTTPClient
+ with suppress(AttributeError):
+ inference_result = inference_result.dict(exclude_none=True, by_alias=True)
+
if not inference_result.get("predictions"):
return cls.empty()
@@ -120,7 +158,13 @@ class KeyPoints:
confidence.append(prediction_confidence)
class_id.append(prediction["class_id"])
- class_names.append(prediction["class"])
+
+ if "class" in prediction:
+ class_names.append(prediction["class"])
+ elif "class_name" in prediction:
+ class_names.append(prediction["class_name"])
+ else:
+ raise KeyError("Neither 'class' nor 'class_name' found in prediction.")
data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
From 619da9575a7d56c5ff13a16e75f956a8f4d6ae32 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Fri, 26 Apr 2024 17:55:17 +0300
Subject: [PATCH 105/136] pre-commit tidy-up
---
supervision/keypoint/core.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index b3704929..e206b320 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -129,12 +129,14 @@ class KeyPoints:
result = model.infer(image)[0]
keypoints = sv.KeyPoints.from_inference(result)
```
-
+
"""
if isinstance(inference_result, list):
- raise ValueError("from_inference() operates on a single result at a time."
- "You can retrieve it like so: inference_result = model.infer(image)[0]")
-
+ raise ValueError(
+ "from_inference() operates on a single result at a time."
+ "You can retrieve it like so: inference_result = model.infer(image)[0]"
+ )
+
# Unpack the result if received from inference.get_model,
# rather than inference_sdk.InferenceHTTPClient
with suppress(AttributeError):
From ba2d30d110b5e8a74281397feb91b75e851bfb17 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Fri, 26 Apr 2024 17:36:50 +0200
Subject: [PATCH 106/136] `calculate_dynamic_line_thickness` ->
`calculate_optimal_line_thickness` `calculate_dynamic_text_scale` ->
`calculate_optimal_text_scale`
---
examples/count_people_in_zone/inference_example.py | 4 ++--
examples/count_people_in_zone/ultralytics_example.py | 4 ++--
examples/speed_estimation/inference_example.py | 4 ++--
examples/speed_estimation/ultralytics_example.py | 4 ++--
examples/speed_estimation/yolo_nas_example.py | 4 ++--
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/examples/count_people_in_zone/inference_example.py b/examples/count_people_in_zone/inference_example.py
index 163f1771..c5086806 100644
--- a/examples/count_people_in_zone/inference_example.py
+++ b/examples/count_people_in_zone/inference_example.py
@@ -38,8 +38,8 @@ def initiate_annotators(
) -> Tuple[
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
]:
- line_thickness = sv.calculate_dynamic_line_thickness(resolution_wh=resolution_wh)
- text_scale = sv.calculate_dynamic_text_scale(resolution_wh=resolution_wh)
+ line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
+ text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
zones = []
zone_annotators = []
diff --git a/examples/count_people_in_zone/ultralytics_example.py b/examples/count_people_in_zone/ultralytics_example.py
index 861db951..c17e4d7b 100644
--- a/examples/count_people_in_zone/ultralytics_example.py
+++ b/examples/count_people_in_zone/ultralytics_example.py
@@ -36,8 +36,8 @@ def initiate_annotators(
) -> Tuple[
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
]:
- line_thickness = sv.calculate_dynamic_line_thickness(resolution_wh=resolution_wh)
- text_scale = sv.calculate_dynamic_text_scale(resolution_wh=resolution_wh)
+ line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
+ text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
zones = []
zone_annotators = []
diff --git a/examples/speed_estimation/inference_example.py b/examples/speed_estimation/inference_example.py
index a435261c..e715d88e 100644
--- a/examples/speed_estimation/inference_example.py
+++ b/examples/speed_estimation/inference_example.py
@@ -98,10 +98,10 @@ if __name__ == "__main__":
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
- thickness = sv.calculate_dynamic_line_thickness(
+ thickness = sv.calculate_optimal_line_thickness(
resolution_wh=video_info.resolution_wh
)
- text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
+ text_scale = sv.calculate_optimal_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
diff --git a/examples/speed_estimation/ultralytics_example.py b/examples/speed_estimation/ultralytics_example.py
index 3218d268..4440d4d9 100644
--- a/examples/speed_estimation/ultralytics_example.py
+++ b/examples/speed_estimation/ultralytics_example.py
@@ -76,10 +76,10 @@ if __name__ == "__main__":
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
- thickness = sv.calculate_dynamic_line_thickness(
+ thickness = sv.calculate_optimal_line_thickness(
resolution_wh=video_info.resolution_wh
)
- text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
+ text_scale = sv.calculate_optimal_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
diff --git a/examples/speed_estimation/yolo_nas_example.py b/examples/speed_estimation/yolo_nas_example.py
index 18798166..4bb1d960 100644
--- a/examples/speed_estimation/yolo_nas_example.py
+++ b/examples/speed_estimation/yolo_nas_example.py
@@ -77,10 +77,10 @@ if __name__ == "__main__":
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
- thickness = sv.calculate_dynamic_line_thickness(
+ thickness = sv.calculate_optimal_line_thickness(
resolution_wh=video_info.resolution_wh
)
- text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
+ text_scale = sv.calculate_optimal_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
From a5326706119ddf9b29017fcc2d372b39cbceadb4 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Fri, 26 Apr 2024 17:55:34 +0200
Subject: [PATCH 107/136] bump version from 0.20.0 to 0.21.0rc1
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index de0df748..5960768a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
-version = "0.20.0"
+version = "0.21.0rc1"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
authors = ["Piotr Skalski "]
maintainers = ["Piotr Skalski "]
From c5a2f87bb91c8c27d27b50ba6ba84681ab20f432 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Apr 2024 01:20:56 +0000
Subject: [PATCH 108/136] :arrow_up: Bump pytest from 8.1.1 to 8.2.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.1.1 to 8.2.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.1.1...8.2.0)
---
updated-dependencies:
- dependency-name: pytest
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index bc59c72e..bc65995e 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2832,13 +2832,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-
[[package]]
name = "pluggy"
-version = "1.4.0"
+version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
- {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -3037,13 +3037,13 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "pytest"
-version = "8.1.1"
+version = "8.2.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
- {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
+ {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
+ {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
]
[package.dependencies]
@@ -3051,11 +3051,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
-pluggy = ">=1.4,<2.0"
+pluggy = ">=1.5,<2.0"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
-testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "python-dateutil"
From 31844c165bc43397a9f6253e13bb01994f537ed6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Apr 2024 01:24:07 +0000
Subject: [PATCH 109/136] :arrow_up: Bump mkdocstrings from 0.24.3 to 0.25.0
Bumps [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) from 0.24.3 to 0.25.0.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.24.3...0.25.0)
---
updated-dependencies:
- dependency-name: mkdocstrings
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 8 ++++----
pyproject.toml | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index bc59c72e..303c9195 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2239,13 +2239,13 @@ files = [
[[package]]
name = "mkdocstrings"
-version = "0.24.3"
+version = "0.25.0"
description = "Automatic documentation from sources, for MkDocs."
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocstrings-0.24.3-py3-none-any.whl", hash = "sha256:5c9cf2a32958cd161d5428699b79c8b0988856b0d4a8c5baf8395fc1bf4087c3"},
- {file = "mkdocstrings-0.24.3.tar.gz", hash = "sha256:f327b234eb8d2551a306735436e157d0a22d45f79963c60a8b585d5f7a94c1d2"},
+ {file = "mkdocstrings-0.25.0-py3-none-any.whl", hash = "sha256:df1b63f26675fcde8c1b77e7ea996cd2f93220b148e06455428f676f5dc838f1"},
+ {file = "mkdocstrings-0.25.0.tar.gz", hash = "sha256:066986b3fb5b9ef2d37c4417255a808f7e63b40ff8f67f6cab8054d903fbc91d"},
]
[package.dependencies]
@@ -4267,4 +4267,4 @@ desktop = ["opencv-python"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
-content-hash = "56ddae6824a9f28c9954badd4c642c57f687b099c6169f97fa0372e294500c17"
+content-hash = "a2019dd5d779d7fe9ae684d9b133bceb519c87123fd9917b42a86984bc2a7d3b"
diff --git a/pyproject.toml b/pyproject.toml
index 5960768a..f8de1a9e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -67,7 +67,7 @@ nbconvert = "^7.14.2"
[tool.poetry.group.docs.dependencies]
mkdocs-material = {extras = ["imaging"], version = "^9.5.5"}
-mkdocstrings = {extras = ["python"], version = ">=0.20,<0.25"}
+mkdocstrings = {extras = ["python"], version = ">=0.20,<0.26"}
mike = "^2.0.0"
# For Documentation Development use Python 3.10 or above
# Use Latest mkdocs-jupyter min 0.24.6 for Jupyter Notebook Theme support
From df0b2df2f68ffa72c4a58b069bd6dfdf09034ce6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Apr 2024 01:25:37 +0000
Subject: [PATCH 110/136] :arrow_up: Bump tox from 4.14.2 to 4.15.0
Bumps [tox](https://github.com/tox-dev/tox) from 4.14.2 to 4.15.0.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.14.2...4.15.0)
---
updated-dependencies:
- dependency-name: tox
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index bc59c72e..d8ed39d2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3957,13 +3957,13 @@ files = [
[[package]]
name = "tox"
-version = "4.14.2"
+version = "4.15.0"
description = "tox is a generic virtualenv management and test command line tool"
optional = false
python-versions = ">=3.8"
files = [
- {file = "tox-4.14.2-py3-none-any.whl", hash = "sha256:2900c4eb7b716af4a928a7fdc2ed248ad6575294ed7cfae2ea41203937422847"},
- {file = "tox-4.14.2.tar.gz", hash = "sha256:0defb44f6dafd911b61788325741cc6b2e12ea71f987ac025ad4d649f1f1a104"},
+ {file = "tox-4.15.0-py3-none-any.whl", hash = "sha256:300055f335d855b2ab1b12c5802de7f62a36d4fd53f30bd2835f6a201dda46ea"},
+ {file = "tox-4.15.0.tar.gz", hash = "sha256:7a0beeef166fbe566f54f795b4906c31b428eddafc0102ac00d20998dd1933f6"},
]
[package.dependencies]
From d2d69f986eedfa3bb1d064ec2659dc283ea51f9d Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Mon, 29 Apr 2024 09:46:12 +0300
Subject: [PATCH 111/136] from_infernece: Example suggests users find their own
model
* Example incorrectly suggested yolov8s-640
* Replaced with "" until we have a COCO human pose model
on prod. I don't think "horse-pose/3" is helpful - the use case is
uncommon and the training quality is poor.
---
supervision/keypoint/core.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index e206b320..d1021b94 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -51,7 +51,8 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]
+ ] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -124,7 +125,7 @@ class KeyPoints:
from inference import get_model
image = cv2.imread()
- model = get_model(model_id="yolov8s-640")
+ model = get_model(model_id="")
result = model.infer(image)[0]
keypoints = sv.KeyPoints.from_inference(result)
@@ -140,7 +141,8 @@ class KeyPoints:
# Unpack the result if received from inference.get_model,
# rather than inference_sdk.InferenceHTTPClient
with suppress(AttributeError):
- inference_result = inference_result.dict(exclude_none=True, by_alias=True)
+ inference_result = inference_result.dict(
+ exclude_none=True, by_alias=True)
if not inference_result.get("predictions"):
return cls.empty()
@@ -166,7 +168,8 @@ class KeyPoints:
elif "class_name" in prediction:
class_names.append(prediction["class_name"])
else:
- raise KeyError("Neither 'class' nor 'class_name' found in prediction.")
+ raise KeyError(
+ "Neither 'class' nor 'class_name' found in prediction.")
data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
@@ -207,7 +210,8 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i] for i in class_id])
+ class_names = np.array([ultralytics_results.names[i]
+ for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
From 0dd8d7dca843f7e93336469c4157b3de79b65d58 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 29 Apr 2024 06:53:33 +0000
Subject: [PATCH 112/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/core.py | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index d1021b94..09c307f5 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -51,8 +51,7 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]
- ] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -141,8 +140,7 @@ class KeyPoints:
# Unpack the result if received from inference.get_model,
# rather than inference_sdk.InferenceHTTPClient
with suppress(AttributeError):
- inference_result = inference_result.dict(
- exclude_none=True, by_alias=True)
+ inference_result = inference_result.dict(exclude_none=True, by_alias=True)
if not inference_result.get("predictions"):
return cls.empty()
@@ -168,8 +166,7 @@ class KeyPoints:
elif "class_name" in prediction:
class_names.append(prediction["class_name"])
else:
- raise KeyError(
- "Neither 'class' nor 'class_name' found in prediction.")
+ raise KeyError("Neither 'class' nor 'class_name' found in prediction.")
data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
@@ -210,8 +207,7 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i]
- for i in class_id])
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
From 7331577e6905b87506a7a8f761af528e9faec82e Mon Sep 17 00:00:00 2001
From: LinasKo
Date: Mon, 29 Apr 2024 13:37:37 +0300
Subject: [PATCH 113/136] keypoints, from_inference: removed class_name option
---
supervision/keypoint/core.py | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 09c307f5..8456eb6f 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -160,13 +160,7 @@ class KeyPoints:
confidence.append(prediction_confidence)
class_id.append(prediction["class_id"])
-
- if "class" in prediction:
- class_names.append(prediction["class"])
- elif "class_name" in prediction:
- class_names.append(prediction["class_name"])
- else:
- raise KeyError("Neither 'class' nor 'class_name' found in prediction.")
+ class_names.append(prediction["class"])
data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
From 67160c96b04278bcd43ceba272553e3292717528 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 29 Apr 2024 17:52:51 +0000
Subject: [PATCH 114/136] =?UTF-8?q?chore(pre=5Fcommit):=20=E2=AC=86=20pre?=
=?UTF-8?q?=5Fcommit=20autoupdate?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.1 → v0.4.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.1...v0.4.2)
---
.pre-commit-config.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ef3620b1..c0903f3a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -45,7 +45,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.4.1
+ rev: v0.4.2
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
From a70588284a1f37989bde166987e498f15982bcff Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Mon, 29 Apr 2024 20:36:05 +0200
Subject: [PATCH 115/136] add Roboflow API example
---
supervision/keypoint/core.py | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 8456eb6f..bbabca66 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -124,12 +124,26 @@ class KeyPoints:
from inference import get_model
image = cv2.imread()
- model = get_model(model_id="")
+ model = get_model(model_id=)
result = model.infer(image)[0]
- keypoints = sv.KeyPoints.from_inference(result)
+ key_points = sv.KeyPoints.from_inference(result)
```
+ ```python
+ import cv2
+ import supervision as sv
+ from inference_sdk import InferenceHTTPClient
+
+ image = cv2.imread()
+ client = InferenceHTTPClient(
+ api_url="https://detect.roboflow.com",
+ api_key=
+ )
+
+ result = client.infer(image, model_id=)
+ key_points = sv.KeyPoints.from_inference(result)
+ ```
"""
if isinstance(inference_result, list):
raise ValueError(
From 66f6a86bbfb0ef7da6fd9a6ab7f1ff474691b350 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Mon, 29 Apr 2024 20:37:56 +0200
Subject: [PATCH 116/136] add Roboflow API KEY to example
---
supervision/keypoint/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index bbabca66..45d2d9b4 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -124,7 +124,7 @@ class KeyPoints:
from inference import get_model
image = cv2.imread()
- model = get_model(model_id=)
+ model = get_model(model_id=, api_key=)
result = model.infer(image)[0]
key_points = sv.KeyPoints.from_inference(result)
From d4b1375db7bbb9716921e2076138593b901257db Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Mon, 29 Apr 2024 21:46:07 +0200
Subject: [PATCH 117/136] pin inference version
---
examples/speed_estimation/requirements.txt | 2 +-
examples/time_in_zone/requirements.txt | 2 +-
examples/traffic_analysis/requirements.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/examples/speed_estimation/requirements.txt b/examples/speed_estimation/requirements.txt
index af2e1c13..b3aa2456 100644
--- a/examples/speed_estimation/requirements.txt
+++ b/examples/speed_estimation/requirements.txt
@@ -3,4 +3,4 @@ tqdm==4.66.1
requests
ultralytics==8.0.237
super-gradients==3.5.0
-inference==0.9.8
+inference<=0.9.21
diff --git a/examples/time_in_zone/requirements.txt b/examples/time_in_zone/requirements.txt
index 752b4a8f..df94e4bd 100644
--- a/examples/time_in_zone/requirements.txt
+++ b/examples/time_in_zone/requirements.txt
@@ -1,5 +1,5 @@
opencv-python
supervision>=0.20.0
ultralytics
-inference
+inference<=0.9.21
pytube
diff --git a/examples/traffic_analysis/requirements.txt b/examples/traffic_analysis/requirements.txt
index 114447fc..536ac653 100644
--- a/examples/traffic_analysis/requirements.txt
+++ b/examples/traffic_analysis/requirements.txt
@@ -1,5 +1,5 @@
gdown
-inference
+inference<=0.9.21
supervision>=0.20.0
tqdm
ultralytics
From 4732b6f1d0142192a9faea635670e64696cae004 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Mon, 29 Apr 2024 22:58:13 +0200
Subject: [PATCH 118/136] bump version from 0.21.0rc1 to 0.21.0rc2
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index f8de1a9e..426ffb7c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
-version = "0.21.0rc1"
+version = "0.21.0rc2"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
authors = ["Piotr Skalski "]
maintainers = ["Piotr Skalski "]
From 71919f1d6fcbbd9a5debb4f102ae15e5118e63c3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 30 Apr 2024 00:33:04 +0000
Subject: [PATCH 119/136] :arrow_up: Bump nbconvert from 7.16.3 to 7.16.4
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.16.3 to 7.16.4.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter/nbconvert/compare/v7.16.3...v7.16.4)
---
updated-dependencies:
- dependency-name: nbconvert
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 754f37f2..be9666ff 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2374,13 +2374,13 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=
[[package]]
name = "nbconvert"
-version = "7.16.3"
+version = "7.16.4"
description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)."
optional = false
python-versions = ">=3.8"
files = [
- {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"},
- {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"},
+ {file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"},
+ {file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"},
]
[package.dependencies]
@@ -2402,9 +2402,9 @@ tinycss2 = "*"
traitlets = ">=5.1"
[package.extras]
-all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"]
+all = ["flaky", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (==5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"]
docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"]
-qtpdf = ["nbconvert[qtpng]"]
+qtpdf = ["pyqtwebengine (>=5.15)"]
qtpng = ["pyqtwebengine (>=5.15)"]
serve = ["tornado (>=6.1)"]
test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"]
From 71fd3178ccdcd38e095b638f69d4b0e8d9174c85 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 30 Apr 2024 00:35:39 +0000
Subject: [PATCH 120/136] :arrow_up: Bump mkdocs-material from 9.5.19 to 9.5.20
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.19 to 9.5.20.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.19...9.5.20)
---
updated-dependencies:
- dependency-name: mkdocs-material
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 754f37f2..367af17b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2197,13 +2197,13 @@ pygments = ">2.12.0"
[[package]]
name = "mkdocs-material"
-version = "9.5.19"
+version = "9.5.20"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.19-py3-none-any.whl", hash = "sha256:ea96e150b6c95f5e4ffe47d78bb712c7bacdd91d2a0bec47f46b6fa0705a86ec"},
- {file = "mkdocs_material-9.5.19.tar.gz", hash = "sha256:7473e06e17e23af608a30ef583fdde8f36389dd3ef56b1d503eed54c89c9618c"},
+ {file = "mkdocs_material-9.5.20-py3-none-any.whl", hash = "sha256:ad0094a7597bcb5d0cc3e8e543a10927c2581f7f647b9bb4861600f583180f9b"},
+ {file = "mkdocs_material-9.5.20.tar.gz", hash = "sha256:986eef0250d22f70fb06ce0f4eac64cc92bd797a589ec3892ce31fad976fe3da"},
]
[package.dependencies]
From 7fd5d80a3bd7b104f87e614c5b28f689de26d82e Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 30 Apr 2024 12:30:44 +0200
Subject: [PATCH 121/136] bump version from 0.21.0rc2 to 0.21.0rc3
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 426ffb7c..1ea80ebf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
-version = "0.21.0rc2"
+version = "0.21.0rc3"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
authors = ["Piotr Skalski "]
maintainers = ["Piotr Skalski "]
From ffda94b1e526aa1fa047df38db1336b700ee40fd Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 30 Apr 2024 16:06:02 +0200
Subject: [PATCH 122/136] pin inference version to 0.9.17
---
examples/count_people_in_zone/requirements.txt | 2 +-
examples/speed_estimation/requirements.txt | 2 +-
examples/time_in_zone/requirements.txt | 2 +-
examples/tracking/requirements.txt | 2 +-
examples/traffic_analysis/requirements.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/examples/count_people_in_zone/requirements.txt b/examples/count_people_in_zone/requirements.txt
index 114447fc..1cd58655 100644
--- a/examples/count_people_in_zone/requirements.txt
+++ b/examples/count_people_in_zone/requirements.txt
@@ -1,5 +1,5 @@
gdown
-inference
+inference==0.9.17
supervision>=0.20.0
tqdm
ultralytics
diff --git a/examples/speed_estimation/requirements.txt b/examples/speed_estimation/requirements.txt
index b3aa2456..aa51418f 100644
--- a/examples/speed_estimation/requirements.txt
+++ b/examples/speed_estimation/requirements.txt
@@ -3,4 +3,4 @@ tqdm==4.66.1
requests
ultralytics==8.0.237
super-gradients==3.5.0
-inference<=0.9.21
+inference==0.9.17
diff --git a/examples/time_in_zone/requirements.txt b/examples/time_in_zone/requirements.txt
index df94e4bd..6154c3eb 100644
--- a/examples/time_in_zone/requirements.txt
+++ b/examples/time_in_zone/requirements.txt
@@ -1,5 +1,5 @@
opencv-python
supervision>=0.20.0
ultralytics
-inference<=0.9.21
+inference==0.9.17
pytube
diff --git a/examples/tracking/requirements.txt b/examples/tracking/requirements.txt
index 8d5a9233..a45d9291 100644
--- a/examples/tracking/requirements.txt
+++ b/examples/tracking/requirements.txt
@@ -1,4 +1,4 @@
-inference
+inference==0.9.17
supervision==0.19.0
tqdm
ultralytics
diff --git a/examples/traffic_analysis/requirements.txt b/examples/traffic_analysis/requirements.txt
index 536ac653..1cd58655 100644
--- a/examples/traffic_analysis/requirements.txt
+++ b/examples/traffic_analysis/requirements.txt
@@ -1,5 +1,5 @@
gdown
-inference<=0.9.21
+inference==0.9.17
supervision>=0.20.0
tqdm
ultralytics
From 33e69b2da323e134e81b5a03eec908a584fd5f19 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 May 2024 00:35:55 +0000
Subject: [PATCH 123/136] :arrow_up: Bump
mkdocs-git-revision-date-localized-plugin
Bumps [mkdocs-git-revision-date-localized-plugin](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/releases)
- [Commits](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/compare/v1.2.4...v1.2.5)
---
updated-dependencies:
- dependency-name: mkdocs-git-revision-date-localized-plugin
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index a980eb6a..89c2d4cd 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2162,13 +2162,13 @@ requests = "*"
[[package]]
name = "mkdocs-git-revision-date-localized-plugin"
-version = "1.2.4"
+version = "1.2.5"
description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file."
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs-git-revision-date-localized-plugin-1.2.4.tar.gz", hash = "sha256:08fd0c6f33c8da9e00daf40f7865943113b3879a1c621b2bbf0fa794ffe997d3"},
- {file = "mkdocs_git_revision_date_localized_plugin-1.2.4-py3-none-any.whl", hash = "sha256:1f94eb510862ef94e982a2910404fa17a1657ecf29f45a07b0f438c00767fc85"},
+ {file = "mkdocs_git_revision_date_localized_plugin-1.2.5-py3-none-any.whl", hash = "sha256:d796a18b07cfcdb154c133e3ec099d2bb5f38389e4fd54d3eb516a8a736815b8"},
+ {file = "mkdocs_git_revision_date_localized_plugin-1.2.5.tar.gz", hash = "sha256:0c439816d9d0dba48e027d9d074b2b9f1d7cd179f74ba46b51e4da7bb3dc4b9b"},
]
[package.dependencies]
From ba30244adda2bea4f39acd03d1ac6fc583839ff6 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Wed, 1 May 2024 15:20:50 +0300
Subject: [PATCH 124/136] Remove hardcoding of class_names, class_id.
* Still hardcoded ID to -1 and name to "" if not provided - this lets us
stick to (n,) shape.
* Found no way to add class_names to pose tracker, so used the response
of YOLO NAS detection instead, to check what the response looks like.
---
supervision/keypoint/core.py | 26 +++++++++++++++++++++-----
1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 4790acd7..10dfb7a0 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,7 +50,8 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]
+ ] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -130,7 +131,8 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i] for i in class_id])
+ class_names = np.array([ultralytics_results.names[i]
+ for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
@@ -170,13 +172,27 @@ class KeyPoints:
xy = yolo_nas_results.prediction.poses[:, :, :2]
confidence = yolo_nas_results.prediction.poses[:, :, 2]
- class_id = [0] * len(xy)
- data = {CLASS_NAME_DATA_FIELD: ["person" for _ in xy]}
+
+ # yolo_nas_results treats params differently.
+ # prediction.labels may not exist, whereas class_names might be None
+ if hasattr(yolo_nas_results.prediction, "labels"):
+ class_id = yolo_nas_results.prediction.labels # np.array[int]
+ else:
+ class_id = np.array([-1] * len(xy))
+
+ class_names = [""] * len(class_id)
+ if yolo_nas_results.class_names:
+ for i, c_id in enumerate(class_id):
+ if c_id != -1:
+ name = yolo_nas_results.class_names[c_id] # tuple[str]
+ class_names[i] = name
+
+ data = {CLASS_NAME_DATA_FIELD: class_names}
return cls(
xy=xy,
confidence=confidence,
- class_id=np.array(class_id),
+ class_id=class_id,
data=data,
)
From aab3ed2ba9fecc0cb5ed13cf5f72aed817c2e540 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 1 May 2024 12:23:51 +0000
Subject: [PATCH 125/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/core.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 10dfb7a0..89a56056 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,8 +50,7 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]
- ] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -131,8 +130,7 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i]
- for i in class_id])
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
From 079008cd176fd0ab207c8905b83ea0e753e09f7b Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Thu, 2 May 2024 16:30:01 +0300
Subject: [PATCH 126/136] from_yolo_nas - None class_id & names if not found
---
supervision/keypoint/core.py | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index 89a56056..d056819e 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -176,16 +176,15 @@ class KeyPoints:
if hasattr(yolo_nas_results.prediction, "labels"):
class_id = yolo_nas_results.prediction.labels # np.array[int]
else:
- class_id = np.array([-1] * len(xy))
+ class_id = None
- class_names = [""] * len(class_id)
- if yolo_nas_results.class_names:
- for i, c_id in enumerate(class_id):
- if c_id != -1:
- name = yolo_nas_results.class_names[c_id] # tuple[str]
- class_names[i] = name
-
- data = {CLASS_NAME_DATA_FIELD: class_names}
+ data = {}
+ if class_id is not None and yolo_nas_results.class_names is not None:
+ class_names = []
+ for c_id in class_id:
+ name = yolo_nas_results.class_names[c_id] # tuple[str]
+ class_names.append(name)
+ data[CLASS_NAME_DATA_FIELD] = class_names
return cls(
xy=xy,
From 347b75935b114da82431b0a9f9ec973eb5e4d1e4 Mon Sep 17 00:00:00 2001
From: Linas Kondrackis
Date: Thu, 2 May 2024 16:31:48 +0300
Subject: [PATCH 127/136] Ruff
---
supervision/keypoint/core.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index d056819e..ae08e38a 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,7 +50,8 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]
+ ] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -130,7 +131,8 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i] for i in class_id])
+ class_names = np.array([ultralytics_results.names[i]
+ for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
From 7e79fa3d07466f90e928d12644d84f31b051eb69 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Thu, 2 May 2024 13:34:11 +0000
Subject: [PATCH 128/136] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20aut?=
=?UTF-8?q?o=20format=20pre-commit=20hooks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
supervision/keypoint/core.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py
index ae08e38a..d056819e 100644
--- a/supervision/keypoint/core.py
+++ b/supervision/keypoint/core.py
@@ -50,8 +50,7 @@ class KeyPoints:
xy: npt.NDArray[np.float32]
class_id: Optional[npt.NDArray[np.int_]] = None
confidence: Optional[npt.NDArray[np.float32]] = None
- data: Dict[str, Union[npt.NDArray[Any], List]
- ] = field(default_factory=dict)
+ data: Dict[str, Union[npt.NDArray[Any], List]] = field(default_factory=dict)
def __post_init__(self):
validate_keypoints_fields(
@@ -131,8 +130,7 @@ class KeyPoints:
xy = ultralytics_results.keypoints.xy.cpu().numpy()
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
- class_names = np.array([ultralytics_results.names[i]
- for i in class_id])
+ class_names = np.array([ultralytics_results.names[i] for i in class_id])
confidence = ultralytics_results.keypoints.conf.cpu().numpy()
data = {CLASS_NAME_DATA_FIELD: class_names}
From 7d5a0bce45aeeb5a54d6e906e99509a33cd779ed Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 3 May 2024 00:33:20 +0000
Subject: [PATCH 129/136] :arrow_up: Bump tqdm from 4.66.2 to 4.66.4
Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.2 to 4.66.4.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.2...v4.66.4)
---
updated-dependencies:
- dependency-name: tqdm
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 8 ++++----
pyproject.toml | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 89c2d4cd..b895934c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3984,13 +3984,13 @@ testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-po
[[package]]
name = "tqdm"
-version = "4.66.2"
+version = "4.66.4"
description = "Fast, Extensible Progress Meter"
optional = true
python-versions = ">=3.7"
files = [
- {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
- {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
+ {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"},
+ {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"},
]
[package.dependencies]
@@ -4267,4 +4267,4 @@ desktop = ["opencv-python"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
-content-hash = "a2019dd5d779d7fe9ae684d9b133bceb519c87123fd9917b42a86984bc2a7d3b"
+content-hash = "29af5aa06f97e77a2dba94c5a6d77d7d1903448724df07416026a378d3c6a64d"
diff --git a/pyproject.toml b/pyproject.toml
index 1ea80ebf..509c05b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -43,7 +43,7 @@ defusedxml = "^0.7.1"
opencv-python = { version = ">=4.5.5.64", optional = true }
opencv-python-headless = ">=4.5.5.64"
requests = { version = ">=2.26.0,<=2.31.0", optional = true }
-tqdm = { version = ">=4.62.3,<=4.66.2", optional = true }
+tqdm = { version = ">=4.62.3,<=4.66.4", optional = true }
pillow = ">=9.4"
[tool.poetry.extras]
From 29b0c514cf1be01b4c7954b10cad7c0ea1de8204 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 3 May 2024 00:36:24 +0000
Subject: [PATCH 130/136] :arrow_up: Bump mike from 2.0.0 to 2.1.0
Bumps [mike](https://github.com/jimporter/mike) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/jimporter/mike/releases)
- [Changelog](https://github.com/jimporter/mike/blob/master/CHANGES.md)
- [Commits](https://github.com/jimporter/mike/compare/v2.0.0...v2.1.0)
---
updated-dependencies:
- dependency-name: mike
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 89c2d4cd..76605a88 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2048,13 +2048,13 @@ files = [
[[package]]
name = "mike"
-version = "2.0.0"
+version = "2.1.0"
description = "Manage multiple versions of your MkDocs-powered documentation"
optional = false
python-versions = "*"
files = [
- {file = "mike-2.0.0-py3-none-any.whl", hash = "sha256:87f496a65900f93ba92d72940242b65c86f3f2f82871bc60ebdcffc91fad1d9e"},
- {file = "mike-2.0.0.tar.gz", hash = "sha256:566f1cab1a58cc50b106fb79ea2f1f56e7bfc8b25a051e95e6eaee9fba0922de"},
+ {file = "mike-2.1.0-py3-none-any.whl", hash = "sha256:b3885f9b9e31fc4b0d61de473750d38ac170a6b291585076effb51a806245608"},
+ {file = "mike-2.1.0.tar.gz", hash = "sha256:f0b8e51cbfae1273d648ffb602a4ab3061e57972ca1cd6836df1c51c01a36eb5"},
]
[package.dependencies]
From f72d06b05e7283946f2600be237d1cf020d13c01 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 3 May 2024 21:51:32 +0000
Subject: [PATCH 131/136] Bump tqdm from 4.66.1 to 4.66.3 in
/examples/speed_estimation
Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.1 to 4.66.3.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.3)
---
updated-dependencies:
- dependency-name: tqdm
dependency-type: direct:production
...
Signed-off-by: dependabot[bot]
---
examples/speed_estimation/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/speed_estimation/requirements.txt b/examples/speed_estimation/requirements.txt
index aa51418f..343a687d 100644
--- a/examples/speed_estimation/requirements.txt
+++ b/examples/speed_estimation/requirements.txt
@@ -1,5 +1,5 @@
supervision>=0.20.0
-tqdm==4.66.1
+tqdm==4.66.3
requests
ultralytics==8.0.237
super-gradients==3.5.0
From 92d56eda150941d69bfe71a48b21e4edd024ad5c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 May 2024 01:06:46 +0000
Subject: [PATCH 132/136] :arrow_up: Bump mike from 2.1.0 to 2.1.1
Bumps [mike](https://github.com/jimporter/mike) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/jimporter/mike/releases)
- [Changelog](https://github.com/jimporter/mike/blob/master/CHANGES.md)
- [Commits](https://github.com/jimporter/mike/compare/v2.1.0...v2.1.1)
---
updated-dependencies:
- dependency-name: mike
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 2497d802..e4ec3872 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2048,13 +2048,13 @@ files = [
[[package]]
name = "mike"
-version = "2.1.0"
+version = "2.1.1"
description = "Manage multiple versions of your MkDocs-powered documentation"
optional = false
python-versions = "*"
files = [
- {file = "mike-2.1.0-py3-none-any.whl", hash = "sha256:b3885f9b9e31fc4b0d61de473750d38ac170a6b291585076effb51a806245608"},
- {file = "mike-2.1.0.tar.gz", hash = "sha256:f0b8e51cbfae1273d648ffb602a4ab3061e57972ca1cd6836df1c51c01a36eb5"},
+ {file = "mike-2.1.1-py3-none-any.whl", hash = "sha256:0b1d01a397a423284593eeb1b5f3194e37169488f929b860c9bfe95c0d5efb79"},
+ {file = "mike-2.1.1.tar.gz", hash = "sha256:f39ed39f3737da83ad0adc33e9f885092ed27f8c9e7ff0523add0480352a2c22"},
]
[package.dependencies]
@@ -2064,6 +2064,7 @@ jinja2 = ">=2.7"
mkdocs = ">=1.0"
pyparsing = ">=3.0"
pyyaml = ">=5.1"
+pyyaml-env-tag = "*"
verspec = "*"
[package.extras]
From f7d21acd3a40bfaab5729ba107915248edfdcbd0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 May 2024 01:09:12 +0000
Subject: [PATCH 133/136] :arrow_up: Bump mkdocs-material from 9.5.20 to 9.5.21
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.20 to 9.5.21.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.20...9.5.21)
---
updated-dependencies:
- dependency-name: mkdocs-material
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 2497d802..4608f00f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2197,13 +2197,13 @@ pygments = ">2.12.0"
[[package]]
name = "mkdocs-material"
-version = "9.5.20"
+version = "9.5.21"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.20-py3-none-any.whl", hash = "sha256:ad0094a7597bcb5d0cc3e8e543a10927c2581f7f647b9bb4861600f583180f9b"},
- {file = "mkdocs_material-9.5.20.tar.gz", hash = "sha256:986eef0250d22f70fb06ce0f4eac64cc92bd797a589ec3892ce31fad976fe3da"},
+ {file = "mkdocs_material-9.5.21-py3-none-any.whl", hash = "sha256:210e1f179682cd4be17d5c641b2f4559574b9dea2f589c3f0e7c17c5bd1959bc"},
+ {file = "mkdocs_material-9.5.21.tar.gz", hash = "sha256:049f82770f40559d3c2aa2259c562ea7257dbb4aaa9624323b5ef27b2d95a450"},
]
[package.dependencies]
From ff52352b006ca0ffcee80792289aeb165ece9017 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 May 2024 01:12:45 +0000
Subject: [PATCH 134/136] :arrow_up: Bump jupytext from 1.16.1 to 1.16.2
Bumps [jupytext](https://github.com/mwouts/jupytext) from 1.16.1 to 1.16.2.
- [Release notes](https://github.com/mwouts/jupytext/releases)
- [Changelog](https://github.com/mwouts/jupytext/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mwouts/jupytext/compare/v1.16.1...v1.16.2)
---
updated-dependencies:
- dependency-name: jupytext
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 29 +++++++++--------------------
1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 2497d802..87dc0781 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1648,13 +1648,13 @@ files = [
[[package]]
name = "jupytext"
-version = "1.16.1"
+version = "1.16.2"
description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts"
optional = false
python-versions = ">=3.8"
files = [
- {file = "jupytext-1.16.1-py3-none-any.whl", hash = "sha256:796ec4f68ada663569e5d38d4ef03738a01284bfe21c943c485bc36433898bd0"},
- {file = "jupytext-1.16.1.tar.gz", hash = "sha256:68c7b68685e870e80e60fda8286fbd6269e9c74dc1df4316df6fe46eabc94c99"},
+ {file = "jupytext-1.16.2-py3-none-any.whl", hash = "sha256:197a43fef31dca612b68b311e01b8abd54441c7e637810b16b6cb8f2ab66065e"},
+ {file = "jupytext-1.16.2.tar.gz", hash = "sha256:8627dd9becbbebd79cc4a4ed4727d89d78e606b4b464eab72357b3b029023a14"},
]
[package.dependencies]
@@ -1663,16 +1663,16 @@ mdit-py-plugins = "*"
nbformat = "*"
packaging = "*"
pyyaml = "*"
-toml = "*"
+tomli = {version = "*", markers = "python_version < \"3.11\""}
[package.extras]
-dev = ["jupytext[test-cov,test-external]"]
+dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs (<0.4.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx-gallery (<0.8)"]
docs = ["myst-parser", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"]
test = ["pytest", "pytest-randomly", "pytest-xdist"]
-test-cov = ["jupytext[test-integration]", "pytest-cov (>=2.6.1)"]
-test-external = ["autopep8", "black", "flake8", "gitpython", "isort", "jupyter-fs (<0.4.0)", "jupytext[test-integration]", "pre-commit", "sphinx-gallery (<0.8)"]
-test-functional = ["jupytext[test]"]
-test-integration = ["ipykernel", "jupyter-server (!=2.11)", "jupytext[test-functional]", "nbconvert"]
+test-cov = ["ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist"]
+test-external = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs (<0.4.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-randomly", "pytest-xdist", "sphinx-gallery (<0.8)"]
+test-functional = ["pytest", "pytest-randomly", "pytest-xdist"]
+test-integration = ["ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-randomly", "pytest-xdist"]
test-ui = ["calysto-bash"]
[[package]]
@@ -3913,17 +3913,6 @@ webencodings = ">=0.4"
doc = ["sphinx", "sphinx_rtd_theme"]
test = ["flake8", "isort", "pytest"]
-[[package]]
-name = "toml"
-version = "0.10.2"
-description = "Python Library for Tom's Obvious, Minimal Language"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
- {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-
[[package]]
name = "tomli"
version = "2.0.1"
From aebbd01830b807def90a6a1d36636ccaa2ba94df Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 May 2024 01:15:08 +0000
Subject: [PATCH 135/136] :arrow_up: Bump mkdocstrings from 0.25.0 to 0.25.1
Bumps [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) from 0.25.0 to 0.25.1.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.25.0...0.25.1)
---
updated-dependencies:
- dependency-name: mkdocstrings
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 2497d802..f209f594 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2239,13 +2239,13 @@ files = [
[[package]]
name = "mkdocstrings"
-version = "0.25.0"
+version = "0.25.1"
description = "Automatic documentation from sources, for MkDocs."
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocstrings-0.25.0-py3-none-any.whl", hash = "sha256:df1b63f26675fcde8c1b77e7ea996cd2f93220b148e06455428f676f5dc838f1"},
- {file = "mkdocstrings-0.25.0.tar.gz", hash = "sha256:066986b3fb5b9ef2d37c4417255a808f7e63b40ff8f67f6cab8054d903fbc91d"},
+ {file = "mkdocstrings-0.25.1-py3-none-any.whl", hash = "sha256:da01fcc2670ad61888e8fe5b60afe9fee5781017d67431996832d63e887c2e51"},
+ {file = "mkdocstrings-0.25.1.tar.gz", hash = "sha256:c3a2515f31577f311a9ee58d089e4c51fc6046dbd9e9b4c3de4c3194667fe9bf"},
]
[package.dependencies]
From edd944fff887bfee404470a8efb408a84d10dfbc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 May 2024 01:18:43 +0000
Subject: [PATCH 136/136] :arrow_up: Bump ruff from 0.4.2 to 0.4.3
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.2 to 0.4.3.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.2...v0.4.3)
---
updated-dependencies:
- dependency-name: ruff
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
poetry.lock | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 2497d802..d01eeae7 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3660,28 +3660,28 @@ files = [
[[package]]
name = "ruff"
-version = "0.4.2"
+version = "0.4.3"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"},
- {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"},
- {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"},
- {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"},
- {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"},
- {file = "ruff-0.4.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e"},
- {file = "ruff-0.4.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd"},
- {file = "ruff-0.4.2-py3-none-win32.whl", hash = "sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe"},
- {file = "ruff-0.4.2-py3-none-win_amd64.whl", hash = "sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798"},
- {file = "ruff-0.4.2-py3-none-win_arm64.whl", hash = "sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf"},
- {file = "ruff-0.4.2.tar.gz", hash = "sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc"},
+ {file = "ruff-0.4.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b70800c290f14ae6fcbb41bbe201cf62dfca024d124a1f373e76371a007454ce"},
+ {file = "ruff-0.4.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08a0d6a22918ab2552ace96adeaca308833873a4d7d1d587bb1d37bae8728eb3"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba1f14df3c758dd7de5b55fbae7e1c8af238597961e5fb628f3de446c3c40c5"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:819fb06d535cc76dfddbfe8d3068ff602ddeb40e3eacbc90e0d1272bb8d97113"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bfc9e955e6dc6359eb6f82ea150c4f4e82b660e5b58d9a20a0e42ec3bb6342b"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:510a67d232d2ebe983fddea324dbf9d69b71c4d2dfeb8a862f4a127536dd4cfb"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9ff11cd9a092ee7680a56d21f302bdda14327772cd870d806610a3503d001f"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29efff25bf9ee685c2c8390563a5b5c006a3fee5230d28ea39f4f75f9d0b6f2f"},
+ {file = "ruff-0.4.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b00e0bcccf0fc8d7186ed21e311dffd19761cb632241a6e4fe4477cc80ef6e"},
+ {file = "ruff-0.4.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:262f5635e2c74d80b7507fbc2fac28fe0d4fef26373bbc62039526f7722bca1b"},
+ {file = "ruff-0.4.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7363691198719c26459e08cc17c6a3dac6f592e9ea3d2fa772f4e561b5fe82a3"},
+ {file = "ruff-0.4.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eeb039f8428fcb6725bb63cbae92ad67b0559e68b5d80f840f11914afd8ddf7f"},
+ {file = "ruff-0.4.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:927b11c1e4d0727ce1a729eace61cee88a334623ec424c0b1c8fe3e5f9d3c865"},
+ {file = "ruff-0.4.3-py3-none-win32.whl", hash = "sha256:25cacda2155778beb0d064e0ec5a3944dcca9c12715f7c4634fd9d93ac33fd30"},
+ {file = "ruff-0.4.3-py3-none-win_amd64.whl", hash = "sha256:7a1c3a450bc6539ef00da6c819fb1b76b6b065dec585f91456e7c0d6a0bbc725"},
+ {file = "ruff-0.4.3-py3-none-win_arm64.whl", hash = "sha256:71ca5f8ccf1121b95a59649482470c5601c60a416bf189d553955b0338e34614"},
+ {file = "ruff-0.4.3.tar.gz", hash = "sha256:ff0a3ef2e3c4b6d133fbedcf9586abfbe38d076041f2dc18ffb2c7e0485d5a07"},
]
[[package]]