diff --git a/README.md b/README.md index beed655c..0894a6a4 100644 --- a/README.md +++ b/README.md @@ -71,16 +71,15 @@ len(detections) ```python import cv2 import supervision as sv - from inference.models.utils import get_roboflow_model + from inference import get_model image = cv2.imread(...) - model = get_roboflow_model(model_id="yolov8s-640", api_key=) + model = get_model(model_id="yolov8s-640", api_key=) result = model.infer(image)[0] detections = sv.Detections.from_inference(result) len(detections) - # 5 - + # 5 ``` diff --git a/docs/assets.md b/docs/assets.md index 7e0d7dff..3fd293ee 100644 --- a/docs/assets.md +++ b/docs/assets.md @@ -19,13 +19,13 @@ as an extra within the Supervision package. ```
-

download_assets

+

download_assets

:::supervision.assets.downloader.download_assets
-

VideoAssets

+

VideoAssets

:::supervision.assets.list.VideoAssets diff --git a/docs/deprecated.md b/docs/deprecated.md index 35c332e4..cc1e9649 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -12,7 +12,7 @@ These features are phased out due to better alternatives or potential issues in - The method `Color.green()` is deprecated and will be removed in `supervision-0.22.0`. Use the constant `Color.GREEN` instead. - The method `Color.blue()` is deprecated and will be removed in `supervision-0.22.0`. Use the constant `Color.BLUE` instead. - The method [`ColorPalette.default()`](draw/color.md/#supervision.draw.color.ColorPalette.default) is deprecated and will be removed in `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](draw/color.md/#supervision.draw.color.ColorPalette.DEFAULT) instead. -- `BoxAnnotator` is deprecated and will be removed in `supervision-0.22.0`. Use [`BoundingBoxAnnotator`](annotators.md/#supervision.annotators.core.BoundingBoxAnnotator) and [`LabelAnnotator`](annotators.md/#supervision.annotators.core.LabelAnnotator) instead. +- `BoxAnnotator` is deprecated and will be removed in `supervision-0.22.0`. Use [`BoundingBoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoundingBoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead. - The method [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) is deprecated and will be removed in `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead. - The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByterTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead. - The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) is deprecated and will be removed in `supervision-0.23.0`. Use `triggering_anchors ` instead. diff --git a/docs/annotators.md b/docs/detection/annotators.md similarity index 100% rename from docs/annotators.md rename to docs/detection/annotators.md diff --git a/docs/detection/metrics.md b/docs/detection/metrics.md new file mode 100644 index 00000000..a68f0f96 --- /dev/null +++ b/docs/detection/metrics.md @@ -0,0 +1,17 @@ +--- +comments: true +--- + +# Metrics + + + +:::supervision.metrics.detection.ConfusionMatrix + + + +:::supervision.metrics.detection.MeanAveragePrecision diff --git a/docs/detection/utils.md b/docs/detection/utils.md index cc4eff5d..abacdc21 100644 --- a/docs/detection/utils.md +++ b/docs/detection/utils.md @@ -6,67 +6,67 @@ status: new # Detection Utils
-

box_iou_batch

+

box_iou_batch

:::supervision.detection.utils.box_iou_batch
-

mask_iou_batch

+

mask_iou_batch

:::supervision.detection.utils.mask_iou_batch
-

box_non_max_suppression

+

box_non_max_suppression

:::supervision.detection.utils.box_non_max_suppression
-

mask_non_max_suppression

+

mask_non_max_suppression

:::supervision.detection.utils.mask_non_max_suppression
-

polygon_to_mask

+

polygon_to_mask

:::supervision.detection.utils.polygon_to_mask
-

mask_to_xyxy

+

mask_to_xyxy

:::supervision.detection.utils.mask_to_xyxy
-

mask_to_polygons

+

mask_to_polygons

:::supervision.detection.utils.mask_to_polygons
-

polygon_to_xyxy

+

polygon_to_xyxy

:::supervision.detection.utils.polygon_to_xyxy
-

filter_polygons_by_area

+

filter_polygons_by_area

:::supervision.detection.utils.filter_polygons_by_area
-

move_boxes

+

move_boxes

:::supervision.detection.utils.move_boxes
-

scale_boxes

+

scale_boxes

:::supervision.detection.utils.scale_boxes diff --git a/docs/draw/color.md b/docs/draw/color.md deleted file mode 100644 index c702814a..00000000 --- a/docs/draw/color.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -comments: true ---- - -# Color - -:::supervision.draw.color.Color - -
-

ColorPalette

-
- -:::supervision.draw.color.ColorPalette diff --git a/docs/draw/utils.md b/docs/draw/utils.md deleted file mode 100644 index 7975952c..00000000 --- a/docs/draw/utils.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -comments: true ---- - -# Draw Utils - -
-

draw_line

-
- -:::supervision.draw.utils.draw_line - -
-

draw_rectangle

-
- -:::supervision.draw.utils.draw_rectangle - -
-

draw_filled_rectangle

-
- -:::supervision.draw.utils.draw_filled_rectangle - -
-

draw_polygon

-
- -:::supervision.draw.utils.draw_polygon - -
-

draw_text

-
- -:::supervision.draw.utils.draw_text - -
-

draw_image

-
- -:::supervision.draw.utils.draw_image - -
-

calculate_optimal_font_scale

-
- -:::supervision.draw.utils.calculate_optimal_text_scale - -
-

calculate_optimal_line_thickness

-
- -:::supervision.draw.utils.calculate_optimal_line_thickness diff --git a/docs/geometry/core.md b/docs/geometry/core.md deleted file mode 100644 index ca99b626..00000000 --- a/docs/geometry/core.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -comments: true ---- - -# Position - -:::supervision.geometry.core.Position diff --git a/docs/how_to/detect_and_annotate.md b/docs/how_to/detect_and_annotate.md index adea95cf..a9a4405e 100644 --- a/docs/how_to/detect_and_annotate.md +++ b/docs/how_to/detect_and_annotate.md @@ -27,7 +27,7 @@ model. from inference import get_model model = get_model(model_id="yolov8n-640") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] ``` @@ -38,7 +38,7 @@ model. from ultralytics import YOLO model = YOLO("yolov8n.pt") - image = cv2.imread() + image = cv2.imread() results = model(image)[0] ``` @@ -52,7 +52,7 @@ model. processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") - image = Image.open() + image = Image.open() inputs = processor(images=image, return_tensors="pt") with torch.no_grad(): @@ -78,7 +78,7 @@ Now that we have predictions from a model, we can load them into Supervision. from inference import get_model model = get_model(model_id="yolov8n-640") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] detections = sv.Detections.from_inference(results) ``` @@ -93,7 +93,7 @@ Now that we have predictions from a model, we can load them into Supervision. from ultralytics import YOLO model = YOLO("yolov8n.pt") - image = cv2.imread() + image = cv2.imread() results = model(image)[0] detections = sv.Detections.from_ultralytics(results) ``` @@ -111,7 +111,7 @@ Now that we have predictions from a model, we can load them into Supervision. processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") - image = Image.open() + image = Image.open() inputs = processor(images=image, return_tensors="pt") with torch.no_grad(): @@ -146,7 +146,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi from inference import get_model model = get_model(model_id="yolov8n-640") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] detections = sv.Detections.from_inference(results) @@ -167,7 +167,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi from ultralytics import YOLO model = YOLO("yolov8n.pt") - image = cv2.imread() + image = cv2.imread() results = model(image)[0] detections = sv.Detections.from_ultralytics(results) @@ -191,7 +191,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") - image = Image.open() + image = Image.open() inputs = processor(images=image, return_tensors="pt") with torch.no_grad(): @@ -230,7 +230,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me from inference import get_model model = get_model(model_id="yolov8n-640") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] detections = sv.Detections.from_inference(results) @@ -257,7 +257,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me from ultralytics import YOLO model = YOLO("yolov8n.pt") - image = cv2.imread() + image = cv2.imread() results = model(image)[0] detections = sv.Detections.from_ultralytics(results) @@ -287,7 +287,7 @@ override this behavior by passing a list of custom `labels` to the `annotate` me processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") - image = Image.open() + image = Image.open() inputs = processor(images=image, return_tensors="pt") with torch.no_grad(): @@ -334,7 +334,7 @@ that will allow you to draw masks instead of boxes. from inference import get_model model = get_model(model_id="yolov8n-seg-640") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] detections = sv.Detections.from_inference(results) @@ -355,7 +355,7 @@ that will allow you to draw masks instead of boxes. from ultralytics import YOLO model = YOLO("yolov8n-seg.pt") - image = cv2.imread() + image = cv2.imread() results = model(image)[0] detections = sv.Detections.from_ultralytics(results) diff --git a/docs/how_to/detect_small_objects.md b/docs/how_to/detect_small_objects.md index 9f35a96c..e2d02328 100644 --- a/docs/how_to/detect_small_objects.md +++ b/docs/how_to/detect_small_objects.md @@ -28,7 +28,7 @@ size relative to the image resolution. from inference import get_model model = get_model(model_id="yolov8x-640") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] detections = sv.Detections.from_inference(results) @@ -49,7 +49,7 @@ size relative to the image resolution. from ultralytics import YOLO model = YOLO("yolov8x.pt") - image = cv2.imread() + image = cv2.imread() results = model(image)[0] detections = sv.Detections.from_ultralytics(results) @@ -73,7 +73,7 @@ size relative to the image resolution. processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") - image = Image.open() + image = Image.open() inputs = processor(images=image, return_tensors="pt") with torch.no_grad(): @@ -116,7 +116,7 @@ is less effective for ultra-high-resolution images (4K and above). from inference import get_model model = get_model(model_id="yolov8x-1280") - image = cv2.imread() + image = cv2.imread() results = model.infer(image)[0] detections = sv.Detections.from_inference(results) @@ -137,7 +137,7 @@ is less effective for ultra-high-resolution images (4K and above). from ultralytics import YOLO model = YOLO("yolov8x.pt") - image = cv2.imread() + image = cv2.imread() results = model(image, imgsz=1280)[0] detections = sv.Detections.from_ultralytics(results) @@ -171,7 +171,7 @@ objects within each, and aggregating the results. from inference import get_model model = get_model(model_id="yolov8x-640") - image = cv2.imread() + image = cv2.imread() def callback(image_slice: np.ndarray) -> sv.Detections: results = model.infer(image_slice)[0] @@ -198,7 +198,7 @@ objects within each, and aggregating the results. from ultralytics import YOLO model = YOLO("yolov8x.pt") - image = cv2.imread() + image = cv2.imread() def callback(image_slice: np.ndarray) -> sv.Detections: result = model(image_slice)[0] @@ -229,7 +229,7 @@ objects within each, and aggregating the results. processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") - image = cv2.imread() + image = cv2.imread() def callback(image_slice: np.ndarray) -> sv.Detections: image_slice = cv2.cvtColor(image_slice, cv2.COLOR_BGR2RGB) diff --git a/docs/keypoint/annotate.md b/docs/keypoint/annotate.md deleted file mode 100644 index 4f9cd861..00000000 --- a/docs/keypoint/annotate.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -comments: true -status: new ---- - -# Annotate - -:::supervision.keypoint.annotate.VertexAnnotator - -:::supervision.keypoint.annotate.EdgeAnnotator diff --git a/docs/keypoint/annotators.md b/docs/keypoint/annotators.md new file mode 100644 index 00000000..b5f998bc --- /dev/null +++ b/docs/keypoint/annotators.md @@ -0,0 +1,60 @@ +--- +comments: true +status: new +--- + +# Annotators + +=== "VertexAnnotator" + + ```python + import supervision as sv + + image = ... + key_points = sv.KeyPoints(...) + + vertex_annotator = sv.VertexAnnotator(color=sv.Color.GREEN, radius=10) + annotated_frame = vertex_annotator.annotate( + scene=image.copy(), + key_points=key_points + ) + ``` + +
+ + ![vertex-annotator-example](https://media.roboflow.com/supervision-annotator-examples/vertex-annotator-example.png){ align=center width="800" } + +
+ +=== "EdgeAnnotator" + + ```python + import supervision as sv + + image = ... + key_points = sv.KeyPoints(...) + + edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5) + annotated_frame = edge_annotator.annotate( + scene=image.copy(), + key_points=key_points + ) + ``` + +
+ + ![edge-annotator-example](https://media.roboflow.com/supervision-annotator-examples/edge-annotator-example.png){ align=center width="800" } + +
+ + + +:::supervision.keypoint.annotators.VertexAnnotator + + + +:::supervision.keypoint.annotators.EdgeAnnotator diff --git a/docs/metrics/detection.md b/docs/metrics/detection.md deleted file mode 100644 index a953ba9b..00000000 --- a/docs/metrics/detection.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -comments: true ---- - -#  Detection Metrics - -!!! warning - - Evaluation API is still fluid and may change. If you use Evaluation API in your project until further notice, freeze the - `supervision` version in your `requirements.txt` or `setup.py`. - - - -:::supervision.metrics.detection.ConfusionMatrix - - - -:::supervision.metrics.detection.MeanAveragePrecision diff --git a/docs/utils/draw.md b/docs/utils/draw.md new file mode 100644 index 00000000..84758e06 --- /dev/null +++ b/docs/utils/draw.md @@ -0,0 +1,65 @@ +--- +comments: true +--- + +# Draw Utils + + + +:::supervision.draw.utils.draw_line + + + +:::supervision.draw.utils.draw_rectangle + + + +:::supervision.draw.utils.draw_filled_rectangle + + + +:::supervision.draw.utils.draw_polygon + + + +:::supervision.draw.utils.draw_text + + + +:::supervision.draw.utils.draw_image + + + +:::supervision.draw.utils.calculate_optimal_text_scale + + + +:::supervision.draw.utils.calculate_optimal_line_thickness + +
+

Color

+
+ +:::supervision.draw.color.Color + + + +:::supervision.draw.color.ColorPalette diff --git a/docs/utils/file.md b/docs/utils/file.md index 2ba5a015..a2a08185 100644 --- a/docs/utils/file.md +++ b/docs/utils/file.md @@ -5,7 +5,7 @@ comments: true # File Utils
-

list_files_with_extensions

+

list_files_with_extensions

:::supervision.utils.file.list_files_with_extensions diff --git a/docs/utils/geometry.md b/docs/utils/geometry.md new file mode 100644 index 00000000..8c92f562 --- /dev/null +++ b/docs/utils/geometry.md @@ -0,0 +1,9 @@ +--- +comments: true +--- + + + +:::supervision.geometry.core.Position diff --git a/docs/utils/image.md b/docs/utils/image.md index 87fd1943..8f170d35 100644 --- a/docs/utils/image.md +++ b/docs/utils/image.md @@ -6,37 +6,37 @@ status: new # Image Utils
-

crop_image

+

crop_image

:::supervision.utils.image.crop_image
-

scale_image

+

crop_image

:::supervision.utils.image.scale_image
-

resize_image

+

resize_image

:::supervision.utils.image.resize_image
-

letterbox_image

+

letterbox_image

:::supervision.utils.image.letterbox_image
-

overlay_image

+

overlay_image

:::supervision.utils.image.overlay_image
-

ImageSink

+

ImageSink

:::supervision.utils.image.ImageSink diff --git a/docs/utils/iterables.md b/docs/utils/iterables.md index 3ccf98e5..b65cd954 100644 --- a/docs/utils/iterables.md +++ b/docs/utils/iterables.md @@ -6,13 +6,13 @@ status: new # Iterables Utils
-

create_batches

+

create_batches

:::supervision.utils.iterables.create_batches
-

fill

+

fill

:::supervision.utils.iterables.fill diff --git a/docs/utils/notebook.md b/docs/utils/notebook.md index d09b9bbd..3eab046a 100644 --- a/docs/utils/notebook.md +++ b/docs/utils/notebook.md @@ -5,14 +5,13 @@ comments: true # Notebooks Utils
-

plot_image

+

plot_image

:::supervision.utils.notebook.plot_image
-

plot_images_grid -

+

plot_images_grid

:::supervision.utils.notebook.plot_images_grid diff --git a/docs/utils/video.md b/docs/utils/video.md index 1f58450f..f9a5821d 100644 --- a/docs/utils/video.md +++ b/docs/utils/video.md @@ -5,31 +5,31 @@ comments: true # Video Utils
-

VideoInfo

+

VideoInfo

:::supervision.utils.video.VideoInfo
-

VideoSink

+

VideoSink

:::supervision.utils.video.VideoSink
-

FPSMonitor

+

FPSMonitor

:::supervision.utils.video.FPSMonitor
-

get_video_frames_generator

+

get_video_frames_generator

:::supervision.utils.video.get_video_frames_generator
-

process_video

+

process_video

:::supervision.utils.video.process_video diff --git a/mkdocs.yml b/mkdocs.yml index 256c6bef..cf206a82 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,15 +44,16 @@ nav: - Track Objects: how_to/track_objects.md - API: - - Annotators: annotators.md - - Classifications: - - Core: classification/core.md - - Detections: + - Detection and Segmentation: - Core: detection/core.md + - Annotators: detection/annotators.md + - Metrics: detection/metrics.md - Utils: detection/utils.md - - KeyPoints: + - Keypoint Detection: - Core: keypoint/core.md - - Annotators: keypoint/annotate.md + - Annotators: keypoint/annotators.md + - Classification: + - Core: classification/core.md - Tools: - Line Zone: detection/tools/line_zone.md - Polygon Zone: detection/tools/polygon_zone.md @@ -61,19 +62,14 @@ nav: - Save Detections: detection/tools/save_detections.md - Trackers: trackers.md - Datasets: datasets.md - - Metrics: - - Object Detection: metrics/detection.md - - Draw: - - Color: draw/color.md - - Utils: draw/utils.md - - Geometry: - - Position: geometry/core.md - Utils: - Video: utils/video.md - Image: utils/image.md - Iterables: utils/iterables.md - Notebook: utils/notebook.md - File: utils/file.md + - Draw: utils/draw.md + - Geometry: utils/geometry.md - Assets: assets.md - Cookbooks: cookbooks.md - Contribute: diff --git a/supervision/__init__.py b/supervision/__init__.py index 053e3718..bb526514 100644 --- a/supervision/__init__.py +++ b/supervision/__init__.py @@ -69,7 +69,7 @@ from supervision.draw.utils import ( ) from supervision.geometry.core import Point, Position, Rect from supervision.geometry.utils import get_polygon_center -from supervision.keypoint.annotate import EdgeAnnotator, VertexAnnotator +from supervision.keypoint.annotators import EdgeAnnotator, VertexAnnotator from supervision.keypoint.core import KeyPoints from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision from supervision.tracker.byte_tracker.core import ByteTrack diff --git a/supervision/assets/list.py b/supervision/assets/list.py index 77b1e461..8a01b758 100644 --- a/supervision/assets/list.py +++ b/supervision/assets/list.py @@ -19,6 +19,7 @@ class VideoAssets(Enum): | `MARKET_SQUARE` | `market-square.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/market-square.mp4) | | `PEOPLE_WALKING` | `people-walking.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/people-walking.mp4) | | `BEACH` | `beach-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/beach-1.mp4) | + | `BASKETBALL` | `basketball-1.mp4` | [Link](https://media.roboflow.com/supervision/video-examples/basketball-1.mp4) | """ # noqa: E501 // docs VEHICLES = "vehicles.mp4" @@ -29,6 +30,7 @@ class VideoAssets(Enum): MARKET_SQUARE = "market-square.mp4" PEOPLE_WALKING = "people-walking.mp4" BEACH = "beach-1.mp4" + BASKETBALL = "basketball-1.mp4" @classmethod def list(cls): @@ -68,4 +70,8 @@ VIDEO_ASSETS: Dict[str, Tuple[str, str]] = { f"{BASE_VIDEO_URL}{VideoAssets.BEACH.value}", "4175d42fec4d450ed081523fd39e0cf8", ), + VideoAssets.BASKETBALL.value: ( + f"{BASE_VIDEO_URL}{VideoAssets.BASKETBALL.value}", + "60d94a3c7c47d16f09d342b088012ecc", + ), } diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 53286375..1900954d 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -27,34 +27,71 @@ from supervision.validators import validate_detections_fields @dataclass class Detections: """ - The `sv.Detections` allows you to convert results from a variety of object detection - and segmentation models into a single, unified format. The `sv.Detections` class - enables easy data manipulation and filtering, and provides a consistent API for - Supervision's tools like trackers, annotators, and zones. + The `sv.Detections` class in the Supervision library standardizes results from + various object detection and segmentation models into a consistent format. This + class simplifies data manipulation and filtering, providing a uniform API for + integration with Supervision [trackers](/trackers/), [annotators](/detection/annotators/), and [tools](/detection/tools/line_zone/). - ```python - import cv2 - import supervision as sv - from ultralytics import YOLO + === "Inference" - image = cv2.imread() - model = YOLO('yolov8s.pt') - annotator = sv.BoundingBoxAnnotator() + Use [`sv.Detections.from_inference`](/detection/core/#supervision.detection.core.Detections.from_inference) + method, which accepts model results from both detection and segmentation models. - result = model(image)[0] - detections = sv.Detections.from_ultralytics(result) + ```python + import cv2 + import supervision as sv + from inference import get_model - annotated_image = annotator.annotate(image, detections) - ``` + model = get_model(model_id="yolov8n-640") + image = cv2.imread() + results = model.infer(image)[0] + detections = sv.Detections.from_inference(results) + ``` - !!! tip + === "Ultralytics" - In `sv.Detections`, detection data is categorized into two main field types: - fixed and custom. The fixed fields include `xyxy`, `mask`, `confidence`, - `class_id`, and `tracker_id`. For any additional data requirements, custom - fields come into play, stored in the data field. These custom fields are easily - accessible using the `detections[]` syntax, providing flexibility - for diverse data handling needs. + Use [`sv.Detections.from_ultralytics`](/detection/core/#supervision.detection.core.Detections.from_ultralytics) + method, which accepts model results from both detection and segmentation models. + + ```python + import cv2 + import supervision as sv + from ultralytics import YOLO + + model = YOLO("yolov8n.pt") + image = cv2.imread() + results = model(image)[0] + detections = sv.Detections.from_ultralytics(results) + ``` + + === "Transformers" + + Use [`sv.Detections.from_transformers`](/detection/core/#supervision.detection.core.Detections.from_transformers) + method, which accepts model results from both detection and segmentation models. + + ```python + import torch + import supervision as sv + from PIL import Image + from transformers import DetrImageProcessor, DetrForObjectDetection + + processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50") + model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50") + + image = Image.open() + inputs = processor(images=image, return_tensors="pt") + + with torch.no_grad(): + outputs = model(**inputs) + + width, height = image.size + target_size = torch.tensor([[height, width]]) + results = processor.post_process_object_detection( + outputs=outputs, target_sizes=target_size)[0] + detections = sv.Detections.from_transformers( + transformers_results=results, + id2label=model.config.id2label) + ``` Attributes: xyxy (np.ndarray): An array of shape `(n, 4)` containing @@ -70,15 +107,7 @@ class Detections: data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional data where each key is a string representing the data type, and the value is either a NumPy array or a list of corresponding data. - - !!! warning - - The `data` field in the `sv.Detections` class is currently in an experimental - phase. Please be aware that its API and functionality are subject to change in - future updates as we continue to refine and improve its capabilities. - We encourage users to experiment with this feature and provide feedback, but - also to be prepared for potential modifications in upcoming releases. - """ + """ # noqa: E501 // docs xyxy: np.ndarray mask: Optional[np.ndarray] = None @@ -177,8 +206,8 @@ class Detections: @classmethod def from_ultralytics(cls, ultralytics_results) -> Detections: """ - Creates a Detections instance from a - [YOLOv8](https://github.com/ultralytics/ultralytics) inference result. + Creates a `sv.Detections` instance from a + [YOLOv8](https://github.com/ultralytics/ultralytics) inference result. !!! Note @@ -202,10 +231,13 @@ class Detections: image = cv2.imread() model = YOLO('yolov8s.pt') - - result = model(image)[0] - detections = sv.Detections.from_ultralytics(result) + results = model(image)[0] + detections = sv.Detections.from_ultralytics(results) ``` + + !!! tip + + Class names values can be accessed using `detections["class_name"]`. """ # noqa: E501 // docs if "obb" in ultralytics_results and ultralytics_results.obb is not None: @@ -396,11 +428,6 @@ class Detections: Creates a Detections instance from object detection or segmentation [Transformer](https://github.com/huggingface/transformers) inference result. - !!! note - - Class names can be accessed using the key `class_name` in the returned - object's data attribute. - Args: transformers_results (dict): The output of Transformers model inference. A dictionary containing the `scores`, `labels`, `boxes` and `masks` keys. @@ -437,6 +464,10 @@ class Detections: id2label=model.config.id2label ) ``` + + !!! tip + + Class names values can be accessed using `detections["class_name"]`. """ # noqa: E501 // docs class_ids = transformers_results["labels"].cpu().detach().numpy().astype(int) @@ -510,17 +541,12 @@ class Detections: @classmethod def from_inference(cls, roboflow_result: Union[dict, Any]) -> Detections: """ - Create a Detections object from the [Roboflow](https://roboflow.com/) + Create a `sv.Detections` object from the [Roboflow](https://roboflow.com/) API inference result or the [Inference](https://inference.roboflow.com/) package results. This method extracts bounding boxes, class IDs, confidences, and class names from the Roboflow API result and encapsulates them into a Detections object. - !!! note - - Class names can be accessed using the key `class_name` in the returned - object's data attribute. - Args: roboflow_result (dict, any): The result from the Roboflow API or Inference package containing predictions. @@ -533,14 +559,18 @@ class Detections: ```python import cv2 import supervision as sv - from inference.models.utils import get_roboflow_model + from inference import get_model image = cv2.imread() - model = get_roboflow_model(model_id="yolov8s-640") + model = get_model(model_id="yolov8s-640") result = model.infer(image)[0] detections = sv.Detections.from_inference(result) ``` + + !!! tip + + Class names values can be accessed using `detections["class_name"]`. """ with suppress(AttributeError): roboflow_result = roboflow_result.dict(exclude_none=True, by_alias=True) @@ -590,10 +620,10 @@ class Detections: ```python import cv2 import supervision as sv - from inference.models.utils import get_roboflow_model + from inference import get_model image = cv2.imread() - model = get_roboflow_model(model_id="yolov8s-640") + model = get_model(model_id="yolov8s-640") result = model.infer(image)[0] detections = sv.Detections.from_roboflow(result) diff --git a/supervision/keypoint/annotate.py b/supervision/keypoint/annotate.py deleted file mode 100644 index b1f3530f..00000000 --- a/supervision/keypoint/annotate.py +++ /dev/null @@ -1,104 +0,0 @@ -from abc import ABC, abstractmethod -from logging import warn -from typing import List, Optional, Tuple - -import cv2 -import numpy as np - -from supervision.annotators.base import ImageType -from supervision.draw.color import Color -from supervision.keypoint.core import KeyPoints -from supervision.keypoint.skeletons import SKELETONS_BY_VERTEX_COUNT -from supervision.utils.conversion import convert_for_annotation_method - - -class BaseKeyPointAnnotator(ABC): - @abstractmethod - def annotate(self, scene: ImageType, detections: KeyPoints) -> ImageType: - pass - - -class VertexAnnotator(BaseKeyPointAnnotator): - def __init__( - self, - color: Color = Color.ROBOFLOW, - radius: int = 4, - ) -> None: - """ - Most basic keypoint annotator. - - Args: - color (Color, optional): The color of the keypoint. - radius (int, optional): The radius of the keypoint. - """ - self.color = color - self.radius = radius - - @convert_for_annotation_method - def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType: - if len(keypoints) == 0: - return scene - - for xy in keypoints.xy: - for x, y in xy: - cv2.circle( - img=scene, - center=(int(x), int(y)), - radius=self.radius, - color=self.color.as_bgr(), - thickness=-1, - ) - - return scene - - -class EdgeAnnotator(BaseKeyPointAnnotator): - def __init__( - self, - color: Color = Color.ROBOFLOW, - thickness: int = 2, - edges: Optional[List[Tuple[int, int]]] = None, - ) -> None: - """ - Draw the lines between points of the image. - - Args: - color (Color, optional): The color of the lines. - thickness (int, optional): The thickness of the lines. - edge (Optional[List[Tuple[int, int]]]): The edges to draw. - If set to `None`, will attempt to select automatically. - """ - self.color = color - self.thickness = thickness - self.edges = edges - - @convert_for_annotation_method - def annotate(self, scene: ImageType, keypoints: KeyPoints) -> ImageType: - if len(keypoints) == 0: - return scene - - for xy in keypoints.xy: - edges = self.edges - if not edges: - edges = SKELETONS_BY_VERTEX_COUNT.get(len(xy)) - if not edges: - warn(f"No skeleton found with {len(xy)} vertices") - return scene - - for class_a, class_b in edges: - xy_a = xy[class_a - 1] - xy_b = xy[class_b - 1] - missing_a = np.allclose(xy_a, 0) - missing_b = np.allclose(xy_b, 0) - if missing_a or missing_b: - continue - - cv2.line( - img=scene, - pt1=(int(xy_a[0]), int(xy_a[1])), - pt2=(int(xy_b[0]), int(xy_b[1])), - color=self.color.as_bgr(), - thickness=self.thickness, - ) - - return scene diff --git a/supervision/keypoint/annotators.py b/supervision/keypoint/annotators.py new file mode 100644 index 00000000..4b43765c --- /dev/null +++ b/supervision/keypoint/annotators.py @@ -0,0 +1,177 @@ +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(color=sv.Color.GREEN, radius=10) + annotated_frame = vertex_annotator.annotate( + scene=image.copy(), + key_points=key_points + ) + ``` + + ![vertex-annotator-example](https://media.roboflow.com/ + supervision-annotator-examples/vertex-annotator-example.png) + """ + 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(color=sv.Color.GREEN, thickness=5) + annotated_frame = edge_annotator.annotate( + scene=image.copy(), + key_points=key_points + ) + ``` + + ![edge-annotator-example](https://media.roboflow.com/ + supervision-annotator-examples/edge-annotator-example.png) + """ + if len(key_points) == 0: + return scene + + for xy in key_points.xy: + edges = self.edges + if not edges: + edges = SKELETONS_BY_VERTEX_COUNT.get(len(xy)) + if not edges: + warn(f"No skeleton found with {len(xy)} vertices") + return scene + + for class_a, class_b in edges: + xy_a = xy[class_a - 1] + xy_b = xy[class_b - 1] + missing_a = np.allclose(xy_a, 0) + missing_b = np.allclose(xy_b, 0) + if missing_a or missing_b: + continue + + cv2.line( + img=scene, + pt1=(int(xy_a[0]), int(xy_a[1])), + pt2=(int(xy_b[0]), int(xy_b[1])), + color=self.color.as_bgr(), + thickness=self.thickness, + ) + + return scene diff --git a/supervision/keypoint/core.py b/supervision/keypoint/core.py index 87f0a20b..7019aa95 100644 --- a/supervision/keypoint/core.py +++ b/supervision/keypoint/core.py @@ -14,28 +14,26 @@ from supervision.validators import validate_keypoints_fields @dataclass class KeyPoints: """ - The `sv.KeyPoints` allows you to convert results from a variety of keypoint - keypoints models into a single, unified format. + The `sv.KeyPoints` class in the Supervision library standardizes results from + various keypoint detection and pose estimation models into a consistent format. This + class simplifies data manipulation and filtering, providing a uniform API for + integration with Supervision annotators. - ```python - import cv2 - import supervision as sv - from ultralytics import YOLO + === "Ultralytics" - image = cv2.imread() - model = YOLO('yolov8s-pose.pt') - result = model(image)[0] - keypoints = sv.KeyPoints.from_ultralytics(result) - ``` + Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics) + method, which accepts model results. - !!! tip + ```python + import cv2 + import supervision as sv + from ultralytics import YOLO - In `sv.KeyPoints`, detection data is categorized into two main field types: - fixed and custom. The fixed fields include `xy`, `confidence`, - `class_id`. For any additional data requirements, custom - fields come into play, stored in the data field. These custom fields are easily - accessible using the `keypoints[]` syntax, providing flexibility - for diverse data handling needs. + image = cv2.imread() + model = YOLO('yolov8s-pose.pt') + result = model(image)[0] + key_points = sv.KeyPoints.from_ultralytics(result) + ``` Attributes: xy (np.ndarray): An array of shape `(n, 2)` containing @@ -47,15 +45,7 @@ class KeyPoints: data (Dict[str, Union[np.ndarray, List]]): A dictionary containing additional data where each key is a string representing the data type, and the value is either a NumPy array or a list of corresponding data. - - !!! warning - - The `data` field in the `sv.KeyPoints` class is currently in an experimental - phase. Please be aware that its API and functionality are subject to change in - future updates as we continue to refine and improve its capabilities. - We encourage users to experiment with this feature and provide feedback, but - also to be prepared for potential modifications in upcoming releases. - """ + """ # noqa: E501 // docs xy: npt.NDArray[np.float32] class_id: Optional[npt.NDArray[np.int_]] = None @@ -263,7 +253,7 @@ class KeyPoints: Create an empty Keypoints object with no keypoints. Returns: - (Keypoints): An empty Keypoints object. + (KeyPoints): An empty Keypoints object. Example: ```python