Compare commits

...
21 Commits
Author SHA1 Message Date
jirka eba1d64d91 resolve triggers for releasing 2026-02-06 19:53:07 +09:00
jirka f50ddca3a1 bump 0.27.0.post0 2026-02-06 17:10:56 +09:00
jirka 5b129a0850 Ensure compatibility with Python 3.9 2026-02-06 17:10:56 +09:00
jirka fd4845a5cf adjust deprecation version for from_lmm 2026-02-06 17:10:56 +09:00
jirka 8c104311d3 Enable caching and enforce dependency versions in uv workflow 2026-02-06 17:10:56 +09:00
jirka 650ca0ae2f Revive create_tiles implementation and all related functions 2026-02-06 17:10:56 +09:00
jirka bc521bc2d0 Update deprecation annotations to extend remove_in versions across multiple modules 2026-02-06 17:10:56 +09:00
Jirka Borovec ee1400a40c resolve docs build / pin mkdocstrings-python<2.0.0 (#2031) 2026-02-06 17:10:56 +09:00
jirka 7f3dcaedbd Refactor imports for consistency and update deprecated utility usage across modules 2026-02-06 17:10:56 +09:00
jirka 0ba57b2f57 Update deprecation annotations and refactor deprecated utility usage 2026-02-06 17:10:56 +09:00
jirka ae6aa369ae Update deprecation annotations and refactor deprecated utility usage 2026-02-06 17:10:56 +09:00
jirka e36fbe061d Add deprecation warnings for keypoint validation utilities in supervision 2026-02-06 17:10:56 +09:00
jirka 224c7569a1 Add deprecation warning for create_tiles in image utilities 2026-02-06 17:10:56 +09:00
jirka c9761515f3 Add deprecation warning for ensure_cv2_image_for_processing in conversion utilities 2026-02-06 17:10:56 +09:00
jirka d72089bb46 Add deprecation warning for denormalize_boxes in bounding box utilities 2026-02-06 17:10:56 +09:00
jirka 219161f1f9 Add deprecation warnings for annotation utilities and update dependencies 2026-02-06 17:10:56 +09:00
jirka 99e772c466 Introduce deprecation warnings for supervision.keypoint module, redirecting to supervision.key_points. 2026-02-06 17:10:56 +09:00
Piotr Skalski a61440ee0b Merge pull request #2005 from roboflow/develop
supervision-0.27.0 release
2025-11-16 19:05:40 +01:00
Piotr Skalski 9bf9685165 Merge pull request #2007 from cdeil/patch-1
Fix Albumentations docs typo
2025-11-05 22:33:48 +01:00
Christoph Deil e37b32f5ca Fix Albumentations docs typo 2025-11-05 16:31:24 +01:00
Piotr Skalski 7ecfc9f475 Merge pull request #1908 from roboflow/develop
`supervision-0.26.1` release
2025-07-23 08:46:45 +02:00
18 changed files with 511 additions and 57 deletions
+6 -3
View File
@@ -3,9 +3,12 @@ name: Publish Supervision Pre-Releases to PyPI
on:
push:
tags:
- "[0-9]+.[0-9]+[0-9]+.[0-9]+a[0-9]"
- "[0-9]+.[0-9]+[0-9]+.[0-9]+b[0-9]"
- "[0-9]+.[0-9]+[0-9]+.[0-9]+rc[0-9]"
- "[0-9]+.[0-9]+.[0-9]+a[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+b[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
- "[0-9]+.[0-9]+.[0-9].+a[0-9]+"
- "[0-9]+.[0-9]+.[0-9].+b[0-9]+"
- "[0-9]+.[0-9]+.[0-9].+rc[0-9]+"
workflow_dispatch:
permissions: {} # Explicitly remove all permissions by default
+2 -1
View File
@@ -3,7 +3,8 @@ name: Publish Supervision Releases to PyPI
on:
push:
tags:
- "[0-9]+.[0-9]+[0-9]+.[0-9]"
- "[0-9]+.[0-9]+.[0-9]"
- "[0-9]+.[0-9]+.[0-9].post[0-9]+"
workflow_dispatch:
permissions: {} # Explicitly remove all permissions by default
+2
View File
@@ -1,6 +1,8 @@
name: 🧪 Docs Test WorkFlow 📚
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
+6 -2
View File
@@ -1,6 +1,8 @@
name: 🔧 Pytest/Test Workflow
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
@@ -23,12 +25,14 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
enable-cache: true # Recommended: caches the uv download/builds
- name: 🚀 Install Packages
run: uv pip install -r pyproject.toml --group dev --group docs --extra metrics
# CHANGED: 'uv sync' enforces the versions in uv.lock
run: uv sync --group dev --group docs --extra metrics
- name: 🧪 Run the Import test
# 'uv run' executes inside the environment created by 'uv sync'
run: uv run python -c "import supervision; from supervision import assets; from supervision import metrics; print(supervision.__version__)"
- name: 🧪 Run the Test
+1 -1
View File
@@ -395,7 +395,7 @@ increase the size and diversity of training datasets, leading to improved model
performance and generalization.
```bash
pip install augmentation
pip install albumentations
```
Albumentations provides a flexible and powerful API for image augmentation. The core of
+4 -3
View File
@@ -2,7 +2,7 @@
name = "supervision"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
license = { text = "MIT" }
version = "0.27.0"
version = "0.27.0.post0"
readme = "README.md"
requires-python = ">=3.9"
authors = [
@@ -46,7 +46,8 @@ dependencies = [
"pillow>=9.4",
"requests>=2.26.0",
"tqdm>=4.62.3",
"opencv-python>=4.5.5.64"
"opencv-python>=4.5.5.64",
"pyDeprecate>=0.4.0"
]
[project.urls]
@@ -73,7 +74,7 @@ dev = [
docs = [
"mkdocs-material[imaging]>=9.5.5",
"mkdocstrings>=0.25.2,<0.31.0",
"mkdocstrings-python>=1.10.9",
"mkdocstrings-python>=1.10.9, <2.0.0", # todo: breaking changes in 2.x
"mike>=2.0.0",
"mkdocs-jupyter>=0.24.3",
"mkdocs-git-committers-plugin-2>=2.4.1; python_version >= '3.9' and python_version < '4'",
+5 -3
View File
@@ -7,6 +7,7 @@ from functools import reduce
from typing import Any
import numpy as np
from deprecate import deprecated
from supervision.config import (
CLASS_NAME_DATA_FIELD,
@@ -51,7 +52,7 @@ from supervision.detection.vlm import (
validate_vlm_parameters,
)
from supervision.geometry.core import Position
from supervision.utils.internal import deprecated, get_instance_variables
from supervision.utils.internal import get_instance_variables
from supervision.validators import validate_detections_fields
@@ -818,8 +819,9 @@ class Detections:
@classmethod
@deprecated(
"`Detections.from_lmm` property is deprecated and will be removed in "
"`supervision-0.31.0`. Use Detections.from_vlm instead."
target=None,
deprecated_in="0.26.0",
remove_in="0.30.0",
)
def from_lmm(cls, lmm: LMM | str, result: str | dict, **kwargs: Any) -> Detections:
"""
+8
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import numpy as np
import numpy.typing as npt
from deprecate import deprecated
from supervision.detection.utils.iou_and_nms import box_iou_batch
@@ -94,10 +95,17 @@ def pad_boxes(xyxy: np.ndarray, px: int, py: int | None = None) -> np.ndarray:
return result
@deprecated(
target=None,
deprecated_in="0.27.0",
remove_in="0.31.0",
args_mapping={"normalized_xyxy": "xyxy"},
)
def denormalize_boxes(
xyxy: np.ndarray,
resolution_wh: tuple[int, int],
normalization_factor: float = 1.0,
normalized_xyxy: np.ndarray = None,
) -> np.ndarray:
"""
Convert normalized bounding box coordinates to absolute pixel coordinates.
+4 -3
View File
@@ -9,17 +9,18 @@ from enum import Enum
from typing import Any
import numpy as np
from deprecate import deprecated
from PIL import Image
from supervision.detection.utils.boxes import denormalize_boxes
from supervision.detection.utils.converters import polygon_to_mask, polygon_to_xyxy
from supervision.utils.internal import deprecated
from supervision.validators import validate_resolution
@deprecated(
"`LMM` enum is deprecated and will be removed in "
"`supervision-0.31.0`. Use VLM instead."
target=None,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
class LMM(Enum):
"""
+15
View File
@@ -0,0 +1,15 @@
import warnings
warnings.warn(
"The 'supervision.keypoint' module is deprecated in `0.27.0` and will be removed "
"in `0.30.0`. Please use 'supervision.key_points' instead.",
DeprecationWarning,
stacklevel=2,
)
from supervision.key_points.annotators import (
EdgeAnnotator,
VertexAnnotator,
VertexLabelAnnotator,
)
from supervision.key_points.core import KeyPoints
+15
View File
@@ -0,0 +1,15 @@
import warnings
warnings.warn(
"The 'supervision.keypoint' module is deprecated in `0.27.0` and will be removed "
"in `0.30.0`. Please use 'supervision.key_points' instead.",
DeprecationWarning,
stacklevel=2,
)
from supervision.key_points.annotators import ( # noqa: F401, E402
BaseKeyPointAnnotator,
EdgeAnnotator,
VertexAnnotator,
VertexLabelAnnotator,
)
+10
View File
@@ -0,0 +1,10 @@
import warnings
warnings.warn(
"The 'supervision.keypoint' module is deprecated in `0.27.0` and will be removed "
"in `0.30.0`. Please use 'supervision.key_points' instead.",
DeprecationWarning,
stacklevel=2,
)
from supervision.key_points.core import KeyPoints # noqa: F401, E402
+4 -5
View File
@@ -6,11 +6,11 @@ from dataclasses import dataclass
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from deprecate import deprecated
from supervision.dataset.core import DetectionDataset
from supervision.detection.core import Detections
from supervision.detection.utils.iou_and_nms import box_iou_batch
from supervision.utils.internal import deprecated
def detections_to_tensor(
@@ -495,10 +495,9 @@ class ConfusionMatrix:
@deprecated(
"`MeanAveragePrecision` is deprecated and will be removed in "
"`supervision-0.31.0`. Use "
"`supervision.metrics.mean_average_precision.MeanAveragePrecision` instead. The "
"deprecated implementation provides results that are inconsistent with pycocotools."
target=None,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
@dataclass(frozen=True)
class MeanAveragePrecision:
+28
View File
@@ -2,6 +2,7 @@ from functools import wraps
import cv2
import numpy as np
from deprecate import deprecated
from PIL import Image
from supervision.draw.base import ImageType
@@ -32,6 +33,15 @@ def ensure_cv2_image_for_class_method(annotate_func):
return wrapper
@deprecated(
target=ensure_cv2_image_for_class_method,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def ensure_cv2_image_for_annotation(annotate_func):
pass
def ensure_cv2_image_for_standalone_function(image_processing_fun):
"""
Decorates image processing functions that accept np.ndarray, converting `image` to
@@ -79,6 +89,24 @@ def ensure_pil_image_for_class_method(annotate_func):
return wrapper
@deprecated(
target=ensure_pil_image_for_class_method,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def ensure_pil_image_for_annotation(annotate_func):
pass
@deprecated(
target=ensure_cv2_image_for_standalone_function,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def ensure_cv2_image_for_processing(image_processing_fun):
pass
def images_to_cv2(images: list[ImageType]) -> list[np.ndarray]:
"""
Converts images provided either as Pillow images or OpenCV
+375 -3
View File
@@ -1,19 +1,33 @@
from __future__ import annotations
import itertools
import math
import os
import shutil
from collections.abc import Callable
from functools import partial
from typing import Literal
import cv2
import numpy as np
import numpy.typing as npt
from deprecate import deprecated
from PIL import Image
from supervision.annotators.base import ImageType
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 (
cv2_to_pillow,
ensure_cv2_image_for_standalone_function,
images_to_cv2,
)
from supervision.utils.internal import deprecated
from supervision.utils.iterables import create_batches, fill
RelativePosition = Literal["top", "bottom"]
MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
def crop_image(
@@ -284,8 +298,9 @@ def letterbox_image(
@deprecated(
"`overlay_image` function is deprecated and will be removed in "
"`supervision-0.32.0`. Use `draw_image` instead."
target=None,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def overlay_image(
image: npt.NDArray[np.uint8],
@@ -627,3 +642,360 @@ class ImageSink:
def __exit__(self, exc_type, exc_value, exc_traceback):
pass
@deprecated(
target=None,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def create_tiles(
images: list[ImageType],
grid_size: tuple[int | None, int | None] | None = None,
single_tile_size: tuple[int, int] | None = None,
tile_scaling: Literal["min", "max", "avg"] = "avg",
tile_padding_color: tuple[int, int, int] | Color = Color.from_hex("#D9D9D9"),
tile_margin: int = 10,
tile_margin_color: tuple[int, int, int] | Color = Color.from_hex("#BFBEBD"),
return_type: Literal["auto", "cv2", "pillow"] = "auto",
titles: list[str | None] | None = None,
titles_anchors: Point | list[Point | None] | None = None,
titles_color: tuple[int, int, int] | Color = Color.from_hex("#262523"),
titles_scale: float | None = None,
titles_thickness: int = 1,
titles_padding: int = 10,
titles_text_font: int = cv2.FONT_HERSHEY_SIMPLEX,
titles_background_color: tuple[int, int, int] | Color = Color.from_hex("#D9D9D9"),
default_title_placement: RelativePosition = "top",
) -> ImageType:
"""
Creates tiles mosaic from input images, automating grid placement and
converting images to common resolution maintaining aspect ratio. It is
also possible to render text titles on tiles, using optional set of
parameters specifying text drawing (see parameters description).
Automated grid placement will try to maintain square shape of grid
(with size being the nearest integer square root of #images), up to two exceptions:
* if there are up to 3 images - images will be displayed in single row
* if square-grid placement causes last row to be empty - number of rows is trimmed
until last row has at least one image
Args:
images (List[ImageType]): Images to create tiles. Elements can be either
np.ndarray or PIL.Image, common representation will be agreed by the
function.
grid_size (Optional[Tuple[Optional[int], Optional[int]]]): Expected grid
size in format (n_rows, n_cols). If not given - automated grid placement
will be applied. One may also provide only one out of two elements of the
tuple - then grid will be created with either n_rows or n_cols fixed,
leaving the other dimension to be adjusted by the number of images
single_tile_size (Optional[Tuple[int, int]]): sizeof a single tile element
provided in (width, height) format. If not given - size of tile will be
automatically calculated based on `tile_scaling` parameter.
tile_scaling (Literal["min", "max", "avg"]): If `single_tile_size` is not
given - parameter will be used to calculate tile size - using
min / max / avg size of image provided in `images` list.
tile_padding_color (Union[Tuple[int, int, int], sv.Color]): Color to be used in
images letterbox procedure (while standardising tiles sizes) as a padding.
If tuple provided - should be BGR.
tile_margin (int): size of margin between tiles (in pixels)
tile_margin_color (Union[Tuple[int, int, int], sv.Color]): Color of tile margin.
If tuple provided - should be BGR.
return_type (Literal["auto", "cv2", "pillow"]): Parameter dictates the format of
return image. One may choose specific type ("cv2" or "pillow") to enforce
conversion. "auto" mode takes a majority vote between types of elements in
`images` list - resolving draws in favour of OpenCV format. "auto" can be
safely used when all input images are of the same type.
titles (Optional[List[Optional[str]]]): Optional titles to be added to tiles.
Elements of that list may be empty - then specific tile (in order presented
in `images` parameter) will not be filled with title. It is possible to
provide list of titles shorter than `images` - then remaining titles will
be assumed empty.
titles_anchors (Optional[Union[Point, List[Optional[Point]]]]): Parameter to
specify anchor points for titles. It is possible to specify anchor either
globally or for specific tiles (following order of `images`).
If not given (either globally, or for specific element of the list),
it will be calculated automatically based on `default_title_placement`.
titles_color (Union[Tuple[int, int, int], Color]): Color of titles text.
If tuple provided - should be BGR.
titles_scale (Optional[float]): Scale of titles. If not provided - value will
be calculated using `calculate_optimal_text_scale(...)`.
titles_thickness (int): Thickness of titles text.
titles_padding (int): Size of titles padding.
titles_text_font (int): Font to be used to render titles. Must be integer
constant representing OpenCV font.
(See docs: https://docs.opencv.org/4.x/d6/d6e/group__imgproc__draw.html)
titles_background_color (Union[Tuple[int, int, int], Color]): Color of title
text padding.
default_title_placement (Literal["top", "bottom"]): Parameter specifies title
anchor placement in case if explicit anchor is not provided.
Returns:
ImageType: Image with all input images located in tails grid. The output type is
determined by `return_type` parameter.
Raises:
ValueError: In case when input images list is empty, provided `grid_size` is too
small to fit all images, `tile_scaling` mode is invalid.
"""
if len(images) == 0:
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 = 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, resolution_wh=single_tile_size, color=tile_padding_color
)
for i in images
]
grid_size = _establish_grid_size(images=images, grid_size=grid_size)
if len(images) > grid_size[0] * grid_size[1]:
raise ValueError(
f"Could not place {len(images)} in grid with size: {grid_size}."
)
if titles is not None:
titles = fill(sequence=titles, desired_size=len(images), content=None)
titles_anchors = (
[titles_anchors]
if not issubclass(type(titles_anchors), list)
else titles_anchors
)
titles_anchors = fill(
sequence=titles_anchors, desired_size=len(images), content=None
)
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,
single_tile_size=single_tile_size,
tile_padding_color=tile_padding_color,
tile_margin=tile_margin,
tile_margin_color=tile_margin_color,
titles=titles,
titles_anchors=titles_anchors,
titles_color=titles_color,
titles_scale=titles_scale,
titles_thickness=titles_thickness,
titles_padding=titles_padding,
titles_text_font=titles_text_font,
titles_background_color=titles_background_color,
default_title_placement=default_title_placement,
)
if return_type == "pillow":
tiles = cv2_to_pillow(image=tiles)
return tiles
def _negotiate_tiles_format(images: list[ImageType]) -> Literal["cv2", "pillow"]:
number_of_np_arrays = sum(issubclass(type(i), np.ndarray) for i in images)
if number_of_np_arrays >= (len(images) // 2):
return "cv2"
return "pillow"
def _calculate_aggregated_images_shape(
images: list[np.ndarray], aggregator: Callable[[list[int]], float]
) -> tuple[int, int]:
height = round(aggregator([i.shape[0] for i in images]))
width = round(aggregator([i.shape[1] for i in images]))
return width, height
SHAPE_AGGREGATION_FUN = {
"min": partial(_calculate_aggregated_images_shape, aggregator=np.min),
"max": partial(_calculate_aggregated_images_shape, aggregator=np.max),
"avg": partial(_calculate_aggregated_images_shape, aggregator=np.average),
}
def _aggregate_images_shape(
images: list[np.ndarray], mode: Literal["min", "max", "avg"]
) -> tuple[int, int]:
if mode not in SHAPE_AGGREGATION_FUN:
raise ValueError(
f"Could not aggregate images shape - provided unknown mode: {mode}. "
f"Supported modes: {list(SHAPE_AGGREGATION_FUN.keys())}."
)
return SHAPE_AGGREGATION_FUN[mode](images)
def _establish_grid_size(
images: list[np.ndarray], grid_size: tuple[int | None, int | None] | None
) -> tuple[int, int]:
if grid_size is None or all(e is None for e in grid_size):
return _negotiate_grid_size(images=images)
if grid_size[0] is None:
return math.ceil(len(images) / grid_size[1]), grid_size[1]
if grid_size[1] is None:
return grid_size[0], math.ceil(len(images) / grid_size[0])
return grid_size
def _negotiate_grid_size(images: list[np.ndarray]) -> tuple[int, int]:
if len(images) <= MAX_COLUMNS_FOR_SINGLE_ROW_GRID:
return 1, len(images)
nearest_sqrt = math.ceil(np.sqrt(len(images)))
proposed_columns = nearest_sqrt
proposed_rows = nearest_sqrt
while proposed_columns * (proposed_rows - 1) >= len(images):
proposed_rows -= 1
return proposed_rows, proposed_columns
def _generate_tiles(
images: list[np.ndarray],
grid_size: tuple[int, int],
single_tile_size: tuple[int, int],
tile_padding_color: tuple[int, int, int],
tile_margin: int,
tile_margin_color: tuple[int, int, int],
titles: list[str | None] | None,
titles_anchors: list[Point | None],
titles_color: tuple[int, int, int],
titles_scale: float | None,
titles_thickness: int,
titles_padding: int,
titles_text_font: int,
titles_background_color: tuple[int, int, int],
default_title_placement: RelativePosition,
) -> np.ndarray:
images = _draw_texts(
images=images,
titles=titles,
titles_anchors=titles_anchors,
titles_color=titles_color,
titles_scale=titles_scale,
titles_thickness=titles_thickness,
titles_padding=titles_padding,
titles_text_font=titles_text_font,
titles_background_color=titles_background_color,
default_title_placement=default_title_placement,
)
rows, columns = grid_size
tiles_elements = list(create_batches(sequence=images, batch_size=columns))
while len(tiles_elements[-1]) < columns:
tiles_elements[-1].append(
_generate_color_image(shape=single_tile_size, color=tile_padding_color)
)
while len(tiles_elements) < rows:
tiles_elements.append(
[_generate_color_image(shape=single_tile_size, color=tile_padding_color)]
* columns
)
return _merge_tiles_elements(
tiles_elements=tiles_elements,
grid_size=grid_size,
single_tile_size=single_tile_size,
tile_margin=tile_margin,
tile_margin_color=tile_margin_color,
)
def _draw_texts(
images: list[np.ndarray],
titles: list[str | None] | None,
titles_anchors: list[Point | None],
titles_color: tuple[int, int, int],
titles_scale: float | None,
titles_thickness: int,
titles_padding: int,
titles_text_font: int,
titles_background_color: tuple[int, int, int],
default_title_placement: RelativePosition,
) -> list[np.ndarray]:
if titles is None:
return images
titles_anchors = _prepare_default_titles_anchors(
images=images,
titles_anchors=titles_anchors,
default_title_placement=default_title_placement,
)
if titles_scale is None:
image_height, image_width = images[0].shape[:2]
titles_scale = calculate_optimal_text_scale(
resolution_wh=(image_width, image_height)
)
result = []
for image, text, anchor in zip(images, titles, titles_anchors):
if text is None:
result.append(image)
continue
processed_image = draw_text(
scene=image,
text=text,
text_anchor=anchor,
text_color=Color.from_bgr_tuple(titles_color),
text_scale=titles_scale,
text_thickness=titles_thickness,
text_padding=titles_padding,
text_font=titles_text_font,
background_color=Color.from_bgr_tuple(titles_background_color),
)
result.append(processed_image)
return result
def _prepare_default_titles_anchors(
images: list[np.ndarray],
titles_anchors: list[Point | None],
default_title_placement: RelativePosition,
) -> list[Point]:
result = []
for image, anchor in zip(images, titles_anchors):
if anchor is not None:
result.append(anchor)
continue
image_height, image_width = image.shape[:2]
if default_title_placement == "top":
default_anchor = Point(x=image_width / 2, y=image_height * 0.1)
else:
default_anchor = Point(x=image_width / 2, y=image_height * 0.9)
result.append(default_anchor)
return result
def _merge_tiles_elements(
tiles_elements: list[list[np.ndarray]],
grid_size: tuple[int, int],
single_tile_size: tuple[int, int],
tile_margin: int,
tile_margin_color: tuple[int, int, int],
) -> np.ndarray:
vertical_padding = (
np.ones((single_tile_size[1], tile_margin, 3)) * tile_margin_color
)
merged_rows = [
np.concatenate(
list(
itertools.chain.from_iterable(
zip(row, [vertical_padding] * grid_size[1])
)
)[:-1],
axis=1,
)
for row in tiles_elements
]
row_width = merged_rows[0].shape[1]
horizontal_padding = (
np.ones((tile_margin, row_width, 3), dtype=np.uint8) * tile_margin_color
)
rows_with_paddings = []
for row in merged_rows:
rows_with_paddings.append(row)
rows_with_paddings.append(horizontal_padding)
return np.concatenate(
rows_with_paddings[:-1],
axis=0,
).astype(np.uint8)
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
-24
View File
@@ -113,30 +113,6 @@ def deprecated_parameter(
return decorator
def deprecated(reason: str):
def decorator(cls_or_func):
if inspect.isclass(cls_or_func):
original_init = cls_or_func.__init__
@functools.wraps(original_init)
def new_init(self, *args, **kwargs):
warn_deprecated(f"{cls_or_func.__name__} is deprecated: {reason}")
original_init(self, *args, **kwargs)
cls_or_func.__init__ = new_init
return cls_or_func
else:
@functools.wraps(cls_or_func)
def wrapper(*args, **kwargs):
warn_deprecated(f"{cls_or_func.__name__} is deprecated: {reason}")
return cls_or_func(*args, **kwargs)
return wrapper
return decorator
T = TypeVar("T")
+21
View File
@@ -1,6 +1,7 @@
from typing import Any
import numpy as np
from deprecate import deprecated
def validate_xyxy(xyxy: Any) -> None:
@@ -66,6 +67,15 @@ def validate_key_point_confidence(confidence: Any, n: int, m: int) -> None:
)
@deprecated(
target=validate_key_point_confidence,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def validate_keypoint_confidence(confidence: Any, n: int, m: int) -> None:
pass
def validate_tracker_id(tracker_id: Any, n: int) -> None:
expected_shape = f"({n},)"
actual_shape = str(getattr(tracker_id, "shape", None))
@@ -140,6 +150,17 @@ def validate_key_points_fields(
validate_data(data, n)
@deprecated(
target=validate_key_points_fields,
deprecated_in="0.27.0",
remove_in="0.31.0",
)
def validate_keypoints_fields(
xy: Any, class_id: Any, confidence: Any, data: dict[str, Any]
) -> None:
pass
def validate_resolution(resolution: Any) -> tuple[int, int]:
if not (isinstance(resolution, tuple) and len(resolution) == 2):
raise ValueError(
+5 -9
View File
@@ -417,31 +417,27 @@ def test_drop_extra_matches(
@pytest.mark.parametrize(
"recall, precision, expected_result, exception",
"recall, precision, expected_result",
[
(
np.array([1.0]),
np.array([1.0]),
1.0,
DoesNotRaise(),
), # perfect recall and precision
(
np.array([0.0]),
np.array([0.0]),
0.0,
DoesNotRaise(),
), # no recall and precision
(
np.array([0.0, 0.2, 0.2, 0.8, 0.8, 1.0]),
np.array([0.7, 0.8, 0.4, 0.5, 0.1, 0.2]),
0.5,
DoesNotRaise(),
),
(
np.array([0.0, 0.5, 0.5, 1.0]),
np.array([0.75, 0.75, 0.75, 0.75]),
0.75,
DoesNotRaise(),
),
],
)
@@ -449,10 +445,10 @@ def test_compute_average_precision(
recall: np.ndarray,
precision: np.ndarray,
expected_result: float,
exception: Exception,
) -> None:
with exception:
result = MeanAveragePrecision.compute_average_precision(
with DoesNotRaise():
# FIX: Use .__func__ to support Python 3.9
result = MeanAveragePrecision.compute_average_precision.__func__(
recall=recall, precision=precision
)
assert_almost_equal(result, expected_result, tolerance=0.01)
assert_almost_equal(result, expected_result, tolerance=0.01)