From 186bfc44facca73b538e7cee10b9e29a149e28bf Mon Sep 17 00:00:00 2001 From: hd Date: Mon, 10 Jul 2023 18:03:56 +0200 Subject: [PATCH 01/37] paddledet initial support added --- supervision/detection/core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index b423aaad..cee2ef5f 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -354,6 +354,14 @@ class Detections: return Detections(xyxy=xywh_to_xyxy(boxes_xywh=xywh), mask=mask) + @classmethod + def from_paddledet(cls, paddledet_result): + return cls( + xyxy=paddledet_result[:, 2:6], + confidence=paddledet_result[:, 1], + class_id=paddledet_result[:, 0].astype(int), + ) + @classmethod def empty(cls) -> Detections: """ From 12ce5feab73208176f6c61ab9c7dc0705a8e6021 Mon Sep 17 00:00:00 2001 From: hd Date: Mon, 10 Jul 2023 18:13:50 +0200 Subject: [PATCH 02/37] paddledet refactored --- supervision/detection/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index cee2ef5f..2f5e7d3c 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -357,9 +357,9 @@ class Detections: @classmethod def from_paddledet(cls, paddledet_result): return cls( - xyxy=paddledet_result[:, 2:6], - confidence=paddledet_result[:, 1], - class_id=paddledet_result[:, 0].astype(int), + xyxy=paddledet_result["bbox"][:, 2:6], + confidence=paddledet_result["bbox"][:, 1], + class_id=paddledet_result["bbox"][:, 0].astype(int), ) @classmethod From 985608146cd998adac94ac1a775a84daf8eae032 Mon Sep 17 00:00:00 2001 From: hd Date: Fri, 21 Jul 2023 12:13:46 +0200 Subject: [PATCH 03/37] Docstrings updated --- supervision/detection/core.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 2f5e7d3c..4e3cb520 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -356,6 +356,34 @@ class Detections: @classmethod def from_paddledet(cls, paddledet_result): + """ + Creates a Detections instance from [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) inference result. + + Args: + paddledet_result (List[dict]): The output Results instance from SAM + + Returns: + Detections: A new Detections object. + + Example: + ```python + >>> import supervision as sv + >>> import paddle + >>> from ppdet.engine import Trainer + >>> from ppdet.core.workspace import load_config + + >>> weights = (...) + >>> config = (...) + + >>> cfg = load_config(config) + >>> trainer = Trainer(cfg, mode='test') + >>> trainer.load_weights(weights) + + >>> paddledet_result = trainer.predict([images])[0] + + >>> detections = sv.Detections.from_paddledet(paddledet_result=paddledet_result) + ``` + """ return cls( xyxy=paddledet_result["bbox"][:, 2:6], confidence=paddledet_result["bbox"][:, 1], From e882c398b78322afafafb7b7e743ab90c0734a62 Mon Sep 17 00:00:00 2001 From: hd Date: Sat, 22 Jul 2023 16:10:31 +0200 Subject: [PATCH 04/37] Added method for complete ultralytics model usage --- supervision/detection/core.py | 41 ++++++++++++++++++++++++++++++++-- supervision/detection/utils.py | 7 ++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 9aab3630..e8cb72c9 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -7,11 +7,12 @@ import cv2 import numpy as np from supervision.detection.utils import ( - extract_yolov8_masks, + extract_ultralytics_masks, non_max_suppression, process_roboflow_result, xywh_to_xyxy, ) +from supervision.utils.internal import deprecated from supervision.geometry.core import Position @@ -170,6 +171,7 @@ class Detections: ) @classmethod + @deprecated("Please use sv.Detections.from_ultralytics() API for future usage. This method is deprecated and removed in future release") def from_yolov8(cls, yolov8_results) -> Detections: """ Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result. @@ -196,7 +198,42 @@ class Detections: xyxy=yolov8_results.boxes.xyxy.cpu().numpy(), confidence=yolov8_results.boxes.conf.cpu().numpy(), class_id=yolov8_results.boxes.cls.cpu().numpy().astype(int), - mask=extract_yolov8_masks(yolov8_results), + mask=extract_ultralytics_masks(yolov8_results), + ) + + @classmethod + def from_ultralytics(cls, ultralytics_results) -> Detections: + """ + Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result. + + Args: + yolov8_results (ultralytics.yolo.engine.results.Results): The output Results instance from YOLOv8 + + Returns: + Detections: A new Detections object. + + Example: + ```python + >>> import cv2 + >>> from ultralytics import YOLO, FastSAM, SAM, RTDETR + >>> import supervision as sv + + >>> image = cv2.imread(SOURCE_IMAGE_PATH) + >>> model = YOLO('yolov8s.pt') + >>> model = SAM('sam_b.pt') + >>> model = SAM('mobile_sam.pt') + >>> model = FastSAM('FastSAM-s.pt') + >>> model = RTDETR('FastSAM-s.pt') + + >>> result = model(image)[0] + >>> detections = sv.Detections.from_ultralytics(result) + ``` + """ + return cls( + xyxy=ultralytics_results.boxes.xyxy.cpu().numpy(), + confidence=ultralytics_results.boxes.conf.cpu().numpy(), + class_id=ultralytics_results.boxes.cls.cpu().numpy().astype(int), + mask=extract_ultralytics_masks(ultralytics_results), ) @classmethod diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index 63206ddc..49d59188 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -260,7 +260,7 @@ def approximate_polygon( return np.squeeze(approximated_points, axis=1) -def extract_yolov8_masks(yolov8_results) -> Optional[np.ndarray]: +def extract_ultralytics_masks(yolov8_results) -> Optional[np.ndarray]: if not yolov8_results.masks: return None @@ -288,7 +288,10 @@ def extract_yolov8_masks(yolov8_results) -> Optional[np.ndarray]: for i in range(masks.shape[0]): mask = masks[i] mask = mask[top:bottom, left:right] - mask = cv2.resize(mask, (orig_shape[1], orig_shape[0])) + + if mask.shape != orig_shape: + mask = cv2.resize(mask, (orig_shape[1], orig_shape[0])) + mask_maps.append(mask) return np.asarray(mask_maps, dtype=bool) From 3c596181522e51d5050fd39181938193c77373b1 Mon Sep 17 00:00:00 2001 From: hd Date: Sat, 22 Jul 2023 16:12:31 +0200 Subject: [PATCH 05/37] ready for review --- supervision/dataset/core.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/supervision/dataset/core.py b/supervision/dataset/core.py index a081d2fb..7167171a 100644 --- a/supervision/dataset/core.py +++ b/supervision/dataset/core.py @@ -511,18 +511,6 @@ class DetectionDataset(BaseDataset): classes=merged_classes, images=merged_images, annotations=merged_annotations ) - def add_class_names(self, class_names: List[str]): - self.classes = class_names - - def add_instance(self, filename: str, image: np.ndarray, detections: Detections): - if filename not in self.annotations.keys(): - self.images[filename] = image - self.annotations[filename] = detections - - @classmethod - def emty(cls): - return cls(classes=[], images={}, annotations={}) - @dataclass class ClassificationDataset(BaseDataset): From 5edd58d6de401a16fd36c371589ce18ed63db7f3 Mon Sep 17 00:00:00 2001 From: hd Date: Sat, 22 Jul 2023 20:45:41 +0200 Subject: [PATCH 06/37] ready for review --- supervision/detection/core.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index e8cb72c9..033a41e5 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -12,8 +12,8 @@ from supervision.detection.utils import ( process_roboflow_result, xywh_to_xyxy, ) -from supervision.utils.internal import deprecated from supervision.geometry.core import Position +from supervision.utils.internal import deprecated def _validate_xyxy(xyxy: Any, n: int) -> None: @@ -171,7 +171,9 @@ class Detections: ) @classmethod - @deprecated("Please use sv.Detections.from_ultralytics() API for future usage. This method is deprecated and removed in future release") + @deprecated( + "Please use sv.Detections.from_ultralytics() API for future usage. This method is deprecated and removed in future release" + ) def from_yolov8(cls, yolov8_results) -> Detections: """ Creates a Detections instance from a [YOLOv8](https://github.com/ultralytics/ultralytics) inference result. @@ -223,7 +225,7 @@ class Detections: >>> model = SAM('sam_b.pt') >>> model = SAM('mobile_sam.pt') >>> model = FastSAM('FastSAM-s.pt') - >>> model = RTDETR('FastSAM-s.pt') + >>> model = RTDETR('rtdetr-l.pt') >>> result = model(image)[0] >>> detections = sv.Detections.from_ultralytics(result) From b2b75f1105210d68e31e04dd682cb6d1bdf39e28 Mon Sep 17 00:00:00 2001 From: Hardik Dava <39372750+hardikdava@users.noreply.github.com> Date: Sun, 23 Jul 2023 22:56:20 +0200 Subject: [PATCH 07/37] Update CONTRIBUTING.md --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43acf5c8..7992316f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,7 @@ When creating new functions, please ensure you have the following: 2. Unit tests for the function. 3. Examples in the documentation for the function. 4. Created an entry in our docs to autogenerate the documentation for the function. +5. Please share google colab with minimal code to test new feature or reproduce PR whenever it is possible. Please ensure that google colab can be accessed without any issue. All pull requests will be reviewed by the maintainers of the project. We will provide feedback and ask for changes if necessary. @@ -48,4 +49,4 @@ So far, **there is no types checking with mypy**. See [issue](https://github.com ## 🧪 tests -[`pytests`](https://docs.pytest.org/en/7.1.x/) is used to run our tests. \ No newline at end of file +[`pytests`](https://docs.pytest.org/en/7.1.x/) is used to run our tests. From c7c8e67ffdfeb0a1e59ce1cd0b393469a8663272 Mon Sep 17 00:00:00 2001 From: Hardik Dava <39372750+hardikdava@users.noreply.github.com> Date: Sun, 23 Jul 2023 22:56:57 +0200 Subject: [PATCH 08/37] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7992316f..e04d60d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ Before you contribute a new feature, consider submitting an Issue to discuss the ## How to Contribute Changes -First, fork this repository to your own GitHub account. Create a new branch that describes your changes (i.e. `line-counter-docs`). Push your changes to the branch on your fork and then submit a pull request to this repository. +First, fork this repository to your own GitHub account. Create a new branch that describes your changes (i.e. `line-counter-docs`). Push your changes to the branch on your fork and then submit a pull request to `develop` branch of this repository. When creating new functions, please ensure you have the following: From 7067741310e4ce582436ebd76d415c2d357cec3f Mon Sep 17 00:00:00 2001 From: Piotr Skalski Date: Mon, 24 Jul 2023 23:31:55 +0200 Subject: [PATCH 09/37] =?UTF-8?q?=C2=A9=20bring=20back=20MIT=20as=20licens?= =?UTF-8?q?e=20in=20`pyproject.toml`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d091dd7f..08626523 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ keywords = ["machine-learning", "deep-learning", "vision", "ML", "DL", "AI", "YO classifiers=[ 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', + 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', From 771a47e15d944566b1ddcf877138479c8ea7097b Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Tue, 25 Jul 2023 08:30:37 +0300 Subject: [PATCH 10/37] =?UTF-8?q?docs:=20=F0=9F=93=9D=20MIT=20license=20cl?= =?UTF-8?q?assifier=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d091dd7f..9e14bedb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "A set of easy-to-use utils that will come in handy in any Compute authors = ["Piotr Skalski "] maintainers = ["Piotr Skalski "] readme = "README.md" +license = "MIT" packages = [{include = "supervision"}] homepage = "https://github.com/roboflow/supervision" repository = "https://github.com/roboflow/supervision" @@ -14,7 +15,7 @@ keywords = ["machine-learning", "deep-learning", "vision", "ML", "DL", "AI", "YO classifiers=[ 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', + 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', From d7e08d42eb0ebf0acf47bbdbb07a7342e43140b7 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Wed, 26 Jul 2023 10:20:47 +0200 Subject: [PATCH 11/37] add load_pascal_voc_annotations v2 --- supervision/dataset/formats/pascal_voc.py | 93 ++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index a3005eb9..bca886d4 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -1,12 +1,16 @@ -from typing import List, Optional, Tuple +import os +from pathlib import Path +from typing import Dict, List, Optional, Tuple from xml.dom.minidom import parseString from xml.etree.ElementTree import Element, SubElement, parse, tostring +import cv2 import numpy as np from supervision.dataset.utils import approximate_mask_with_polygons from supervision.detection.core import Detections from supervision.detection.utils import polygon_to_xyxy +from supervision.utils.file import list_files_with_extensions def object_to_pascal_voc( @@ -120,6 +124,93 @@ def detections_to_pascal_voc( def load_pascal_voc_annotations( + images_directory_path: str, + annotations_directory_path: str, + force_masks: bool = False, +) -> Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]: + """ + Loads PASCAL VOC XML annotations and returns the image name, a Detections instance, and a list of class names. + + Args: + annotation_path (str): The path to the PASCAL VOC XML annotations file. + + Returns: + Tuple[str, Detections, List[str]]: A tuple containing the image name, a Detections instance, and a list of class names of objects in the detections. + """ + + image_paths = list_files_with_extensions( + directory=images_directory_path, extensions=["jpg", "jpeg", "png"] + ) + + classes = [] + images = {} + annotations = {} + + for image_path in image_paths: + image_name = Path(image_path).stem + image = cv2.imread(str(image_path)) + + annotation_path = os.path.join(annotations_directory_path, f"{image_name}.xml") + if not os.path.exists(annotation_path): + images[image_path.name] = image + annotations[image_path.name] = Detections.empty() + continue + + tree = parse(annotation_path) + root = tree.getroot() + + xyxy = [] + class_names = [] + masks = [] + for obj in root.findall("object"): + class_name = obj.find("name").text + class_names.append(class_name) + + bbox = obj.find("bndbox") + x1 = int(bbox.find("xmin").text) + y1 = int(bbox.find("ymin").text) + x2 = int(bbox.find("xmax").text) + y2 = int(bbox.find("ymax").text) + + xyxy.append([x1, y1, x2, y2]) + + with_masks = obj.find("polygon") is not None + with_masks = force_masks if force_masks else with_masks + + for polygon in obj.findall("polygon"): + polygon_points = [] + coords = polygon.findall(".//*") + for i in range(0, len(coords), 2): + x = int(coords[i].text) + y = int(coords[i + 1].text) + polygon_points.append([x, y]) + + mask_from_polygon = polygon_to_mask( + polygon=np.array(polygon_points), + resolution_wh=(image.shape[0], image.shape[1]), + ) + masks.append(mask_from_polygon) + + xyxy = np.array(xyxy) + masks = np.array(masks) + annotation = Detections(xyxy=xyxy, mask=masks, class_id=np.array(class_names)) + + images[image_path.name] = image + annotations[image_path.name] = annotation + classes += class_names + + classes = list(set(classes)) + + return classes, images, annotations + + +def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray: + mask = np.zeros(resolution_wh, dtype=np.uint8) + cv2.fillPoly(mask, pts=[polygon], color=1) + return mask + + +def load_pascal_voc_annotations_v1( annotation_path: str, ) -> Tuple[str, Detections, List[str]]: """ From 3723eae50c385fc2cbbf4e3cd97b68d5056e28ef Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Wed, 26 Jul 2023 10:21:17 +0200 Subject: [PATCH 12/37] update from_pascal_voc to match v2 loader --- supervision/dataset/core.py | 38 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/supervision/dataset/core.py b/supervision/dataset/core.py index ce7e0edb..4bd80c1b 100644 --- a/supervision/dataset/core.py +++ b/supervision/dataset/core.py @@ -197,7 +197,10 @@ class DetectionDataset(BaseDataset): @classmethod def from_pascal_voc( - cls, images_directory_path: str, annotations_directory_path: str + cls, + images_directory_path: str, + annotations_directory_path: str, + force_masks: bool = False, ) -> DetectionDataset: """ Creates a Dataset instance from PASCAL VOC formatted data. @@ -231,34 +234,17 @@ class DetectionDataset(BaseDataset): ['dog', 'person'] ``` """ - image_paths = list_files_with_extensions( - directory=images_directory_path, extensions=["jpg", "jpeg", "png"] - ) - annotation_paths = list_files_with_extensions( - directory=annotations_directory_path, extensions=["xml"] + + classes, images, annotations = load_pascal_voc_annotations( + images_directory_path=images_directory_path, + annotations_directory_path=annotations_directory_path, + force_masks=force_masks, ) - raw_annotations: List[Tuple[str, Detections, List[str]]] = [ - load_pascal_voc_annotations(annotation_path=str(annotation_path)) - for annotation_path in annotation_paths - ] + for annotation in annotations.values(): + class_id = [classes.index(class_name) for class_name in annotation.class_id] + annotation.class_id = np.array(class_id) - classes = [] - for annotation in raw_annotations: - classes.extend(annotation[2]) - classes = list(set(classes)) - - for annotation in raw_annotations: - class_id = [classes.index(class_name) for class_name in annotation[2]] - annotation[1].class_id = np.array(class_id) - - images = { - image_path.name: cv2.imread(str(image_path)) for image_path in image_paths - } - - annotations = { - image_name: detections for image_name, detections, _ in raw_annotations - } return DetectionDataset(classes=classes, images=images, annotations=annotations) @classmethod From 86b33ed55801d33d1c14dd89d1a95f6784228d83 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Wed, 26 Jul 2023 10:39:45 +0200 Subject: [PATCH 13/37] add test_pascal template --- test/dataset/formats/test_pascal.py | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/dataset/formats/test_pascal.py diff --git a/test/dataset/formats/test_pascal.py b/test/dataset/formats/test_pascal.py new file mode 100644 index 00000000..30bbc05e --- /dev/null +++ b/test/dataset/formats/test_pascal.py @@ -0,0 +1,32 @@ +from contextlib import ExitStack as DoesNotRaise +from typing import List, Optional, Tuple + +import numpy as np +import pytest + +from supervision.dataset.formats.pascal_voc import ( + detections_to_pascal_voc, + load_pascal_voc_annotations, + object_to_pascal_voc, +) +from supervision.detection.core import Detections + +# TODO + + +def test_detections_to_pascal_voc( + expected_result, exception: Exception +): + ... + + +def test_load_pascal_voc_annotations( + expected_result, exception: Exception +): + ... + + +def test_object_to_pascal_voc( + expected_result, exception: Exception +): + ... From a91118322b84ebf9c01b1b94a410490c18983e35 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Wed, 26 Jul 2023 10:45:46 +0200 Subject: [PATCH 14/37] update docstrings --- supervision/dataset/core.py | 2 +- supervision/dataset/formats/pascal_voc.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/supervision/dataset/core.py b/supervision/dataset/core.py index 4bd80c1b..d40728e4 100644 --- a/supervision/dataset/core.py +++ b/supervision/dataset/core.py @@ -225,7 +225,7 @@ class DetectionDataset(BaseDataset): >>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID) >>> dataset = project.version(PROJECT_VERSION).download("voc") - >>> ds = sv.DetectionDataset.from_yolo( + >>> ds = sv.DetectionDataset.from_pascal_voc( ... images_directory_path=f"{dataset.location}/train/images", ... annotations_directory_path=f"{dataset.location}/train/labels" ... ) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index bca886d4..90501be3 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -129,13 +129,15 @@ def load_pascal_voc_annotations( force_masks: bool = False, ) -> Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]: """ - Loads PASCAL VOC XML annotations and returns the image name, a Detections instance, and a list of class names. + Loads PASCAL VOC annotations and returns class names, images, and their corresponding detections. Args: - annotation_path (str): The path to the PASCAL VOC XML annotations file. + images_directory_path (str): The path to the directory containing the images. + annotations_directory_path (str): The path to the directory containing the PASCAL VOC annotation files. + force_masks (bool, optional): If True, forces masks to be loaded for all annotations, regardless of whether they are present. Returns: - Tuple[str, Detections, List[str]]: A tuple containing the image name, a Detections instance, and a list of class names of objects in the detections. + Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]: A tuple containing a list of class names, a dictionary with image names as keys and images as values, and a dictionary with image names as keys and corresponding Detections instances as values. """ image_paths = list_files_with_extensions( From 6dde70eb0c7ffcfa97191b9276bbbc77c82471ce Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Wed, 26 Jul 2023 10:46:31 +0200 Subject: [PATCH 15/37] move fixing of class_ids to load_pascal_voc_annotations --- supervision/dataset/core.py | 4 ---- supervision/dataset/formats/pascal_voc.py | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/supervision/dataset/core.py b/supervision/dataset/core.py index d40728e4..15d92aa0 100644 --- a/supervision/dataset/core.py +++ b/supervision/dataset/core.py @@ -241,10 +241,6 @@ class DetectionDataset(BaseDataset): force_masks=force_masks, ) - for annotation in annotations.values(): - class_id = [classes.index(class_name) for class_name in annotation.class_id] - annotation.class_id = np.array(class_id) - return DetectionDataset(classes=classes, images=images, annotations=annotations) @classmethod diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 90501be3..8f3fb090 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -203,6 +203,10 @@ def load_pascal_voc_annotations( classes = list(set(classes)) + for annotation in annotations.values(): + class_id = [classes.index(class_name) for class_name in annotation.class_id] + annotation.class_id = np.array(class_id) + return classes, images, annotations From 277a9cb6b9d3d073b1232e167b3edfddebeee696 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Wed, 26 Jul 2023 12:36:49 +0200 Subject: [PATCH 16/37] import polygon_to_mask from supervision --- supervision/dataset/formats/pascal_voc.py | 8 +------- .../formats/{test_pascal.py => test_pascal_voc.py} | 0 2 files changed, 1 insertion(+), 7 deletions(-) rename test/dataset/formats/{test_pascal.py => test_pascal_voc.py} (100%) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 8f3fb090..8cc3cef2 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -9,7 +9,7 @@ import numpy as np from supervision.dataset.utils import approximate_mask_with_polygons from supervision.detection.core import Detections -from supervision.detection.utils import polygon_to_xyxy +from supervision.detection.utils import polygon_to_mask, polygon_to_xyxy from supervision.utils.file import list_files_with_extensions @@ -210,12 +210,6 @@ def load_pascal_voc_annotations( return classes, images, annotations -def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray: - mask = np.zeros(resolution_wh, dtype=np.uint8) - cv2.fillPoly(mask, pts=[polygon], color=1) - return mask - - def load_pascal_voc_annotations_v1( annotation_path: str, ) -> Tuple[str, Detections, List[str]]: diff --git a/test/dataset/formats/test_pascal.py b/test/dataset/formats/test_pascal_voc.py similarity index 100% rename from test/dataset/formats/test_pascal.py rename to test/dataset/formats/test_pascal_voc.py From b219961f6e817df3681a59341a2235eed2ad36db Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Fri, 28 Jul 2023 13:45:29 +0200 Subject: [PATCH 17/37] =?UTF-8?q?=F0=9F=93=A6=20update=20`Pillow`=20versio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 117 ++++++++++++++++++++++++++++++------------------- pyproject.toml | 2 +- 2 files changed, 74 insertions(+), 45 deletions(-) diff --git a/poetry.lock b/poetry.lock index fa38d3ab..ae9d1338 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2156,54 +2156,83 @@ files = [ [[package]] name = "pillow" -version = "8.4.0" +version = "9.5.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, - {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, - {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"}, - {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"}, - {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"}, - {file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"}, - {file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"}, - {file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"}, - {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"}, - {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"}, - {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"}, - {file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"}, - {file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"}, - {file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"}, - {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"}, - {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"}, - {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"}, - {file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"}, - {file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"}, - {file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"}, - {file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"}, - {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"}, - {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"}, - {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"}, - {file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"}, - {file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"}, - {file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"}, - {file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"}, - {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"}, - {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"}, - {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"}, - {file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"}, - {file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"}, - {file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"}, - {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"}, - {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"}, - {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"}, + {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, + {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, + {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, + {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, + {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, + {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, + {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, + {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, + {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, + {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, + {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, + {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, + {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, + {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, + {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, + {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, + {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, ] +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + [[package]] name = "pkginfo" version = "1.9.6" @@ -3443,4 +3472,4 @@ desktop = ["opencv-python"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12.0" -content-hash = "be27f05c8857580f327c9d6b89216524c9b8cacf662a24b1d5d4147e4a194f81" +content-hash = "4917c08576fa8226c0593bac637f8442171d16cb50912a0cfe225959dcaa4e5e" diff --git a/pyproject.toml b/pyproject.toml index 9e14bedb..06a76f3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ python = ">=3.8,<3.12.0" numpy = "^1.20.0" matplotlib = "^3.7.1" pyyaml = "^6.0" -pillow = "^8.4.0" +pillow = "^9.4.0" opencv-python = { version = "^4.8.0.74", optional = true } opencv-python-headless = "^4.8.0.74" From b0e0d074af503ad8c7d35276ebf99b8ce5fe24d1 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Fri, 28 Jul 2023 22:21:47 +0200 Subject: [PATCH 18/37] fixes patch --- supervision/dataset/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/dataset/core.py b/supervision/dataset/core.py index 15d92aa0..6e47d747 100644 --- a/supervision/dataset/core.py +++ b/supervision/dataset/core.py @@ -31,7 +31,6 @@ from supervision.dataset.utils import ( train_test_split, ) from supervision.detection.core import Detections -from supervision.utils.file import list_files_with_extensions @dataclass @@ -208,6 +207,7 @@ class DetectionDataset(BaseDataset): Args: images_directory_path (str): The path to the directory containing the images. annotations_directory_path (str): The path to the directory containing the PASCAL VOC XML annotations. + force_masks (bool, optional): If True, forces masks to be loaded for all annotations, regardless of whether they are present. Returns: DetectionDataset: A DetectionDataset instance containing the loaded images and annotations. From 0a1212f23d532aecf01a99a44e8dec079a25d473 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Fri, 28 Jul 2023 22:22:03 +0200 Subject: [PATCH 19/37] fix with_masks defined but not used --- supervision/dataset/formats/pascal_voc.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 8cc3cef2..71a929a6 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -164,6 +164,7 @@ def load_pascal_voc_annotations( xyxy = [] class_names = [] masks = [] + with_masks = False for obj in root.findall("object"): class_name = obj.find("name").text class_names.append(class_name) @@ -194,8 +195,14 @@ def load_pascal_voc_annotations( masks.append(mask_from_polygon) xyxy = np.array(xyxy) - masks = np.array(masks) - annotation = Detections(xyxy=xyxy, mask=masks, class_id=np.array(class_names)) + + if with_masks: + masks = np.array(masks) + annotation = Detections( + xyxy=xyxy, mask=masks, class_id=np.array(class_names) + ) + else: + annotation = Detections(xyxy=xyxy, class_id=np.array(class_names)) images[image_path.name] = image annotations[image_path.name] = annotation From a181826527d7929604c7f3348f60be0e99a9fd9d Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Fri, 28 Jul 2023 23:13:11 +0200 Subject: [PATCH 20/37] add test_object_to_pascal_voc --- test/dataset/formats/test_pascal_voc.py | 73 ++++++++++++++++++++----- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index 30bbc05e..f5a953a2 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -1,4 +1,6 @@ +import xml.etree.ElementTree as ET from contextlib import ExitStack as DoesNotRaise +from test.utils import mock_detections from typing import List, Optional, Tuple import numpy as np @@ -11,22 +13,65 @@ from supervision.dataset.formats.pascal_voc import ( ) from supervision.detection.core import Detections -# TODO - - -def test_detections_to_pascal_voc( - expected_result, exception: Exception -): - ... - - -def test_load_pascal_voc_annotations( - expected_result, exception: Exception -): - ... + +def are_xml_elements_equal(elem1, elem2): + if ( + elem1.tag != elem2.tag + or elem1.attrib != elem2.attrib + or elem1.text != elem2.text + or len(elem1) != len(elem2) + ): + return False + + for child1, child2 in zip(elem1, elem2): + if not are_xml_elements_equal(child1, child2): + return False + + return True +@pytest.mark.parametrize( + "xyxy, name, polygon, expected_result, exception", + [ + ( + [0, 0, 10, 10], + "test", + None, + ET.fromstring( + """test001010""" + ), + DoesNotRaise(), + ), + ( + [0, 0, 10, 10], + "test", + [[0, 0], [10, 0], [10, 10], [0, 10]], + ET.fromstring( + """test001010001001010010""" + ), + DoesNotRaise(), + ), + ], +) def test_object_to_pascal_voc( - expected_result, exception: Exception + xyxy: np.ndarray, + name: str, + polygon: Optional[np.ndarray], + expected_result, + exception: Exception, ): + with exception: + result = object_to_pascal_voc(xyxy=xyxy, name=name, polygon=polygon) + with open("/tmp/test.xml", "w") as f: + f.write(ET.tostring(result).decode()) + with open("/tmp/exptest.xml", "w") as f: + f.write(ET.tostring(expected_result).decode()) + assert are_xml_elements_equal(result, expected_result) + + +def test_load_pascal_voc_annotations(expected_result, exception: Exception): + ... + + +def test_detections_to_pascal_voc(expected_result, exception: Exception): ... From 446ae23d99a7eeadd9497806f70e62bd9d037d9b Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 00:38:08 +0200 Subject: [PATCH 21/37] fix registering of empty detection --- supervision/dataset/formats/pascal_voc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 71a929a6..9dc15bbb 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -194,7 +194,7 @@ def load_pascal_voc_annotations( ) masks.append(mask_from_polygon) - xyxy = np.array(xyxy) + xyxy = np.array(xyxy) if len(xyxy) > 0 else np.empty((0, 4)) if with_masks: masks = np.array(masks) From 9a9d0c5259cda8961e0346ccbdb10ce7a54880e7 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 00:39:11 +0200 Subject: [PATCH 22/37] refactor class_id assignment to Detections in VOC --- supervision/dataset/formats/pascal_voc.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 9dc15bbb..ad78246b 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -195,24 +195,18 @@ def load_pascal_voc_annotations( masks.append(mask_from_polygon) xyxy = np.array(xyxy) if len(xyxy) > 0 else np.empty((0, 4)) + for k in set(class_names): + if k not in classes: + classes.append(k) + class_id = np.array([classes.index(class_name) for class_name in class_names]) if with_masks: - masks = np.array(masks) - annotation = Detections( - xyxy=xyxy, mask=masks, class_id=np.array(class_names) - ) + annotation = Detections(xyxy=xyxy, mask=np.array(masks), class_id=class_id) else: - annotation = Detections(xyxy=xyxy, class_id=np.array(class_names)) + annotation = Detections(xyxy=xyxy, class_id=class_id) images[image_path.name] = image annotations[image_path.name] = annotation - classes += class_names - - classes = list(set(classes)) - - for annotation in annotations.values(): - class_id = [classes.index(class_name) for class_name in annotation.class_id] - annotation.class_id = np.array(class_id) return classes, images, annotations From 1febd715e2df897b8b3cdcbc84d4029228f15596 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 22:53:24 +0200 Subject: [PATCH 23/37] remove test_detections_to_pascal_voc. conversion to XML is tested, and approx of masks should be in another test suite --- test/dataset/formats/test_pascal_voc.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index f5a953a2..0d316d95 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -71,7 +71,3 @@ def test_object_to_pascal_voc( def test_load_pascal_voc_annotations(expected_result, exception: Exception): ... - - -def test_detections_to_pascal_voc(expected_result, exception: Exception): - ... From 16424325417f160269c5854c7142262e89001a8b Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 23:08:50 +0200 Subject: [PATCH 24/37] add test_parse_polygon_points --- supervision/dataset/formats/pascal_voc.py | 17 +++++++++++------ test/dataset/formats/test_pascal_voc.py | 23 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index ad78246b..1cc0bfbf 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -181,12 +181,7 @@ def load_pascal_voc_annotations( with_masks = force_masks if force_masks else with_masks for polygon in obj.findall("polygon"): - polygon_points = [] - coords = polygon.findall(".//*") - for i in range(0, len(coords), 2): - x = int(coords[i].text) - y = int(coords[i + 1].text) - polygon_points.append([x, y]) + polygon_points = parse_polygon_points(polygon) mask_from_polygon = polygon_to_mask( polygon=np.array(polygon_points), @@ -211,6 +206,16 @@ def load_pascal_voc_annotations( return classes, images, annotations +def parse_polygon_points(polygon: Element): + polygon_points = [] + coords = polygon.findall(".//*") + for i in range(0, len(coords), 2): + x = int(coords[i].text) + y = int(coords[i + 1].text) + polygon_points.append([x, y]) + return polygon_points + + def load_pascal_voc_annotations_v1( annotation_path: str, ) -> Tuple[str, Detections, List[str]]: diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index 0d316d95..5d1dae69 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -10,6 +10,7 @@ from supervision.dataset.formats.pascal_voc import ( detections_to_pascal_voc, load_pascal_voc_annotations, object_to_pascal_voc, + parse_polygon_points, ) from supervision.detection.core import Detections @@ -69,5 +70,23 @@ def test_object_to_pascal_voc( assert are_xml_elements_equal(result, expected_result) -def test_load_pascal_voc_annotations(expected_result, exception: Exception): - ... +@pytest.mark.parametrize( + "polygon_element, expected_result, exception", + [ + ( + ET.fromstring( + """001001010010""" + ), + [[0, 0], [10, 0], [10, 10], [0, 10]], + DoesNotRaise(), + ) + ], +) +def test_parse_polygon_points( + polygon_element, + expected_result: List[list], + exception, +): + with exception: + result = parse_polygon_points(polygon_element) + assert result == expected_result From e4ef57ec2d6a5c455ff7cc50869f0254c5ca56e8 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 23:27:33 +0200 Subject: [PATCH 25/37] add test_detections_from_xml_obj --- supervision/dataset/formats/pascal_voc.py | 87 +++++++++++++---------- test/dataset/formats/test_pascal_voc.py | 23 ++++++ 2 files changed, 72 insertions(+), 38 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 1cc0bfbf..c325e80a 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -161,44 +161,10 @@ def load_pascal_voc_annotations( tree = parse(annotation_path) root = tree.getroot() - xyxy = [] - class_names = [] - masks = [] - with_masks = False - for obj in root.findall("object"): - class_name = obj.find("name").text - class_names.append(class_name) - - bbox = obj.find("bndbox") - x1 = int(bbox.find("xmin").text) - y1 = int(bbox.find("ymin").text) - x2 = int(bbox.find("xmax").text) - y2 = int(bbox.find("ymax").text) - - xyxy.append([x1, y1, x2, y2]) - - with_masks = obj.find("polygon") is not None - with_masks = force_masks if force_masks else with_masks - - for polygon in obj.findall("polygon"): - polygon_points = parse_polygon_points(polygon) - - mask_from_polygon = polygon_to_mask( - polygon=np.array(polygon_points), - resolution_wh=(image.shape[0], image.shape[1]), - ) - masks.append(mask_from_polygon) - - xyxy = np.array(xyxy) if len(xyxy) > 0 else np.empty((0, 4)) - for k in set(class_names): - if k not in classes: - classes.append(k) - class_id = np.array([classes.index(class_name) for class_name in class_names]) - - if with_masks: - annotation = Detections(xyxy=xyxy, mask=np.array(masks), class_id=class_id) - else: - annotation = Detections(xyxy=xyxy, class_id=class_id) + resolution_wh = (image.shape[0], image.shape[1]) + annotation, classes = detections_from_xml_obj( + root, classes, resolution_wh, force_masks + ) images[image_path.name] = image annotations[image_path.name] = annotation @@ -206,6 +172,51 @@ def load_pascal_voc_annotations( return classes, images, annotations +def detections_from_xml_obj(root, classes, resolution_wh, force_masks=False): + xyxy = [] + class_names = [] + masks = [] + with_masks = False + extended_classes = classes[:] + for obj in root.findall("object"): + class_name = obj.find("name").text + class_names.append(class_name) + + bbox = obj.find("bndbox") + x1 = int(bbox.find("xmin").text) + y1 = int(bbox.find("ymin").text) + x2 = int(bbox.find("xmax").text) + y2 = int(bbox.find("ymax").text) + + xyxy.append([x1, y1, x2, y2]) + + with_masks = obj.find("polygon") is not None + with_masks = force_masks if force_masks else with_masks + + for polygon in obj.findall("polygon"): + polygon_points = parse_polygon_points(polygon) + + mask_from_polygon = polygon_to_mask( + polygon=np.array(polygon_points), + resolution_wh=resolution_wh, + ) + masks.append(mask_from_polygon) + + xyxy = np.array(xyxy) if len(xyxy) > 0 else np.empty((0, 4)) + for k in set(class_names): + if k not in extended_classes: + extended_classes.append(k) + class_id = np.array( + [extended_classes.index(class_name) for class_name in class_names] + ) + + if with_masks: + annotation = Detections(xyxy=xyxy, mask=np.array(masks), class_id=class_id) + else: + annotation = Detections(xyxy=xyxy, class_id=class_id) + return annotation, extended_classes + + def parse_polygon_points(polygon: Element): polygon_points = [] coords = polygon.findall(".//*") diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index 5d1dae69..fa900f1c 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -7,6 +7,7 @@ import numpy as np import pytest from supervision.dataset.formats.pascal_voc import ( + detections_from_xml_obj, detections_to_pascal_voc, load_pascal_voc_annotations, object_to_pascal_voc, @@ -90,3 +91,25 @@ def test_parse_polygon_points( with exception: result = parse_polygon_points(polygon_element) assert result == expected_result + + +@pytest.mark.parametrize( + "xml_string, classes, resolution_wh, force_masks, expected_result, exception", + [ + ( + """test.jpg100100test001010""", + ["test"], + (100, 100), + False, + mock_detections(np.array([[0, 0, 10, 10]]), None, [0]), + DoesNotRaise(), + ) + ], +) +def test_detections_from_xml_obj( + xml_string, classes, resolution_wh, force_masks, expected_result, exception +): + with exception: + root = ET.fromstring(xml_string) + result, _ = detections_from_xml_obj(root, classes, resolution_wh, force_masks) + assert result == expected_result From f8005b268b86c661350feb818a87ea6768fb7cfd Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 23:39:42 +0200 Subject: [PATCH 26/37] add docstrings --- supervision/dataset/formats/pascal_voc.py | 34 +++++++++++++++++++++-- test/dataset/formats/test_pascal_voc.py | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index c325e80a..53834d34 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -172,7 +172,36 @@ def load_pascal_voc_annotations( return classes, images, annotations -def detections_from_xml_obj(root, classes, resolution_wh, force_masks=False): +def detections_from_xml_obj( + root: Element, classes: List[str], resolution_wh, force_masks: bool = False +) -> Tuple[Detections, List[str]]: + """ + Converts an XML object in Pascal VOC format to a Detections object. + Expected XML format: + + ... + + dog + + 48 + 240 + 195 + 371 + + + 48 + 240 + 195 + 240 + 195 + 371 + 48 + 371 + + + + + """ xyxy = [] class_names = [] masks = [] @@ -217,7 +246,8 @@ def detections_from_xml_obj(root, classes, resolution_wh, force_masks=False): return annotation, extended_classes -def parse_polygon_points(polygon: Element): +def parse_polygon_points(polygon: Element) -> List[List[int]]: + # Parses polygon points in format: ............... polygon_points = [] coords = polygon.findall(".//*") for i in range(0, len(coords), 2): diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index fa900f1c..d1f25bd7 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -97,7 +97,7 @@ def test_parse_polygon_points( "xml_string, classes, resolution_wh, force_masks, expected_result, exception", [ ( - """test.jpg100100test001010""", + """test001010""", ["test"], (100, 100), False, From e8f616ba2fe4fef84aa5fdd2a487210e6be97e0e Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 23:41:02 +0200 Subject: [PATCH 27/37] cleanup imports --- test/dataset/formats/test_pascal_voc.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index d1f25bd7..b12f2964 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -1,19 +1,16 @@ import xml.etree.ElementTree as ET from contextlib import ExitStack as DoesNotRaise from test.utils import mock_detections -from typing import List, Optional, Tuple +from typing import List, Optional import numpy as np import pytest from supervision.dataset.formats.pascal_voc import ( detections_from_xml_obj, - detections_to_pascal_voc, - load_pascal_voc_annotations, object_to_pascal_voc, parse_polygon_points, ) -from supervision.detection.core import Detections def are_xml_elements_equal(elem1, elem2): From 12b3def032142b0a466e3847a19f60ac5ec4224a Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Sat, 29 Jul 2023 23:42:59 +0200 Subject: [PATCH 28/37] upd docstring --- supervision/dataset/formats/pascal_voc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 53834d34..e915159c 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -201,6 +201,8 @@ def detections_from_xml_obj( + Returns: + Tuple[Detections, List[str]]: A tuple containing a Detections object and an updated list of class names, extended with the class names from the XML object. """ xyxy = [] class_names = [] From 95c8576aaf74a52f15afd769a5d08031d39b06bb Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 13:09:35 +0200 Subject: [PATCH 29/37] fix mask shape (N, W, H) -> (N, H, W) --- supervision/dataset/formats/pascal_voc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index e915159c..93e90927 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -161,7 +161,7 @@ def load_pascal_voc_annotations( tree = parse(annotation_path) root = tree.getroot() - resolution_wh = (image.shape[0], image.shape[1]) + resolution_wh = (image.shape[1], image.shape[0]) annotation, classes = detections_from_xml_obj( root, classes, resolution_wh, force_masks ) From 77c2afe55917526b201263de6012759a9382e43f Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 13:13:32 +0200 Subject: [PATCH 30/37] fix mask dtype --- supervision/detection/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index 63206ddc..cdca3ff7 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -17,8 +17,9 @@ def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.n np.ndarray: The generated 2D mask, where the polygon is marked with `1`'s and the rest is filled with `0`'s. """ width, height = resolution_wh - mask = np.zeros((height, width), dtype=np.uint8) + mask = np.zeros((height, width)) cv2.fillPoly(mask, [polygon], color=1) + mask = mask.astype(bool) return mask From 9462a3058d3bf57cc428afc28f454ce645cf0d9e Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 13:15:40 +0200 Subject: [PATCH 31/37] cast masks to bool only when creating a Detection obj --- supervision/dataset/formats/pascal_voc.py | 3 +-- supervision/detection/utils.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 93e90927..18f130c7 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -242,14 +242,13 @@ def detections_from_xml_obj( ) if with_masks: - annotation = Detections(xyxy=xyxy, mask=np.array(masks), class_id=class_id) + annotation = Detections(xyxy=xyxy, mask=np.array(masks).astype(bool), class_id=class_id) else: annotation = Detections(xyxy=xyxy, class_id=class_id) return annotation, extended_classes def parse_polygon_points(polygon: Element) -> List[List[int]]: - # Parses polygon points in format: ............... polygon_points = [] coords = polygon.findall(".//*") for i in range(0, len(coords), 2): diff --git a/supervision/detection/utils.py b/supervision/detection/utils.py index cdca3ff7..e30e6a05 100644 --- a/supervision/detection/utils.py +++ b/supervision/detection/utils.py @@ -19,7 +19,6 @@ def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.n width, height = resolution_wh mask = np.zeros((height, width)) cv2.fillPoly(mask, [polygon], color=1) - mask = mask.astype(bool) return mask From c0c292ef8270196eac2752893c38047fc8de78f7 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 15:19:39 +0200 Subject: [PATCH 32/37] lint --- supervision/dataset/formats/pascal_voc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 18f130c7..17223d31 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -242,7 +242,9 @@ def detections_from_xml_obj( ) if with_masks: - annotation = Detections(xyxy=xyxy, mask=np.array(masks).astype(bool), class_id=class_id) + annotation = Detections( + xyxy=xyxy, mask=np.array(masks).astype(bool), class_id=class_id + ) else: annotation = Detections(xyxy=xyxy, class_id=class_id) return annotation, extended_classes From 36d3e94a769870ffbd416c2bbb8b736ae3e384b3 Mon Sep 17 00:00:00 2001 From: Hardik Dava <39372750+hardikdava@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:51:02 +0200 Subject: [PATCH 33/37] removing unwanted code --- supervision/dataset/core.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/supervision/dataset/core.py b/supervision/dataset/core.py index 7167171a..ce7e0edb 100644 --- a/supervision/dataset/core.py +++ b/supervision/dataset/core.py @@ -46,14 +46,6 @@ class BaseDataset(ABC): ) -> Tuple[BaseDataset, BaseDataset]: pass - def add_instance( - self, filename: str, image: np.ndarray, detections: Detections - ) -> None: - pass - - def add_class_names(self, class_names: List[str]) -> None: - pass - @dataclass class DetectionDataset(BaseDataset): From e2d1fe7695a98a1e7fb69da96cd9326b41a20fba Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 19:42:53 +0200 Subject: [PATCH 34/37] remove artifacts --- test/dataset/formats/test_pascal_voc.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index b12f2964..8ae38801 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -61,10 +61,6 @@ def test_object_to_pascal_voc( ): with exception: result = object_to_pascal_voc(xyxy=xyxy, name=name, polygon=polygon) - with open("/tmp/test.xml", "w") as f: - f.write(ET.tostring(result).decode()) - with open("/tmp/exptest.xml", "w") as f: - f.write(ET.tostring(expected_result).decode()) assert are_xml_elements_equal(result, expected_result) From 26289e37439186ad18c39aedcff525d7cdb15340 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 19:43:32 +0200 Subject: [PATCH 35/37] drop load_pascal_voc_annotations_v1 --- supervision/dataset/formats/pascal_voc.py | 37 ----------------------- 1 file changed, 37 deletions(-) diff --git a/supervision/dataset/formats/pascal_voc.py b/supervision/dataset/formats/pascal_voc.py index 17223d31..4efb2917 100644 --- a/supervision/dataset/formats/pascal_voc.py +++ b/supervision/dataset/formats/pascal_voc.py @@ -258,40 +258,3 @@ def parse_polygon_points(polygon: Element) -> List[List[int]]: y = int(coords[i + 1].text) polygon_points.append([x, y]) return polygon_points - - -def load_pascal_voc_annotations_v1( - annotation_path: str, -) -> Tuple[str, Detections, List[str]]: - """ - Loads PASCAL VOC XML annotations and returns the image name, a Detections instance, and a list of class names. - - Args: - annotation_path (str): The path to the PASCAL VOC XML annotations file. - - Returns: - Tuple[str, Detections, List[str]]: A tuple containing the image name, a Detections instance, and a list of class names of objects in the detections. - """ - tree = parse(annotation_path) - root = tree.getroot() - - image_name = root.find("filename").text - - xyxy = [] - class_names = [] - for obj in root.findall("object"): - class_name = obj.find("name").text - class_names.append(class_name) - - bbox = obj.find("bndbox") - x1 = int(bbox.find("xmin").text) - y1 = int(bbox.find("ymin").text) - x2 = int(bbox.find("xmax").text) - y2 = int(bbox.find("ymax").text) - - xyxy.append([x1, y1, x2, y2]) - - xyxy = np.array(xyxy) - detections = Detections(xyxy=xyxy) - - return image_name, detections, class_names From 6fdabf0a964e5eda445256070c025a7797de5269 Mon Sep 17 00:00:00 2001 From: kirilllzaitsev Date: Mon, 31 Jul 2023 20:10:20 +0200 Subject: [PATCH 36/37] extend tests for test_detections_from_xml_obj --- test/dataset/formats/test_pascal_voc.py | 43 +++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/test/dataset/formats/test_pascal_voc.py b/test/dataset/formats/test_pascal_voc.py index 8ae38801..fa3f1124 100644 --- a/test/dataset/formats/test_pascal_voc.py +++ b/test/dataset/formats/test_pascal_voc.py @@ -86,17 +86,56 @@ def test_parse_polygon_points( assert result == expected_result +ONE_CLASS_N_BBOX = """test001010test10102020""" + + +ONE_CLASS_ONE_BBOX = """test001010""" + + +N_CLASS_N_BBOX = """test001010test20303040test210102020""" + +NO_DETECTIONS = """""" + + @pytest.mark.parametrize( "xml_string, classes, resolution_wh, force_masks, expected_result, exception", [ ( - """test001010""", + ONE_CLASS_ONE_BBOX, ["test"], (100, 100), False, mock_detections(np.array([[0, 0, 10, 10]]), None, [0]), DoesNotRaise(), - ) + ), + ( + ONE_CLASS_N_BBOX, + ["test"], + (100, 100), + False, + mock_detections(np.array([[0, 0, 10, 10], [10, 10, 20, 20]]), None, [0, 0]), + DoesNotRaise(), + ), + ( + N_CLASS_N_BBOX, + ["test", "test2"], + (100, 100), + False, + mock_detections( + np.array([[0, 0, 10, 10], [20, 30, 30, 40], [10, 10, 20, 20]]), + None, + [0, 0, 1], + ), + DoesNotRaise(), + ), + ( + NO_DETECTIONS, + [], + (100, 100), + False, + mock_detections(np.empty((0, 4)), None, []), + DoesNotRaise(), + ), ], ) def test_detections_from_xml_obj( From fa44884ea74cf85cefefd59a1d24438cd63fa9b7 Mon Sep 17 00:00:00 2001 From: SkalskiP Date: Mon, 31 Jul 2023 23:16:20 +0200 Subject: [PATCH 37/37] =?UTF-8?q?=F0=9F=92=AC=20update=20YOLOv8=20deprecat?= =?UTF-8?q?ed=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supervision/detection/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervision/detection/core.py b/supervision/detection/core.py index 033a41e5..4d7f8e5c 100644 --- a/supervision/detection/core.py +++ b/supervision/detection/core.py @@ -172,7 +172,7 @@ class Detections: @classmethod @deprecated( - "Please use sv.Detections.from_ultralytics() API for future usage. This method is deprecated and removed in future release" + "This method is deprecated and removed in 0.15.0 release. Use sv.Detections.from_ultralytics() instead." ) def from_yolov8(cls, yolov8_results) -> Detections: """