From ab622d2544d7be3c52fd74be02fb8bd8be6720b7 Mon Sep 17 00:00:00 2001 From: Youho99 Date: Tue, 2 Apr 2024 14:52:32 +0200 Subject: [PATCH 01/11] Fix `detections_to_coco_annotations` function for empty polygons. --- supervision/dataset/formats/coco.py | 69 ++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 4f8679d52..5415d4f7f 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -1,7 +1,7 @@ import os from datetime import datetime from pathlib import Path -from typing import Dict, List, Tuple +from typing import Dict, List, Tuple, Optional import cv2 import numpy as np @@ -97,6 +97,34 @@ def coco_annotations_to_detections( return Detections(xyxy=xyxy, class_id=np.asarray(class_ids, dtype=int)) +def object_to_coco( + xyxy: np.ndarray, + class_id: int, + annotation_id: int, + image_id: int, + polygon: Optional[np.ndarray] = None, +) -> dict: + + coco_annotation = { + "id": annotation_id, + "image_id": image_id, + "category_id": int(class_id), + "iscrowd": 0, + } + + if polygon is None: + box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1] + coco_annotation.update({ + "bbox": [xyxy[0], xyxy[1], box_width, box_height], + "area": box_width * box_height, + }) + else: + polygon = polygon.reshape(-1) + coco_annotation["segmentation"] = [polygon] + + return coco_annotation + + def detections_to_coco_annotations( detections: Detections, image_id: int, @@ -105,31 +133,32 @@ def detections_to_coco_annotations( max_image_area_percentage: float = 1.0, approximation_percentage: float = 0.75, ) -> Tuple[List[Dict], int]: - coco_annotations = [] + annotation = [] for xyxy, mask, _, class_id, _, _ in detections: - box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1] - polygon = [] if mask is not None: - polygon = list( - approximate_mask_with_polygons( + polygons = approximate_mask_with_polygons( mask=mask, min_image_area_percentage=min_image_area_percentage, max_image_area_percentage=max_image_area_percentage, approximation_percentage=approximation_percentage, - )[0].flatten() - ) - coco_annotation = { - "id": annotation_id, - "image_id": image_id, - "category_id": int(class_id), - "bbox": [xyxy[0], xyxy[1], box_width, box_height], - "area": box_width * box_height, - "segmentation": [polygon] if polygon else [], - "iscrowd": 0, - } - coco_annotations.append(coco_annotation) - annotation_id += 1 - return coco_annotations, annotation_id + ) + for polygon in polygons: + if polygon.any(): # polygon not empty + next_object = object_to_coco( + xyxy=xyxy, + class_id=class_id, + annotation_id=annotation_id, + image_id=image_id, + polygon=polygon, + ) + annotation.append(next_object) + annotation_id += 1 + else: + next_object = object_to_coco( + xyxy=xyxy, class_id=class_id, annotation_id=annotation_id, image_id=image_id) + annotation.append(next_object) + annotation_id += 1 + return annotation, annotation_id def load_coco_annotations( From a5867276feded97d6193701ed7b49871ccc180d9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 13:04:45 +0000 Subject: [PATCH 02/11] =?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/dataset/formats/coco.py | 47 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 5415d4f7f..f34483d65 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -1,7 +1,7 @@ import os from datetime import datetime from pathlib import Path -from typing import Dict, List, Tuple, Optional +from typing import Dict, List, Optional, Tuple import cv2 import numpy as np @@ -98,30 +98,31 @@ def coco_annotations_to_detections( def object_to_coco( - xyxy: np.ndarray, - class_id: int, - annotation_id: int, - image_id: int, - polygon: Optional[np.ndarray] = None, + xyxy: np.ndarray, + class_id: int, + annotation_id: int, + image_id: int, + polygon: Optional[np.ndarray] = None, ) -> dict: - coco_annotation = { "id": annotation_id, "image_id": image_id, "category_id": int(class_id), "iscrowd": 0, } - + if polygon is None: box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1] - coco_annotation.update({ - "bbox": [xyxy[0], xyxy[1], box_width, box_height], - "area": box_width * box_height, - }) + coco_annotation.update( + { + "bbox": [xyxy[0], xyxy[1], box_width, box_height], + "area": box_width * box_height, + } + ) else: polygon = polygon.reshape(-1) coco_annotation["segmentation"] = [polygon] - + return coco_annotation @@ -137,17 +138,17 @@ def detections_to_coco_annotations( for xyxy, mask, _, class_id, _, _ in detections: if mask is not None: polygons = approximate_mask_with_polygons( - mask=mask, - min_image_area_percentage=min_image_area_percentage, - max_image_area_percentage=max_image_area_percentage, - approximation_percentage=approximation_percentage, - ) + mask=mask, + min_image_area_percentage=min_image_area_percentage, + max_image_area_percentage=max_image_area_percentage, + approximation_percentage=approximation_percentage, + ) for polygon in polygons: - if polygon.any(): # polygon not empty + if polygon.any(): # polygon not empty next_object = object_to_coco( xyxy=xyxy, class_id=class_id, - annotation_id=annotation_id, + annotation_id=annotation_id, image_id=image_id, polygon=polygon, ) @@ -155,7 +156,11 @@ def detections_to_coco_annotations( annotation_id += 1 else: next_object = object_to_coco( - xyxy=xyxy, class_id=class_id, annotation_id=annotation_id, image_id=image_id) + xyxy=xyxy, + class_id=class_id, + annotation_id=annotation_id, + image_id=image_id, + ) annotation.append(next_object) annotation_id += 1 return annotation, annotation_id From ee757bbb75279831b7c3c7db895b80064c2958f9 Mon Sep 17 00:00:00 2001 From: Youho99 Date: Wed, 3 Apr 2024 10:10:43 +0200 Subject: [PATCH 03/11] Add `segmentation` empty field for bboxes coco format --- supervision/dataset/formats/coco.py | 1 + 1 file changed, 1 insertion(+) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index f34483d65..8c54d5d4c 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -117,6 +117,7 @@ def object_to_coco( { "bbox": [xyxy[0], xyxy[1], box_width, box_height], "area": box_width * box_height, + "segmentation": [], } ) else: From 9ff106e2377b51013b20c85c49c09b471c4d1592 Mon Sep 17 00:00:00 2001 From: Youho99 Date: Mon, 15 Apr 2024 14:44:05 +0200 Subject: [PATCH 04/11] update coco.py Always include box, area, and segmentation in the result COCO JSON. --- supervision/dataset/formats/coco.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 8c54d5d4c..c108361f9 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -104,26 +104,18 @@ def object_to_coco( image_id: int, polygon: Optional[np.ndarray] = None, ) -> dict: + box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1] + coco_annotation = { "id": annotation_id, "image_id": image_id, "category_id": int(class_id), "iscrowd": 0, + "bbox": [xyxy[0], xyxy[1], box_width, box_height], + "area": box_width * box_height, + "segmentation": [] if polygon is None else [polygon.reshape(-1)] } - if polygon is None: - box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1] - coco_annotation.update( - { - "bbox": [xyxy[0], xyxy[1], box_width, box_height], - "area": box_width * box_height, - "segmentation": [], - } - ) - else: - polygon = polygon.reshape(-1) - coco_annotation["segmentation"] = [polygon] - return coco_annotation From 92b99df924ae068a16e76905938f30c05bf4542e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:44:19 +0000 Subject: [PATCH 05/11] =?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/dataset/formats/coco.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index c108361f9..aad845e9c 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -113,7 +113,7 @@ def object_to_coco( "iscrowd": 0, "bbox": [xyxy[0], xyxy[1], box_width, box_height], "area": box_width * box_height, - "segmentation": [] if polygon is None else [polygon.reshape(-1)] + "segmentation": [] if polygon is None else [polygon.reshape(-1)], } return coco_annotation From f29e42322724e1396d59a344cbaaa3ef0745143c Mon Sep 17 00:00:00 2001 From: Youho99 Date: Tue, 16 Apr 2024 15:39:17 +0200 Subject: [PATCH 06/11] `as_coco()` : Add COCO format disjoint masks support --- supervision/dataset/formats/coco.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index aad845e9c..17ca7b78a 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -113,7 +113,7 @@ def object_to_coco( "iscrowd": 0, "bbox": [xyxy[0], xyxy[1], box_width, box_height], "area": box_width * box_height, - "segmentation": [] if polygon is None else [polygon.reshape(-1)], + "segmentation": [] if polygon is None else polygon, } return coco_annotation @@ -136,17 +136,17 @@ def detections_to_coco_annotations( max_image_area_percentage=max_image_area_percentage, approximation_percentage=approximation_percentage, ) - for polygon in polygons: - if polygon.any(): # polygon not empty - next_object = object_to_coco( - xyxy=xyxy, - class_id=class_id, - annotation_id=annotation_id, - image_id=image_id, - polygon=polygon, - ) - annotation.append(next_object) - annotation_id += 1 + reshaped_polygons = [polygon.reshape(-1) for polygon in polygons if polygon.any()] + if reshaped_polygons: + next_object = object_to_coco( + xyxy=xyxy, + class_id=class_id, + annotation_id=annotation_id, + image_id=image_id, + polygon=reshaped_polygons, + ) + annotation.append(next_object) + annotation_id += 1 else: next_object = object_to_coco( xyxy=xyxy, From 92aaa522d5b3e3507246539324f33c0615364aad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:40:20 +0000 Subject: [PATCH 07/11] =?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/dataset/formats/coco.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 17ca7b78a..23443d582 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -136,7 +136,9 @@ def detections_to_coco_annotations( max_image_area_percentage=max_image_area_percentage, approximation_percentage=approximation_percentage, ) - reshaped_polygons = [polygon.reshape(-1) for polygon in polygons if polygon.any()] + reshaped_polygons = [ + polygon.reshape(-1) for polygon in polygons if polygon.any() + ] if reshaped_polygons: next_object = object_to_coco( xyxy=xyxy, From 6850dfd1624033d9e84cec3a1e8c21542bee4b70 Mon Sep 17 00:00:00 2001 From: Youho99 Date: Thu, 18 Apr 2024 11:20:59 +0200 Subject: [PATCH 08/11] Fix `force_masks` parameter on `from_coco()` function Allows reconstruction of disjointed masks --- supervision/dataset/formats/coco.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 23443d582..4023bac1a 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -85,11 +85,14 @@ def coco_annotations_to_detections( if with_masks: polygons = [ np.reshape( - np.asarray(image_annotation["segmentation"], dtype=np.int32), (-1, 2) - ) + np.asarray(segmentation, dtype=np.int32), (-1, 2) + ) for image_annotation in image_annotations + for segmentation in image_annotation["segmentation"] ] - mask = _polygons_to_masks(polygons=polygons, resolution_wh=resolution_wh) + separate_masks = _polygons_to_masks(polygons=polygons, resolution_wh=resolution_wh) + mask = np.sum(separate_masks, axis=0, keepdims=True) # merge mask parts representing a disjoint mask + return Detections( class_id=np.asarray(class_ids, dtype=int), xyxy=xyxy, mask=mask ) From aa5d292173b0a84b71e2d9194e8d29ce6326e42c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:21:14 +0000 Subject: [PATCH 09/11] =?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/dataset/formats/coco.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 4023bac1a..39a28f7cb 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -84,14 +84,16 @@ def coco_annotations_to_detections( if with_masks: polygons = [ - np.reshape( - np.asarray(segmentation, dtype=np.int32), (-1, 2) - ) + np.reshape(np.asarray(segmentation, dtype=np.int32), (-1, 2)) for image_annotation in image_annotations for segmentation in image_annotation["segmentation"] ] - separate_masks = _polygons_to_masks(polygons=polygons, resolution_wh=resolution_wh) - mask = np.sum(separate_masks, axis=0, keepdims=True) # merge mask parts representing a disjoint mask + separate_masks = _polygons_to_masks( + polygons=polygons, resolution_wh=resolution_wh + ) + mask = np.sum( + separate_masks, axis=0, keepdims=True + ) # merge mask parts representing a disjoint mask return Detections( class_id=np.asarray(class_ids, dtype=int), xyxy=xyxy, mask=mask From 21b0ab5236d72bf10d0cffab6f6d7d983f645f36 Mon Sep 17 00:00:00 2001 From: Youho99 Date: Thu, 18 Apr 2024 11:45:38 +0200 Subject: [PATCH 10/11] Ensures the mask is binary --- supervision/dataset/formats/coco.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 39a28f7cb..29c057d51 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -91,9 +91,12 @@ def coco_annotations_to_detections( separate_masks = _polygons_to_masks( polygons=polygons, resolution_wh=resolution_wh ) - mask = np.sum( - separate_masks, axis=0, keepdims=True - ) # merge mask parts representing a disjoint mask + mask = ( + np.sum( + separate_masks, axis=0, keepdims=True + ) # merge mask parts representing a disjoint mask + > 0 # ensure that the result is a binary mask + ) return Detections( class_id=np.asarray(class_ids, dtype=int), xyxy=xyxy, mask=mask From 6d7ce7bdd4057b4c6914af9c3f63d29a519cd05e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:45:55 +0000 Subject: [PATCH 11/11] =?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/dataset/formats/coco.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/dataset/formats/coco.py b/supervision/dataset/formats/coco.py index 29c057d51..4abac2f05 100644 --- a/supervision/dataset/formats/coco.py +++ b/supervision/dataset/formats/coco.py @@ -95,7 +95,7 @@ def coco_annotations_to_detections( np.sum( separate_masks, axis=0, keepdims=True ) # merge mask parts representing a disjoint mask - > 0 # ensure that the result is a binary mask + > 0 # ensure that the result is a binary mask ) return Detections(