supervision/supervision/keypoint/annotators.py

171 lines
5.4 KiB
Python

from abc import ABC, abstractmethod
from logging import warn
from typing import List, Optional, Tuple
import cv2
import numpy as np
from supervision.annotators.base import ImageType
from supervision.draw.color import Color
from supervision.keypoint.core import KeyPoints
from supervision.keypoint.skeletons import SKELETONS_BY_VERTEX_COUNT
from supervision.utils.conversion import convert_for_annotation_method
class BaseKeyPointAnnotator(ABC):
@abstractmethod
def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
pass
class VertexAnnotator(BaseKeyPointAnnotator):
"""
A class that specializes in drawing skeleton vertices on images. It uses
specified key points to determine the locations where the vertices should be
drawn.
"""
def __init__(
self,
color: Color = Color.ROBOFLOW,
radius: int = 4,
) -> None:
"""
Args:
color (Color, optional): The color to use for annotating key points.
radius (int, optional): The radius of the circles used to represent the key
points.
"""
self.color = color
self.radius = radius
@convert_for_annotation_method
def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
"""
Annotates the given scene with skeleton vertices based on the provided key
points. It draws circles at each key point location.
Args:
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
is a flexible type, accepting either `numpy.ndarray` or
`PIL.Image.Image`.
key_points (KeyPoints): A collection of key points where each key point
consists of x and y coordinates.
Returns:
The annotated image, matching the type of `scene` (`numpy.ndarray`
or `PIL.Image.Image`)
Example:
```python
import supervision as sv
image = ...
key_points = sv.KeyPoints(...)
vertex_annotator = sv.VertexAnnotator()
annotated_frame = vertex_annotator.annotate(
scene=image.copy(),
key_points=key_points
)
```
"""
if len(key_points) == 0:
return scene
for xy in key_points.xy:
for x, y in xy:
cv2.circle(
img=scene,
center=(int(x), int(y)),
radius=self.radius,
color=self.color.as_bgr(),
thickness=-1,
)
return scene
class EdgeAnnotator(BaseKeyPointAnnotator):
"""
A class that specializes in drawing skeleton edges on images using specified key
points. It connects key points with lines to form the skeleton structure.
"""
def __init__(
self,
color: Color = Color.ROBOFLOW,
thickness: int = 2,
edges: Optional[List[Tuple[int, int]]] = None,
) -> None:
"""
Args:
color (Color, optional): The color to use for the edges.
thickness (int, optional): The thickness of the edges.
edges (Optional[List[Tuple[int, int]]]): The edges to draw.
If set to `None`, will attempt to select automatically.
"""
self.color = color
self.thickness = thickness
self.edges = edges
@convert_for_annotation_method
def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
"""
Annotates the given scene by drawing lines between specified key points to form
edges.
Args:
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
is a flexible type, accepting either `numpy.ndarray` or
`PIL.Image.Image`.
key_points (KeyPoints): A collection of key points where each key point
consists of x and y coordinates.
Returns:
Returns:
The annotated image, matching the type of `scene` (`numpy.ndarray`
or `PIL.Image.Image`)
Example:
```python
import supervision as sv
image = ...
key_points = sv.KeyPoints(...)
edge_annotator = sv.EdgeAnnotator()
annotated_frame = edge_annotator.annotate(
scene=image.copy(),
key_points=key_points
)
```
"""
if len(key_points) == 0:
return scene
for xy in key_points.xy:
edges = self.edges
if not edges:
edges = SKELETONS_BY_VERTEX_COUNT.get(len(xy))
if not edges:
warn(f"No skeleton found with {len(xy)} vertices")
return scene
for class_a, class_b in edges:
xy_a = xy[class_a - 1]
xy_b = xy[class_b - 1]
missing_a = np.allclose(xy_a, 0)
missing_b = np.allclose(xy_b, 0)
if missing_a or missing_b:
continue
cv2.line(
img=scene,
pt1=(int(xy_a[0]), int(xy_a[1])),
pt2=(int(xy_b[0]), int(xy_b[1])),
color=self.color.as_bgr(),
thickness=self.thickness,
)
return scene