From 8fc541339440be84875a5cd24bfd19536cf3520e Mon Sep 17 00:00:00 2001
From: Onuralp SEZER
Date: Mon, 1 Apr 2024 00:32:35 +0300
Subject: [PATCH 01/36] =?UTF-8?q?ci:=20=F0=9F=91=B7=20supervision=20docume?=
=?UTF-8?q?ntation=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 6b682f00ae473807d1a07cdda31d3dae89ea35c2 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 8 Apr 2024 17:55:20 +0000
Subject: [PATCH 02/36] =?UTF-8?q?chore(pre=5Fcommit):=20=E2=AC=86=20pre=5F?=
=?UTF-8?q?commit=20autoupdate?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0)
- [github.com/astral-sh/ruff-pre-commit: v0.3.4 → v0.3.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.4...v0.3.5)
---
.pre-commit-config.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f0515b9e..4c2b1a7e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,7 +7,7 @@ ci:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.5.0
+ rev: v4.6.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
@@ -45,7 +45,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.3.4
+ rev: v0.3.5
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
From 574c61b6b00bb80f90b6826b404b843b3c1f6bf4 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 14:28:40 +0200
Subject: [PATCH 03/36] 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 04/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 05/36] 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 06/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 94e06ab0d6b71d87a3839207efe6b23d6bf74186 Mon Sep 17 00:00:00 2001
From: SkalskiP
Date: Tue, 9 Apr 2024 16:46:05 +0200
Subject: [PATCH 07/36] 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 08/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 09/36] 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 10/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 11/36] 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 12/36] 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 13/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 14/36] 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 15/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 16/36] `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 17/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 18/36] 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 19/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 20/36] 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 21/36] :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 22/36] 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 ed225187b248dad4dbcebc6385508a561d70a629 Mon Sep 17 00:00:00 2001
From: Piotr Skalski
Date: Thu, 11 Apr 2024 14:09:02 +0200
Subject: [PATCH 23/36] 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 263421d145d2d5836714365b59e537e75f21ee30 Mon Sep 17 00:00:00 2001
From: Piotr Skalski
Date: Thu, 11 Apr 2024 14:40:43 +0200
Subject: [PATCH 24/36] 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 25/36] 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 26/36] 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 b7607c6f152d73ee5556c67bdb6ff94b54d29847 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Thu, 11 Apr 2024 18:57:30 +0530
Subject: [PATCH 27/36] 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 28baf18cb6dcd6005fa030e35390028adfe350e8 Mon Sep 17 00:00:00 2001
From: Jeslin P James
Date: Thu, 11 Apr 2024 18:59:48 +0530
Subject: [PATCH 28/36] 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 29/36] 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 30/36] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?=
=?UTF-8?q?=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 31/36] :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 32/36] :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 33/36] 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 34/36] 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 35/36] 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 36/36] 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,
)