From b3ee31ab64d98737bc3d0c92eb71f58e1fc0fefe Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Mon, 12 Feb 2024 15:31:44 +0000 Subject: [PATCH 01/28] WIP --- darwin/utils/utils.py | 194 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 35 deletions(-) diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index 6d84c82f7..c7716ab84 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -627,9 +627,11 @@ def _parse_darwin_v2(path: Path, data: Dict[str, Any]) -> dt.AnnotationFile: is_video=slot.frame_urls is not None or slot.frame_manifest is not None, image_width=slot.width, image_height=slot.height, - image_url=None - if len(slot.source_files or []) == 0 - else slot.source_files[0]["url"], + image_url=( + None + if len(slot.source_files or []) == 0 + else slot.source_files[0]["url"] + ), image_thumbnail_url=slot.thumbnail_url, workview_url=item_source.get("workview_url", None), seq=0, @@ -757,7 +759,12 @@ def _parse_darwin_video( return annotation_file -def _parse_darwin_annotation(annotation: Dict[str, Any]) -> Optional[dt.Annotation]: +def _parse_darwin_annotation( + annotation: Dict[str, Any], + only_keyframes: bool = False, + annotation_type: Optional[str] = None, + annotation_data: Optional[Dict] = None, +) -> Optional[dt.Annotation]: slot_names = parse_slot_names(annotation) name: str = annotation["name"] main_annotation: Optional[dt.Annotation] = None @@ -784,34 +791,6 @@ def _parse_darwin_annotation(annotation: Dict[str, Any]) -> Optional[dt.Annotati main_annotation = dt.make_polygon( name, paths[0], bounding_box, slot_names=slot_names ) - # Darwin JSON 1.0 representation of complex and simple polygons - elif "polygon" in annotation: - bounding_box = annotation.get("bounding_box") - if "additional_paths" in annotation["polygon"]: - paths = [annotation["polygon"]["path"]] + annotation["polygon"][ - "additional_paths" - ] - main_annotation = dt.make_complex_polygon( - name, paths, bounding_box, slot_names=slot_names - ) - else: - main_annotation = dt.make_polygon( - name, annotation["polygon"]["path"], bounding_box, slot_names=slot_names - ) - # Darwin JSON 1.0 representation of complex polygons - elif "complex_polygon" in annotation: - bounding_box = annotation.get("bounding_box") - if isinstance(annotation["complex_polygon"]["path"][0], list): - paths = annotation["complex_polygon"]["path"] - else: - paths = [annotation["complex_polygon"]["path"]] - - if "additional_paths" in annotation["complex_polygon"]: - paths.extend(annotation["complex_polygon"]["additional_paths"]) - - main_annotation = dt.make_complex_polygon( - name, paths, bounding_box, slot_names=slot_names - ) elif "bounding_box" in annotation: bounding_box = annotation["bounding_box"] main_annotation = dt.make_bounding_box( @@ -876,6 +855,11 @@ def _parse_darwin_annotation(annotation: Dict[str, Any]) -> Optional[dt.Annotati raster_layer["dense_rle"], slot_names=slot_names, ) + elif only_keyframes: + main_annotaiton = make_keyframe_annotation( + annotation_type, annotation_data, name, slot_names + ) + annotation_type, annotation_data = get_annotation_type_and_data(main_annotation, annotation_type, annotation_data) if not main_annotation: print(f"[WARNING] Unsupported annotation type: '{annotation.keys()}'") @@ -917,7 +901,58 @@ def _parse_darwin_annotation(annotation: Dict[str, Any]) -> Optional[dt.Annotati if "properties" in annotation: main_annotation.properties = _parse_properties(annotation["properties"]) - return main_annotation + return main_annotation, annotation_data + + +def make_keyframe_annotation( + annotation_type: Optional[str], + annotation_data: Optional[Dict], + name: str, + slot_names: List[str], +) -> dt.Annotation: + if annotation_type == "polygon": + return dt.make_polygon( + name, annotation_data["paths"], annotation_data["bounding_box"] + ) + elif annotation_type == "bounding_box": + return dt.make_bounding_box( + name, + annotation_data["x"], + annotation_data["y"], + annotation_data["w"], + annotation_data["h"], + ) + elif annotation_type == "tag": + return dt.make_tag(name) + elif annotation_type == "line": + return dt.make_line(name, annotation_data["path"]) + elif annotation_type == "keypoint": + return dt.make_keypoint(name, annotation_data["x"], annotation_data["y"]) + elif annotation_type == "ellipse": + return dt.make_ellipse(name, annotation_data) + elif annotation_type == "cuboid": + return dt.make_cuboid(name, annotation_data) + elif annotation_type == "skeleton": + return dt.make_skeleton(name, annotation_data["nodes"]) + elif annotation_type == "table": + return dt.make_table( + name, annotation_data["bounding_box"], annotation_data["cells"] + ) + elif annotation_type == "string": + return dt.make_string(name, annotation_data["sources"]) + elif annotation_type == "graph": + return dt.make_graph(name, annotation_data["nodes"], annotation_data["edges"]) + elif annotation_type == "mask": + return dt.make_mask(name) + elif annotation_type == "raster_layer": + return dt.make_raster_layer( + name, + annotation_data["mask_annotation_ids_mapping"], + annotation_data["total_pixels"], + annotation_data["dense_rle"], + ) + else: + raise ValueError(f"Unsupported annotation type: '{annotation_type}'") def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotation]: @@ -925,9 +960,22 @@ def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotat frame_annotations = {} keyframes: Dict[int, bool] = {} frames = {**annotation.get("frames", {}), **annotation.get("sections", {})} + only_keyframes = annotation["only_keyframes"] + annotation_type, annotation_data = None, None + if only_keyframes: + for f, frame in frames.items(): + annotation_type, annotation_data = get_annotation_type_and_data( + frame, annotation_type, annotation_data + ) + if annotation_type: + break for f, frame in frames.items(): - frame_annotations[int(f)] = _parse_darwin_annotation( - {**frame, **{"name": name, "id": annotation.get("id", None)}} + print(f) + frame_annotations[int(f)], annotation_data = _parse_darwin_annotation( + {**frame, **{"name": name, "id": annotation.get("id", None)}}, + only_keyframes, + annotation_type, + annotation_data, ) keyframes[int(f)] = frame.get("keyframe", False) @@ -954,6 +1002,82 @@ def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotat return main_annotation +def get_annotation_type_and_data( + frame: Dict, + annotation_type: str, + annotation_data: Dict +) -> Tuple[Optional[str], Optional[Dict]]: + """ + Returns the type of a given video annotation and its data. + """ + if "polygon" in frame and "paths" in frame["polygon"]: + bounding_box = frame.get("bounding_box") + paths = frame["polygon"]["paths"] + annotation_type = "polygon" + annotation_data = { + "paths": paths, + "bounding_box": bounding_box, + } + elif "bounding_box" in frame: + bounding_box = frame["bounding_box"] + annotation_type = "bounding_box" + annotation_data = { + "x": bounding_box["x"], + "y": bounding_box["y"], + "w": bounding_box["w"], + "h": bounding_box["h"], + } + elif "tag" in frame: + annotation_type = "tag" + annotation_data = {} + elif "line" in frame: + annotation_type = "line" + annotation_data = {"path": frame["line"]["path"]} + elif "keypoint" in frame: + annotation_type = "keypoint" + annotation_data = { + "x": frame["keypoint"]["x"], + "y": frame["keypoint"]["y"], + } + elif "ellipse" in frame: + annotation_type = "ellipse" + annotation_data = frame["ellipse"] + elif "cuboid" in frame: + annotation_type = "cuboid" + annotation_data = frame["cuboid"] + elif "skeleton" in frame: + annotation_type = "skeleton" + annotation_data = {"nodes": frame["skeleton"]["nodes"]} + elif "table" in frame: + annotation_type = "table" + annotation_data = { + "bounding_box": frame["table"]["bounding_box"], + "cells": frame["table"]["cells"], + } + elif "string" in frame: + annotation_type = "string" + annotation_data = {"sources": frame["string"]["sources"]} + elif "graph" in frame: + annotation_type = "graph" + annotation_type = { + "nodes": frame["graph"]["nodes"], + "edges": frame["graph"]["edges"], + } + elif "mask" in frame: + annotation_type = "mask" + annotation_data = {} + elif "raster_layer" in frame: + raster_layer = frame["raster_layer"] + annotation_type = "raster_layer" + annotation_data = { + "dense_rle": raster_layer["dense_rle"], + "mask_annotation_ids_mapping": raster_layer["mask_annotation_ids_mapping"], + "total_pixels": raster_layer["total_pixels"], + } + + return annotation_type, annotation_data + + def _parse_darwin_raster_annotation(annotation: dict) -> Optional[dt.Annotation]: if not annotation.get("raster_layer"): raise ValueError("Raster annotation must have a 'raster_layer' field") From ec2c48014a0dc7687771d9c8d91cbf09046f3827 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 13 Feb 2024 12:27:09 +0000 Subject: [PATCH 02/28] WIP --- darwin/utils/utils.py | 105 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index c7716ab84..7e1caedd9 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -856,10 +856,14 @@ def _parse_darwin_annotation( slot_names=slot_names, ) elif only_keyframes: - main_annotaiton = make_keyframe_annotation( + main_annotation = make_keyframe_annotation( annotation_type, annotation_data, name, slot_names ) - annotation_type, annotation_data = get_annotation_type_and_data(main_annotation, annotation_type, annotation_data) + + # If we hit a keyframe, we need to update annotation_data for frames later on that may be missing a main type + annotation_type, annotation_data = update_annotation_data( + main_annotation.data, annotation_type, annotation_data + ) if not main_annotation: print(f"[WARNING] Unsupported annotation type: '{annotation.keys()}'") @@ -911,7 +915,7 @@ def make_keyframe_annotation( slot_names: List[str], ) -> dt.Annotation: if annotation_type == "polygon": - return dt.make_polygon( + return dt.make_complex_polygon( name, annotation_data["paths"], annotation_data["bounding_box"] ) elif annotation_type == "bounding_box": @@ -955,6 +959,76 @@ def make_keyframe_annotation( raise ValueError(f"Unsupported annotation type: '{annotation_type}'") +def update_annotation_data( + main_annotation_data: Dict[str, Any], + annotation_type: Optional[str], + annotation_data: Optional[Dict], +) -> Tuple[Optional[str], Optional[Dict]]: + if annotation_type == "polygon": + bounding_box = main_annotation_data.get("bounding_box") + paths = main_annotation_data["paths"] + annotation_type = "polygon" + annotation_data = {"paths": paths, "bounding_box": bounding_box} + elif annotation_type == "bounding_box": + bounding_box = main_annotation_data.get("bounding_box") + annotation_type = "bounding_box" + annotation_data = { + "x": bounding_box["x"], + "y": bounding_box["y"], + "w": bounding_box["w"], + "h": bounding_box["h"], + } + elif annotation_type == "tag": + annotation_type = "tag" + annotation_data = {} + elif annotation_type == "line": + annotation_type = "line" + annotation_data = {"path": main_annotation_data["line"]["path"]} + elif annotation_type == "keypoint": + annotation_type = "keypoint" + annotation_data = { + "x": main_annotation_data["keypoint"]["x"], + "y": main_annotation_data["keypoint"]["y"], + } + elif annotation_type == "ellipse": + annotation_type = "ellipse" + annotation_data = main_annotation_data["ellipse"] + elif annotation_type == "cuboid": + annotation_type = "cuboid" + annotation_data = main_annotation_data["cuboid"] + elif annotation_type == "skeleton": + annotation_type = "skeleton" + annotation_data = {"nodes": main_annotation_data["skeleton"]["nodes"]} + elif annotation_type == "table": + annotation_type = "table" + annotation_data = { + "bounding_box": main_annotation_data["table"]["bounding_box"], + "cells": main_annotation_data["table"]["cells"], + } + elif annotation_type == "string": + annotation_type = "string" + annotation_data = {"sources": main_annotation_data["string"]["sources"]} + elif annotation_type == "graph": + annotation_type = "graph" + annotation_data = { + "nodes": main_annotation_data["graph"]["nodes"], + "edges": main_annotation_data["graph"]["edges"], + } + elif annotation_type == "mask": + annotation_type = "mask" + annotation_data = {} + elif annotation_type == "raster_layer": + raster_layer = main_annotation_data["raster_layer"] + annotation_type = "raster_layer" + annotation_data = { + "dense_rle": raster_layer["dense_rle"], + "mask_annotation_ids_mapping": raster_layer["mask_annotation_ids_mapping"], + "total_pixels": raster_layer["total_pixels"], + } + + return annotation_type, annotation_data + + def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotation]: name = annotation["name"] frame_annotations = {} @@ -971,6 +1045,7 @@ def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotat break for f, frame in frames.items(): print(f) + print(annotation["id"]) frame_annotations[int(f)], annotation_data = _parse_darwin_annotation( {**frame, **{"name": name, "id": annotation.get("id", None)}}, only_keyframes, @@ -1003,21 +1078,23 @@ def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotat def get_annotation_type_and_data( - frame: Dict, - annotation_type: str, - annotation_data: Dict + frame: Dict, annotation_type: str, annotation_data: Dict ) -> Tuple[Optional[str], Optional[Dict]]: """ Returns the type of a given video annotation and its data. """ - if "polygon" in frame and "paths" in frame["polygon"]: - bounding_box = frame.get("bounding_box") - paths = frame["polygon"]["paths"] - annotation_type = "polygon" - annotation_data = { - "paths": paths, - "bounding_box": bounding_box, - } + + if "polygon" in frame: + if frame["polygon"]["paths"]: + bounding_box = frame.get("bounding_box") + paths = frame["polygon"]["paths"] + annotation_type = "polygon" + annotation_data = {"paths": paths, "bounding_box": bounding_box} + else: + bounding_box = frame.get("bounding_box") + path = frame["polygon"]["path"] + annotation_type = "polygon" + annotation_data = {"path": path, "bounding_box": bounding_box} elif "bounding_box" in frame: bounding_box = frame["bounding_box"] annotation_type = "bounding_box" From 95330f87aab567d612a4072e58e40467b11a3256 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 13 Feb 2024 14:20:01 +0000 Subject: [PATCH 03/28] WIP --- darwin/utils/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index 7e1caedd9..2fda75751 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -1034,7 +1034,7 @@ def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotat frame_annotations = {} keyframes: Dict[int, bool] = {} frames = {**annotation.get("frames", {}), **annotation.get("sections", {})} - only_keyframes = annotation["only_keyframes"] + only_keyframes = annotation.get("only_keyframes", False) annotation_type, annotation_data = None, None if only_keyframes: for f, frame in frames.items(): @@ -1044,8 +1044,6 @@ def _parse_darwin_video_annotation(annotation: dict) -> Optional[dt.VideoAnnotat if annotation_type: break for f, frame in frames.items(): - print(f) - print(annotation["id"]) frame_annotations[int(f)], annotation_data = _parse_darwin_annotation( {**frame, **{"name": name, "id": annotation.get("id", None)}}, only_keyframes, From 686a2ff8bbf4d47afdeaec09f81cc94633da38b5 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 20 Feb 2024 11:52:38 +0000 Subject: [PATCH 04/28] WIP --- darwin/datatypes.py | 46 +--------------------- darwin/exporter/exporter.py | 1 + darwin/exporter/formats/coco.py | 31 +-------------- darwin/importer/formats/coco.py | 2 +- darwin/importer/formats/dataloop.py | 3 +- darwin/importer/formats/nifti.py | 6 +-- darwin/utils/utils.py | 12 +++++- tests/darwin/dataset/dataset_utils_test.py | 24 +++++------ tests/darwin/datatypes_test.py | 5 +-- tests/darwin/utils_test.py | 7 ++-- 10 files changed, 37 insertions(+), 100 deletions(-) diff --git a/darwin/datatypes.py b/darwin/datatypes.py index 291dc3474..7cd07149c 100644 --- a/darwin/datatypes.py +++ b/darwin/datatypes.py @@ -638,49 +638,7 @@ def make_tag( def make_polygon( class_name: str, - point_path: List[Point], - bounding_box: Optional[Dict] = None, - subs: Optional[List[SubAnnotation]] = None, - slot_names: Optional[List[str]] = None, -) -> Annotation: - """ - Creates and returns a polygon annotation. - - Parameters - ---------- - class_name : str - The name of the class for this ``Annotation``. - point_path : List[Point] - A list of points that comprises the polygon. The list should have a format similar to: - - .. code-block:: python - - [ - {"x": 1, "y": 0}, - {"x": 2, "y": 1} - ] - - bounding_box : Optional[Dict], default: None - The bounding box that encompasses the polyong. - subs : Optional[List[SubAnnotation]], default: None - List of ``SubAnnotation``s for this ``Annotation``. - - Returns - ------- - Annotation - A polygon ``Annotation``. - """ - return Annotation( - AnnotationClass(class_name, "polygon"), - _maybe_add_bounding_box_data({"path": point_path}, bounding_box), - subs or [], - slot_names=slot_names or [], - ) - - -def make_complex_polygon( - class_name: str, - point_paths: List[List[Point]], + point_paths: List[List[Point]] | List[Point], bounding_box: Optional[Dict] = None, subs: Optional[List[SubAnnotation]] = None, slot_names: Optional[List[str]] = None, @@ -723,7 +681,7 @@ def make_complex_polygon( A complex polygon ``Annotation``. """ return Annotation( - AnnotationClass(class_name, "complex_polygon", "polygon"), + AnnotationClass(class_name, "polygon", "polygon"), _maybe_add_bounding_box_data({"paths": point_paths}, bounding_box), subs or [], slot_names=slot_names or [], diff --git a/darwin/exporter/exporter.py b/darwin/exporter/exporter.py index 8cf4d95c6..35a1e6161 100644 --- a/darwin/exporter/exporter.py +++ b/darwin/exporter/exporter.py @@ -36,6 +36,7 @@ def darwin_to_dt_gen( for d in split_video_annotation(data): d.seq = count count += 1 + print(count) yield d else: yield data diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index fbba4d504..8af14b8a3 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -476,6 +476,7 @@ def _build_annotations( ) -> Iterator[Optional[Dict[str, Any]]]: annotation_id = 0 for annotation_file in annotation_files: + print(annotation_file.filename) for annotation in annotation_file.annotations: annotation_id += 1 annotation_data = _build_annotation( @@ -493,36 +494,6 @@ def _build_annotation( ) -> Optional[Dict[str, Any]]: annotation_type = annotation.annotation_class.annotation_type if annotation_type == "polygon": - sequences = convert_polygons_to_sequences( - annotation.data["path"], rounding=False - ) - x_coords = [s[0::2] for s in sequences] - y_coords = [s[1::2] for s in sequences] - min_x = np.min([np.min(x_coord) for x_coord in x_coords]) - min_y = np.min([np.min(y_coord) for y_coord in y_coords]) - max_x = np.max([np.max(x_coord) for x_coord in x_coords]) - max_y = np.max([np.max(y_coord) for y_coord in y_coords]) - w = max_x - min_x - h = max_y - min_y - # Compute the area of the polygon - poly_area = np.sum( - [ - _polygon_area(x_coord, y_coord) - for x_coord, y_coord in zip(x_coords, y_coords) - ] - ) - - return { - "id": annotation_id, - "image_id": _build_image_id(annotation_file), - "category_id": categories[annotation.annotation_class.name], - "segmentation": sequences, - "area": poly_area, - "bbox": [min_x, min_y, w, h], - "iscrowd": 0, - "extra": _build_extra(annotation), - } - elif annotation_type == "complex_polygon": mask = np.zeros((annotation_file.image_height, annotation_file.image_width)) sequences = convert_polygons_to_sequences(annotation.data["paths"]) draw_polygon(mask, sequences, 1) diff --git a/darwin/importer/formats/coco.py b/darwin/importer/formats/coco.py index 28d4f4f8f..6e0c876a5 100644 --- a/darwin/importer/formats/coco.py +++ b/darwin/importer/formats/coco.py @@ -169,7 +169,7 @@ def parse_annotation( except StopIteration: break paths.append(path) - return dt.make_complex_polygon(category["name"], paths) + return dt.make_polygon(category["name"], paths) elif isinstance(segmentation, list): path = [] points = iter( diff --git a/darwin/importer/formats/dataloop.py b/darwin/importer/formats/dataloop.py index 4f546ab09..a075b4a2b 100644 --- a/darwin/importer/formats/dataloop.py +++ b/darwin/importer/formats/dataloop.py @@ -1,7 +1,6 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Set - import darwin.datatypes as dt from darwin.exceptions import ( DataloopComplexPolygonsNotYetSupported, @@ -76,6 +75,6 @@ def _parse_annotation(annotation: Dict[str, Any]) -> Optional[dt.Annotation]: raise DataloopComplexPolygonsNotYetSupported() points: List[dt.Point] = [{"x": c["x"], "y": c["y"]} for c in coords[0]] - return dt.make_polygon(annotation_label, point_path=points) + return dt.make_polygon(annotation_label, point_paths=points) return None diff --git a/darwin/importer/formats/nifti.py b/darwin/importer/formats/nifti.py index 018690188..64e2ee097 100644 --- a/darwin/importer/formats/nifti.py +++ b/darwin/importer/formats/nifti.py @@ -350,11 +350,11 @@ def adjust_for_pixdims(x, y, pixdims): ] paths.append(path) if len(paths) > 1: - polygon = dt.make_complex_polygon(class_name, paths) + polygon = dt.make_polygon(class_name, paths) elif len(paths) == 1: polygon = dt.make_polygon( class_name, - point_path=paths[0], + point_paths=paths[0], ) else: return None @@ -364,7 +364,7 @@ def adjust_for_pixdims(x, y, pixdims): return None polygon = dt.make_polygon( class_name, - point_path=[ + point_paths=[ adjust_for_pixdims(x, y, pixdims) for x, y in zip(external_path[0::2], external_path[1::2]) ], diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index 748d32480..915a25bc3 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -773,10 +773,17 @@ def _parse_darwin_annotation( if "polygon" in annotation and "paths" in annotation["polygon"]: bounding_box = annotation.get("bounding_box") paths = annotation["polygon"]["paths"] - main_annotation = dt.make_complex_polygon( + main_annotation = dt.make_polygon( name, paths, bounding_box, slot_names=slot_names ) + elif "polygon" in annotation and "path" in annotation["polygon"]: + bounding_box = annotation.get("bounding_box") + path = annotation["polygon"]["path"] + main_annotation = dt.make_polygon( + name, path, bounding_box, slot_names=slot_names + ) + elif "bounding_box" in annotation: bounding_box = annotation["bounding_box"] main_annotation = dt.make_bounding_box( @@ -896,7 +903,7 @@ def make_keyframe_annotation( slot_names: List[str], ) -> dt.Annotation: if annotation_type == "polygon": - return dt.make_complex_polygon( + return dt.make_polygon( name, annotation_data["paths"], annotation_data["bounding_box"] ) elif annotation_type == "bounding_box": @@ -1254,6 +1261,7 @@ def split_video_annotation(annotation: dt.AnnotationFile) -> List[dt.AnnotationF urls = annotation.frame_urls or [None] * (annotation.frame_count or 1) frame_annotations = [] for i, frame_url in enumerate(urls): + print(i) annotations = [ a.frames[i] for a in annotation.annotations diff --git a/tests/darwin/dataset/dataset_utils_test.py b/tests/darwin/dataset/dataset_utils_test.py index 29157be75..893224829 100644 --- a/tests/darwin/dataset/dataset_utils_test.py +++ b/tests/darwin/dataset/dataset_utils_test.py @@ -72,14 +72,14 @@ def annotations_path(self, tmp_path: Path): def test_builds_correct_mapping_dictionaries(self, annotations_path: Path): payload = { "annotations": [ - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, { "name": "class_2", "bounding_box": {"x": 0, "y": 0, "w": 100, "h": 100}, }, - {"name": "class_3", "polygon": {"path": []}}, + {"name": "class_3", "polygon": {"paths": [[]]}}, {"name": "class_4", "tag": {}}, - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, ], "image": {"filename": "0.jpg"}, } @@ -87,14 +87,14 @@ def test_builds_correct_mapping_dictionaries(self, annotations_path: Path): payload = { "annotations": [ - {"name": "class_5", "polygon": {"path": []}}, + {"name": "class_5", "polygon": {"paths": [[]]}}, { "name": "class_6", "bounding_box": {"x": 0, "y": 0, "w": 100, "h": 100}, }, - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, {"name": "class_4", "tag": {}}, - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, ], "image": {"filename": "1.jpg"}, } @@ -122,14 +122,14 @@ def test_extract_multiple_annotation_types(self, annotations_path: Path): "0.json", { "annotations": [ - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, { "name": "class_2", "bounding_box": {"x": 0, "y": 0, "w": 100, "h": 100}, }, - {"name": "class_3", "polygon": {"path": []}}, + {"name": "class_3", "polygon": {"paths": [[]]}}, {"name": "class_4", "tag": {}}, - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, ], "image": {"filename": "0.jpg"}, }, @@ -139,14 +139,14 @@ def test_extract_multiple_annotation_types(self, annotations_path: Path): "1.json", { "annotations": [ - {"name": "class_5", "polygon": {"path": []}}, + {"name": "class_5", "polygon": {"paths": [[]]}}, { "name": "class_6", "bounding_box": {"x": 0, "y": 0, "w": 100, "h": 100}, }, - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, {"name": "class_4", "tag": {}}, - {"name": "class_1", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [[]]}}, ], "image": {"filename": "1.jpg"}, }, diff --git a/tests/darwin/datatypes_test.py b/tests/darwin/datatypes_test.py index 569f8e285..5b5b87143 100644 --- a/tests/darwin/datatypes_test.py +++ b/tests/darwin/datatypes_test.py @@ -8,7 +8,6 @@ from darwin.datatypes import ( Point, - make_complex_polygon, make_polygon, parse_property_classes, split_paths_by_metadata, @@ -48,7 +47,7 @@ def test_it_returns_annotation_with_default_params(self): [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 1, "y": 2}], [{"x": 4, "y": 5}, {"x": 6, "y": 7}, {"x": 4, "y": 5}], ] - annotation = make_complex_polygon(class_name, points) + annotation = make_polygon(class_name, points) assert_annotation_class(annotation, class_name, "complex_polygon", "polygon") @@ -62,7 +61,7 @@ def test_it_returns_annotation_with_bounding_box(self): [{"x": 4, "y": 5}, {"x": 6, "y": 7}, {"x": 4, "y": 5}], ] bbox: Dict[str, float] = {"x": 1, "y": 2, "w": 2, "h": 2} - annotation = make_complex_polygon(class_name, points, bbox) + annotation = make_polygon(class_name, points, bbox) assert_annotation_class(annotation, class_name, "complex_polygon", "polygon") diff --git a/tests/darwin/utils_test.py b/tests/darwin/utils_test.py index 8edcd0c9e..ed1a0c611 100644 --- a/tests/darwin/utils_test.py +++ b/tests/darwin/utils_test.py @@ -623,7 +623,7 @@ def test_imports_a_skeleton(self, tmp_path): }, "name": "car", "polygon": { - "path": [ + "paths": [ { "x": 1805.0, "y": 586.0 @@ -670,7 +670,8 @@ def test_imports_a_skeleton(self, tmp_path): annotation_file: dt.AnnotationFile = parse_darwin_json(import_file, None) assert ( - annotation_file.annotations[0].annotation_class.annotation_type == "polygon" + annotation_file.annotations[0].annotation_class.annotation_type + == "complex_polygon" ) assert ( annotation_file.annotations[1].annotation_class.annotation_type @@ -694,7 +695,7 @@ def test_imports_multiple_skeletetons(self, tmp_path): }, "name":"car", "polygon":{ - "path":[ + "paths":[ { "x":1805.0, "y":586.0 From 6bcbd5e2fc8f09034cf417d67c56a13dcd63e5e7 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 20 Feb 2024 15:57:11 +0000 Subject: [PATCH 05/28] WIP --- darwin/exporter/formats/coco.py | 28 ++++++++++--------- .../importer/formats/import_labelbox_test.py | 4 +-- tests/darwin/utils_test.py | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index 8af14b8a3..21b5f11bc 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -494,31 +494,33 @@ def _build_annotation( ) -> Optional[Dict[str, Any]]: annotation_type = annotation.annotation_class.annotation_type if annotation_type == "polygon": - mask = np.zeros((annotation_file.image_height, annotation_file.image_width)) - sequences = convert_polygons_to_sequences(annotation.data["paths"]) - draw_polygon(mask, sequences, 1) - counts = rle_encode(mask) - + sequences = convert_polygons_to_sequences( + annotation.data["paths"], rounding=False + ) x_coords = [s[0::2] for s in sequences] y_coords = [s[1::2] for s in sequences] min_x = np.min([np.min(x_coord) for x_coord in x_coords]) min_y = np.min([np.min(y_coord) for y_coord in y_coords]) max_x = np.max([np.max(x_coord) for x_coord in x_coords]) max_y = np.max([np.max(y_coord) for y_coord in y_coords]) - w = max_x - min_x + 1 - h = max_y - min_y + 1 + w = max_x - min_x + h = max_y - min_y + # Compute the area of the polygon + poly_area = np.sum( + [ + _polygon_area(x_coord, y_coord) + for x_coord, y_coord in zip(x_coords, y_coords) + ] + ) return { "id": annotation_id, "image_id": _build_image_id(annotation_file), "category_id": categories[annotation.annotation_class.name], - "segmentation": { - "counts": counts, - "size": [annotation_file.image_height, annotation_file.image_width], - }, - "area": np.sum(mask), + "segmentation": sequences, + "area": poly_area, "bbox": [min_x, min_y, w, h], - "iscrowd": 1, + "iscrowd": 0, "extra": _build_extra(annotation), } elif annotation_type == "tag": diff --git a/tests/darwin/importer/formats/import_labelbox_test.py b/tests/darwin/importer/formats/import_labelbox_test.py index f01f7b320..730456050 100644 --- a/tests/darwin/importer/formats/import_labelbox_test.py +++ b/tests/darwin/importer/formats/import_labelbox_test.py @@ -391,7 +391,7 @@ def test_it_imports_polygon_images(self, file_path: Path): ) annotation_class = polygon_annotation.annotation_class - assert_annotation_class(annotation_class, "Fish", "polygon") + assert_annotation_class(annotation_class, "Fish", "polygon", "polygon") def test_it_imports_point_images(self, file_path: Path): json: str = """ @@ -729,7 +729,7 @@ def assert_bbox(annotation: Annotation, x: float, y: float, h: float, w: float) def assert_polygon(annotation: Annotation, points: List[Point]) -> None: - actual_points = annotation.data.get("path") + actual_points = annotation.data.get("paths") assert actual_points assert actual_points == points diff --git a/tests/darwin/utils_test.py b/tests/darwin/utils_test.py index ed1a0c611..7e1c52b8b 100644 --- a/tests/darwin/utils_test.py +++ b/tests/darwin/utils_test.py @@ -671,7 +671,7 @@ def test_imports_a_skeleton(self, tmp_path): assert ( annotation_file.annotations[0].annotation_class.annotation_type - == "complex_polygon" + == "polygon" ) assert ( annotation_file.annotations[1].annotation_class.annotation_type From e83bef27df2190fc565bc9e2e37c54b32d1caac3 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 20 Feb 2024 16:26:22 +0000 Subject: [PATCH 06/28] Ensure COCO categories are always in the same, ascending order to avoid E2E flakiness --- darwin/exporter/formats/coco.py | 4 ++-- darwin/importer/importer.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index fbba4d504..b6eb0922a 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -385,7 +385,7 @@ def _calculate_categories(annotation_files: List[dt.AnnotationFile]) -> Dict[str categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return categories + return {k: v for k, v in sorted(categories.items(), key=lambda item: item[1])} def _calculate_tag_categories( @@ -401,7 +401,7 @@ def _calculate_tag_categories( categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return categories + return {k: v for k, v in sorted(categories.items(), key=lambda item: item[1])} def _calculate_category_id(annotation_class: dt.AnnotationClass) -> int: diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 88e6acd65..5a27d02b4 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -339,9 +339,9 @@ def _import_properties( # get team properties -> List[FullProperty] team_properties = client.get_team_properties() # (property-name, annotation_class_id): FullProperty object - team_properties_annotation_lookup: Dict[Tuple[str, Optional[int]], FullProperty] = ( - {} - ) + team_properties_annotation_lookup: Dict[ + Tuple[str, Optional[int]], FullProperty + ] = {} for prop in team_properties: team_properties_annotation_lookup[(prop.name, prop.annotation_class_id)] = prop @@ -828,9 +828,9 @@ def import_annotations( # noqa: C901 # Need to re parse the files since we didn't save the annotations in memory for local_path in set(local_file.path for local_file in local_files): # noqa: C401 - imported_files: Union[List[dt.AnnotationFile], dt.AnnotationFile, None] = ( - importer(local_path) - ) + imported_files: Union[ + List[dt.AnnotationFile], dt.AnnotationFile, None + ] = importer(local_path) if imported_files is None: parsed_files = [] elif not isinstance(imported_files, List): From 6b1560acb4bc68eedc043ec7fc2db979785fbcd4 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 20 Feb 2024 16:27:41 +0000 Subject: [PATCH 07/28] Ensure COCO categories are always in the same, ascending order to avoid E2E test flakiness --- darwin/exporter/formats/coco.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index 21b5f11bc..a3cb26ea6 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -385,7 +385,7 @@ def _calculate_categories(annotation_files: List[dt.AnnotationFile]) -> Dict[str categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return categories + return {k: v for k, v in sorted(categories.items(), key=lambda item: item[1])} def _calculate_tag_categories( @@ -401,7 +401,7 @@ def _calculate_tag_categories( categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return categories + return {k: v for k, v in sorted(categories.items(), key=lambda item: item[1])} def _calculate_category_id(annotation_class: dt.AnnotationClass) -> int: From 2cb96c05175a3b17bfb8656697445b72f42265cb Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 22 Feb 2024 09:41:41 +0000 Subject: [PATCH 08/28] WIP --- darwin/dataset/local_dataset.py | 3 +-- darwin/datatypes.py | 1 - tests/darwin/exporter/formats/export_coco_test.py | 2 +- tests/darwin/importer/formats/import_superannotate_test.py | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/darwin/dataset/local_dataset.py b/darwin/dataset/local_dataset.py index d7a1ee65a..8dad19f7f 100644 --- a/darwin/dataset/local_dataset.py +++ b/darwin/dataset/local_dataset.py @@ -335,8 +335,7 @@ def annotation_type_supported(self, annotation) -> bool: elif self.annotation_type == "bounding_box": is_bounding_box = annotation_type == "bounding_box" is_supported_polygon = ( - annotation_type in ["polygon", "complex_polygon"] - and "bounding_box" in annotation.data + annotation_type == "polygon" and "bounding_box" in annotation.data ) return is_bounding_box or is_supported_polygon elif self.annotation_type == "polygon": diff --git a/darwin/datatypes.py b/darwin/datatypes.py index 34652fc5f..1808bfff7 100644 --- a/darwin/datatypes.py +++ b/darwin/datatypes.py @@ -86,7 +86,6 @@ def from_dict(cls, json: JSONFreeForm) -> "JSONType": AnnotationType = Literal[ # NB: Some of these are not supported yet "bounding_box", "polygon", - "complex_polygon", "ellipse", "cuboid", "segmentation", diff --git a/tests/darwin/exporter/formats/export_coco_test.py b/tests/darwin/exporter/formats/export_coco_test.py index ecdac9aed..b3471ddf3 100644 --- a/tests/darwin/exporter/formats/export_coco_test.py +++ b/tests/darwin/exporter/formats/export_coco_test.py @@ -19,7 +19,7 @@ def annotation_file(self) -> dt.AnnotationFile: def test_polygon_include_extras(self, annotation_file: dt.AnnotationFile): polygon = dt.Annotation( dt.AnnotationClass("polygon_class", "polygon"), - {"path": [{"x": 1, "y": 1}, {"x": 2, "y": 2}, {"x": 1, "y": 2}]}, + {"paths": [{"x": 1, "y": 1}, {"x": 2, "y": 2}, {"x": 1, "y": 2}]}, [dt.make_instance_id(1)], ) diff --git a/tests/darwin/importer/formats/import_superannotate_test.py b/tests/darwin/importer/formats/import_superannotate_test.py index 2b28ada40..ea7fb8bb9 100644 --- a/tests/darwin/importer/formats/import_superannotate_test.py +++ b/tests/darwin/importer/formats/import_superannotate_test.py @@ -891,7 +891,7 @@ def assert_bbox(annotation: Annotation, x: float, y: float, h: float, w: float) def assert_polygon(annotation: Annotation, points: List[Point]) -> None: - actual_points = annotation.data.get("path") + actual_points = annotation.data.get("paths") assert actual_points assert actual_points == points From e2c36e3106b2c81871b120c42d74096d3d3486b7 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 22 Feb 2024 12:06:07 +0000 Subject: [PATCH 09/28] Removed deprecated functions & removed erroneous deprecation labels --- darwin/dataset/local_dataset.py | 2 +- darwin/datatypes.py | 9 + darwin/exporter/formats/coco.py | 316 ----------------------- darwin/exporter/formats/cvat.py | 186 ------------- darwin/exporter/formats/darwin.py | 35 +-- darwin/exporter/formats/instance_mask.py | 2 - darwin/exporter/formats/mask.py | 13 +- darwin/exporter/formats/pascalvoc.py | 144 ----------- darwin/importer/formats/coco.py | 6 - darwin/importer/importer.py | 42 +-- darwin/item.py | 36 --- darwin/utils/utils.py | 176 ------------- tests/darwin/utils/find_files_test.py | 18 +- 13 files changed, 25 insertions(+), 960 deletions(-) diff --git a/darwin/dataset/local_dataset.py b/darwin/dataset/local_dataset.py index 8dad19f7f..5ba891dd7 100644 --- a/darwin/dataset/local_dataset.py +++ b/darwin/dataset/local_dataset.py @@ -339,7 +339,7 @@ def annotation_type_supported(self, annotation) -> bool: ) return is_bounding_box or is_supported_polygon elif self.annotation_type == "polygon": - return annotation_type in ["polygon", "complex_polygon"] + return annotation_type == "polygon" else: raise ValueError( "annotation_type should be either 'tag', 'bounding_box', or 'polygon'" diff --git a/darwin/datatypes.py b/darwin/datatypes.py index 1808bfff7..c69ba13a3 100644 --- a/darwin/datatypes.py +++ b/darwin/datatypes.py @@ -679,6 +679,15 @@ def make_polygon( Annotation A complex polygon ``Annotation``. """ + + # Check if point_paths is List[Point] and convert to List[List[Point]] + if ( + isinstance(point_paths[0], dict) + and "x" in point_paths[0] + and "y" in point_paths[0] + ): + point_paths = [point_paths] + return Annotation( AnnotationClass(class_name, "polygon", "polygon"), _maybe_add_bounding_box_data({"paths": point_paths}, bounding_box), diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index a3cb26ea6..21f802f8b 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -41,321 +41,6 @@ def export(annotation_files: Iterator[dt.AnnotationFile], output_dir: Path) -> N f.write(op) -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_json(annotation_files: List[dt.AnnotationFile]) -> Dict[str, Any]: - categories: Dict[str, int] = calculate_categories(annotation_files) - tag_categories: Dict[str, int] = calculate_tag_categories(annotation_files) - return { - "info": build_info(), - "licenses": build_licenses(), - "images": build_images(annotation_files, tag_categories), - "annotations": list(build_annotations(annotation_files, categories)), - "categories": list(build_categories(categories)), - "tag_categories": list(build_tag_categories(tag_categories)), - } - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def calculate_categories(annotation_files: List[dt.AnnotationFile]) -> Dict[str, int]: - categories: Dict[str, int] = {} - for annotation_file in annotation_files: - for annotation_class in annotation_file.annotation_classes: - if ( - annotation_class.name not in categories - and annotation_class.annotation_type - in [ - "polygon", - "complex_polygon", - "bounding_box", - ] - ): - categories[annotation_class.name] = _calculate_category_id( - annotation_class - ) - return categories - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def calculate_tag_categories( - annotation_files: List[dt.AnnotationFile], -) -> Dict[str, int]: - categories: Dict[str, int] = {} - for annotation_file in annotation_files: - for annotation_class in annotation_file.annotation_classes: - if ( - annotation_class.name not in categories - and annotation_class.annotation_type == "tag" - ): - categories[annotation_class.name] = _calculate_category_id( - annotation_class - ) - return categories - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_info() -> Dict[str, Any]: - # TODO fill out these fields in a meaningful way - today = date.today() - return { - "description": "Exported from Darwin", - "url": "n/a", - "version": "n/a", - "year": today.year, - "contributor": "n/a", - "date_created": today.strftime("%Y/%m/%d"), - } - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_licenses() -> List[Dict[str, Any]]: - return [{"url": "n/a", "id": 0, "name": "placeholder license"}] - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_images( - annotation_files: List[dt.AnnotationFile], tag_categories: Dict[str, int] -) -> List[Dict[str, Any]]: - return [ - build_image(annotation_file, tag_categories) - for annotation_file in sorted(annotation_files, key=lambda x: x.seq) - ] - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_image( - annotation_file: dt.AnnotationFile, tag_categories: Dict[str, int] -) -> Dict[str, Any]: - tags = [ - annotation - for annotation in annotation_file.annotations - if annotation.annotation_class.annotation_type == "tag" - ] - return { - "license": 0, - "file_name": annotation_file.filename, - "coco_url": "n/a", - "height": annotation_file.image_height, - "width": annotation_file.image_width, - "date_captured": "", - "flickr_url": "n/a", - "darwin_url": annotation_file.image_url, - "darwin_workview_url": annotation_file.workview_url, - "id": _build_image_id(annotation_file), - "tag_ids": [tag_categories[tag.annotation_class.name] for tag in tags], - } - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_annotations( - annotation_files: List[dt.AnnotationFile], categories: Dict[str, int] -) -> Iterator[Optional[Dict[str, Any]]]: - annotation_id = 0 - for annotation_file in annotation_files: - for annotation in annotation_file.annotations: - annotation_id += 1 - annotation_data = build_annotation( - annotation_file, annotation_id, annotation, categories - ) - if annotation_data: - yield annotation_data - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_annotation( - annotation_file: dt.AnnotationFile, - annotation_id: int, - annotation: dt.Annotation, - categories: Dict[str, int], -) -> Optional[Dict[str, Any]]: - annotation_type = annotation.annotation_class.annotation_type - if annotation_type == "polygon": - sequences = convert_polygons_to_sequences( - annotation.data["path"], rounding=False - ) - x_coords = [s[0::2] for s in sequences] - y_coords = [s[1::2] for s in sequences] - min_x = np.min([np.min(x_coord) for x_coord in x_coords]) - min_y = np.min([np.min(y_coord) for y_coord in y_coords]) - max_x = np.max([np.max(x_coord) for x_coord in x_coords]) - max_y = np.max([np.max(y_coord) for y_coord in y_coords]) - w = max_x - min_x - h = max_y - min_y - # Compute the area of the polygon - poly_area = np.sum( - [ - polygon_area(x_coord, y_coord) - for x_coord, y_coord in zip(x_coords, y_coords) - ] - ) - - return { - "id": annotation_id, - "image_id": _build_image_id(annotation_file), - "category_id": categories[annotation.annotation_class.name], - "segmentation": sequences, - "area": poly_area, - "bbox": [min_x, min_y, w, h], - "iscrowd": 0, - "extra": build_extra(annotation), - } - elif annotation_type == "complex_polygon": - mask = np.zeros((annotation_file.image_height, annotation_file.image_width)) - sequences = convert_polygons_to_sequences(annotation.data["paths"]) - draw_polygon(mask, sequences, 1) - counts = rle_encode(mask) - - x_coords = [s[0::2] for s in sequences] - y_coords = [s[1::2] for s in sequences] - min_x = np.min([np.min(x_coord) for x_coord in x_coords]) - min_y = np.min([np.min(y_coord) for y_coord in y_coords]) - max_x = np.max([np.max(x_coord) for x_coord in x_coords]) - max_y = np.max([np.max(y_coord) for y_coord in y_coords]) - w = max_x - min_x + 1 - h = max_y - min_y + 1 - - return { - "id": annotation_id, - "image_id": _build_image_id(annotation_file), - "category_id": categories[annotation.annotation_class.name], - "segmentation": { - "counts": counts, - "size": [annotation_file.image_height, annotation_file.image_width], - }, - "area": np.sum(mask), - "bbox": [min_x, min_y, w, h], - "iscrowd": 1, - "extra": build_extra(annotation), - } - elif annotation_type == "tag": - pass - elif annotation_type == "bounding_box": - x = annotation.data["x"] - y = annotation.data["y"] - w = annotation.data["w"] - h = annotation.data["h"] - - return build_annotation( - annotation_file, - annotation_id, - dt.make_polygon( - annotation.annotation_class.name, - [ - {"x": x, "y": y}, - {"x": x + w, "y": y}, - {"x": x + w, "y": y + h}, - {"x": x, "y": y + h}, - ], - None, - annotation.subs, - ), - categories, - ) - else: - print(f"skipping unsupported annotation_type '{annotation_type}'") - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_extra(annotation: dt.Annotation) -> Dict[str, Any]: - data = {} - instance_id_sub = annotation.get_sub("instance_id") - attributes_sub = annotation.get_sub("attributes") - text_sub = annotation.get_sub("text") - - if instance_id_sub: - data["instance_id"] = instance_id_sub.data - if attributes_sub: - data["attributes"] = attributes_sub.data - if text_sub: - data["text"] = text_sub.data - return data - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_categories(categories: Dict[str, int]) -> Iterator[Dict[str, Any]]: - for name, id in categories.items(): - yield {"id": id, "name": name, "supercategory": "root"} - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_tag_categories(categories: Dict[str, int]) -> Iterator[Dict[str, Any]]: - for name, id in categories.items(): - yield {"id": id, "name": name} - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def polygon_area(x: np.ndarray, y: np.ndarray) -> float: - """ - Returns the area of the input polygon, represented with two numpy arrays - for x and y coordinates. - """ - return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) - - def _build_json(annotation_files: List[dt.AnnotationFile]) -> Dict[str, Any]: categories: Dict[str, int] = _calculate_categories(annotation_files) tag_categories: Dict[str, int] = _calculate_tag_categories(annotation_files) @@ -378,7 +63,6 @@ def _calculate_categories(annotation_files: List[dt.AnnotationFile]) -> Dict[str and annotation_class.annotation_type in [ "polygon", - "complex_polygon", "bounding_box", ] ): diff --git a/darwin/exporter/formats/cvat.py b/darwin/exporter/formats/cvat.py index 20d6dcfa5..1aa133964 100644 --- a/darwin/exporter/formats/cvat.py +++ b/darwin/exporter/formats/cvat.py @@ -35,192 +35,6 @@ def export(annotation_files: Iterator[dt.AnnotationFile], output_dir: Path) -> N f.write(tostring(output)) -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def add_subelement_text(parent: Element, name: str, value: Any) -> Element: - sub = SubElement(parent, name) - sub.text = str(value) - return sub - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_xml(annotation_files: List[dt.AnnotationFile]) -> Element: - label_lookup: Dict[str, int] = build_label_lookup(annotation_files) - root: Element = Element("annotations") - add_subelement_text(root, "version", "1.1") - build_meta(root, annotation_files, label_lookup) - build_images(root, annotation_files, label_lookup) - return root - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_images( - root: Element, - annotation_files: List[dt.AnnotationFile], - label_lookup: Dict[str, int], -) -> None: - for id, annotation_file in enumerate(annotation_files, 1): - image = SubElement(root, "image") - image.attrib["id"] = str(id) - image.attrib["name"] = annotation_file.filename - image.attrib["width"] = str(annotation_file.image_width) - image.attrib["height"] = str(annotation_file.image_height) - - for annotation in annotation_file.annotations: - build_annotation(image, annotation) - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_annotation(image: Element, annotation: dt.Annotation) -> None: - if annotation.annotation_class.annotation_type == "bounding_box": - box = SubElement(image, "box") - box.attrib["label"] = annotation.annotation_class.name - box.attrib["xtl"] = str(annotation.data["x"]) - box.attrib["ytl"] = str(annotation.data["y"]) - box.attrib["xbr"] = str(annotation.data["x"] + annotation.data["w"]) - box.attrib["ybr"] = str(annotation.data["y"] + annotation.data["h"]) - box.attrib["occluded"] = "0" - build_attributes(box, annotation) - else: - print(f"[warning] skipping {annotation.annotation_class.annotation_type}") - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_attributes(box: Element, annotation: dt.Annotation) -> None: - annotation_text: Optional[dt.SubAnnotation] = annotation.get_sub("text") - if annotation_text: - attribute = add_subelement_text(box, "attribute", annotation_text.data) - attribute.attrib["name"] = "__text" - - annotation_instance_id: Optional[dt.SubAnnotation] = annotation.get_sub( - "instance_id" - ) - if annotation_instance_id: - attribute = add_subelement_text( - box, "attribute", str(annotation_instance_id.data) - ) - attribute.attrib["name"] = "__instance_id" - - annotation_attributes: Optional[dt.SubAnnotation] = annotation.get_sub("attributes") - if annotation_attributes: - for attrib in annotation_attributes.data: - attribute = add_subelement_text(box, "attribute", "") - attribute.attrib["name"] = attrib - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_meta( - root: Element, - annotation_files: List[dt.AnnotationFile], - label_lookup: Dict[str, int], -) -> None: - meta: Element = SubElement(root, "meta") - add_subelement_text( - meta, "dumped", str(datetime.datetime.now(tz=datetime.timezone.utc)) - ) - - task: Element = SubElement(meta, "task") - add_subelement_text(task, "id", 1) - add_subelement_text(task, "name", "exported_task_from_darwin") - add_subelement_text(task, "size", len(annotation_files)) - add_subelement_text(task, "mode", "annotation") - add_subelement_text(task, "overlapp", 0) - add_subelement_text(task, "bugtracker", None) - add_subelement_text(task, "flipped", False) - add_subelement_text( - task, "created", str(datetime.datetime.now(tz=datetime.timezone.utc)) - ) - add_subelement_text( - task, "updated", str(datetime.datetime.now(tz=datetime.timezone.utc)) - ) - - labels: Element = SubElement(task, "labels") - build_labels(labels, label_lookup) - - segments: Element = SubElement(task, "segments") - build_segments(segments, annotation_files) - - owner: Element = SubElement(task, "owner") - add_subelement_text(owner, "username", "example_username") - add_subelement_text(owner, "email", "user@example.com") - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_segments( - segments: Element, annotation_files: List[dt.AnnotationFile] -) -> None: - segment: Element = SubElement(segments, "segment") - add_subelement_text(segment, "id", 1) - add_subelement_text(segment, "start", 1) - add_subelement_text(segment, "end", len(annotation_files)) - add_subelement_text(segment, "url", "not applicable") - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_labels(labels: Element, label_lookup: Dict[str, int]) -> None: - for key in label_lookup.keys(): - label: Element = SubElement(labels, "label") - add_subelement_text(label, "name", key) - SubElement(label, "attributes") - - -@deprecation.deprecated( - deprecated_in="0.7.7", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_label_lookup(annotation_files: List[dt.AnnotationFile]) -> Dict[str, int]: - labels: Dict[str, int] = {} - for annotation_file in annotation_files: - for annotation_class in annotation_file.annotation_classes: - if ( - annotation_class.name not in labels - and annotation_class.annotation_type == "bounding_box" - ): - labels[annotation_class.name] = len(labels) - return labels - - def _add_subelement_text(parent: Element, name: str, value: Any) -> Element: sub = SubElement(parent, name) sub.text = str(value) diff --git a/darwin/exporter/formats/darwin.py b/darwin/exporter/formats/darwin.py index 430f9ca67..fd436057a 100644 --- a/darwin/exporter/formats/darwin.py +++ b/darwin/exporter/formats/darwin.py @@ -58,10 +58,7 @@ def _build_v2_annotation_data(annotation: dt.Annotation) -> Dict[str, Any]: annotation_data["bounding_box"] = _build_bounding_box_data(annotation.data) elif annotation.annotation_class.annotation_type == "tag": annotation_data["tag"] = {} - elif ( - annotation.annotation_class.annotation_type == "polygon" - or annotation.annotation_class.annotation_type == "complex_polygon" - ): + elif annotation.annotation_class.annotation_type == "polygon": polygon_data = _build_polygon_data(annotation.data) annotation_data["polygon"] = polygon_data annotation_data["bounding_box"] = _build_bounding_box_data(annotation.data) @@ -167,33 +164,3 @@ def _build_slots_data(slots: List[dt.Slot]) -> List[Dict[str, Any]]: slots_data.append(slot_data) return slots_data - - -@deprecation.deprecated( - deprecated_in="0.7.8", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_annotation_data(annotation: dt.Annotation) -> Dict[str, Any]: - if annotation.annotation_class.annotation_type == "complex_polygon": - return {"path": annotation.data["paths"]} - - if annotation.annotation_class.annotation_type == "polygon": - return dict( - filter(lambda item: item[0] != "bounding_box", annotation.data.items()) - ) - - return dict(annotation.data) - - -def _build_annotation_data(annotation: dt.Annotation) -> Dict[str, Any]: - if annotation.annotation_class.annotation_type == "complex_polygon": - return {"path": annotation.data["paths"]} - - if annotation.annotation_class.annotation_type == "polygon": - return dict( - filter(lambda item: item[0] != "bounding_box", annotation.data.items()) - ) - - return dict(annotation.data) diff --git a/darwin/exporter/formats/instance_mask.py b/darwin/exporter/formats/instance_mask.py index 2991faaa9..e7a31b0d4 100644 --- a/darwin/exporter/formats/instance_mask.py +++ b/darwin/exporter/formats/instance_mask.py @@ -40,8 +40,6 @@ def export(annotation_files: Iterable[dt.AnnotationFile], output_dir: Path) -> N for i, annotation in enumerate(annotations): cat = annotation.annotation_class.name if annotation.annotation_class.annotation_type == "polygon": - polygon = annotation.data["path"] - elif annotation.annotation_class.annotation_type == "complex_polygon": polygon = annotation.data["paths"] else: continue diff --git a/darwin/exporter/formats/mask.py b/darwin/exporter/formats/mask.py index 0ae38e2c2..a1d65e26c 100644 --- a/darwin/exporter/formats/mask.py +++ b/darwin/exporter/formats/mask.py @@ -138,7 +138,7 @@ def get_render_mode(annotations: List[dt.AnnotationLike]) -> dt.MaskTypes.TypeOf types: Set[str] = set(list_of_types) is_raster_mask = ("mask" in types) and ("raster_layer" in types) - is_polygon = ("polygon" in types) or ("complex_polygon" in types) + is_polygon = "polygon" in types raster_layer_count = len([a for a in types if a == "raster_layer"]) @@ -294,8 +294,6 @@ def render_polygons( categories.append(cat) if a.annotation_class.annotation_type == "polygon": - polygon = a.data["path"] - elif a.annotation_class.annotation_type == "complex_polygon": polygon = a.data["paths"] else: raise ValueError( @@ -437,7 +435,7 @@ def export( masks_dir: Path = output_dir / "masks" masks_dir.mkdir(exist_ok=True, parents=True) annotation_files = list(annotation_files) - accepted_types = ["polygon", "complex_polygon", "raster_layer", "mask"] + accepted_types = ["polygon", "raster_layer", "mask"] all_classes_sets: List[Set[dt.AnnotationClass]] = [ a.annotation_classes for a in annotation_files ] @@ -589,13 +587,10 @@ def offset_polygon(polygon: List, offset_x: int, offset_y: int) -> List: Returns: List: polygon with offset applied """ - if isinstance(polygon[0], list): - return offset_complex_polygon(polygon, offset_x, offset_y) - else: - return offset_simple_polygon(polygon, offset_x, offset_y) + return offset_polygon_paths(polygon, offset_x, offset_y) -def offset_complex_polygon(polygons: List, offset_x: int, offset_y: int) -> List: +def offset_polygon_paths(polygons: List, offset_x: int, offset_y: int) -> List: new_polygons = [] for polygon in polygons: new_polygons.append(offset_simple_polygon(polygon, offset_x, offset_y)) diff --git a/darwin/exporter/formats/pascalvoc.py b/darwin/exporter/formats/pascalvoc.py index aa7acd08b..c3f681081 100644 --- a/darwin/exporter/formats/pascalvoc.py +++ b/darwin/exporter/formats/pascalvoc.py @@ -40,150 +40,6 @@ def export(annotation_files: Iterable[dt.AnnotationFile], output_dir: Path) -> N _export_file(annotation_file, output_dir) -@deprecation.deprecated( - deprecated_in="0.7.10", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def export_file(annotation_file: dt.AnnotationFile, output_dir: Path) -> None: - xml = build_xml(annotation_file) - output_file_path = (output_dir / annotation_file.filename).with_suffix(".xml") - output_file_path.parent.mkdir(parents=True, exist_ok=True) - with open(output_file_path, "wb") as f: - f.write(tostring(xml)) - - -@deprecation.deprecated( - deprecated_in="0.7.10", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_xml(annotation_file: dt.AnnotationFile) -> Element: - root: Element = Element("annotation") - add_subelement_text(root, "folder", "images") - add_subelement_text(root, "filename", annotation_file.filename) - add_subelement_text(root, "path", f"images/{annotation_file.filename}") - - source = SubElement(root, "source") - add_subelement_text(source, "database", "darwin") - - size = SubElement(root, "size") - add_subelement_text(size, "width", str(annotation_file.image_width)) - add_subelement_text(size, "height", str(annotation_file.image_height)) - add_subelement_text(size, "depth", "3") - - add_subelement_text(root, "segmented", "0") - - for annotation in annotation_file.annotations: - annotation_type = annotation.annotation_class.annotation_type - if annotation_type not in ["bounding_box", "polygon", "complex_polygon"]: - continue - - data = annotation.data - sub_annotation = SubElement(root, "object") - add_subelement_text(sub_annotation, "name", annotation.annotation_class.name) - add_subelement_text(sub_annotation, "pose", "Unspecified") - add_subelement_text(sub_annotation, "truncated", "0") - add_subelement_text(sub_annotation, "difficult", "0") - bndbox = SubElement(sub_annotation, "bndbox") - - if annotation_type == "polygon" or annotation_type == "complex_polygon": - data = data.get("bounding_box") - - xmin = data.get("x") - ymin = data.get("y") - xmax = xmin + data.get("w") - ymax = ymin + data.get("h") - add_subelement_text(bndbox, "xmin", str(round(xmin))) - add_subelement_text(bndbox, "ymin", str(round(ymin))) - add_subelement_text(bndbox, "xmax", str(round(xmax))) - add_subelement_text(bndbox, "ymax", str(round(ymax))) - - return root - - -@deprecation.deprecated( - deprecated_in="0.7.10", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def add_subelement_text(parent: Element, name: str, value: Any) -> Element: - sub: Element = SubElement(parent, name) - sub.text = value - return sub - - -@deprecation.deprecated( - deprecated_in="0.7.10", - removed_in="0.8.0", - current_version=__version__, - details=REMOVAL_MESSAGE, -) -def convert_file(path: Path) -> Element: - data = attempt_decode(path) - return build_voc(data["image"], data["annotations"]) - - -@deprecation.deprecated( - deprecated_in="0.7.10", - removed_in="0.8.0", - current_version=__version__, - details=REMOVAL_MESSAGE, -) -def save_xml(xml: Element, path: Path) -> None: - with open(path, "wb") as f: - f.write(tostring(xml)) - - -@deprecation.deprecated( - deprecated_in="0.7.10", - removed_in="0.8.0", - current_version=__version__, - details=REMOVAL_MESSAGE, -) -def build_voc( - metadata: Dict[str, Any], annotations: Iterable[Dict[str, Any]] -) -> Element: - print(metadata) - root: Element = Element("annotation") - add_subelement_text(root, "folder", "images") - add_subelement_text(root, "filename", metadata["original_filename"]) - add_subelement_text(root, "path", f"images/{metadata['original_filename']}") - - source: Element = SubElement(root, "source") - add_subelement_text(source, "database", "darwin") - - size: Element = SubElement(root, "size") - add_subelement_text(size, "width", str(metadata["width"])) - add_subelement_text(size, "height", str(metadata["height"])) - add_subelement_text(size, "depth", "3") - - add_subelement_text(root, "segmented", "0") - - for annotation in annotations: - if "bounding_box" not in annotation: - continue - data = annotation["bounding_box"] - sub_annotation = SubElement(root, "object") - add_subelement_text(sub_annotation, "name", annotation["name"]) - add_subelement_text(sub_annotation, "pose", "Unspecified") - add_subelement_text(sub_annotation, "truncated", "0") - add_subelement_text(sub_annotation, "difficult", "0") - bndbox = SubElement(sub_annotation, "bndbox") - add_subelement_text(bndbox, "xmin", str(round(data["x"]))) - add_subelement_text(bndbox, "ymin", str(round(data["y"]))) - add_subelement_text(bndbox, "xmax", str(round(data["x"] + data["w"]))) - add_subelement_text(bndbox, "ymax", str(round(data["y"] + data["h"]))) - - return root - - -###################################### - - def _export_file(annotation_file: dt.AnnotationFile, output_dir: Path) -> None: xml = _build_xml(annotation_file) output_file_path = (output_dir / annotation_file.filename).with_suffix(".xml") diff --git a/darwin/importer/formats/coco.py b/darwin/importer/formats/coco.py index 6e0c876a5..6b500dd6b 100644 --- a/darwin/importer/formats/coco.py +++ b/darwin/importer/formats/coco.py @@ -196,12 +196,6 @@ def _decode_file(current_encoding: str, path: Path): return list(parse_json(path, data)) -@deprecation.deprecated( - deprecated_in="0.7.12", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) def decode_binary_rle(data: str) -> List[int]: """ Decodes binary rle to integer list rle. diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 5a27d02b4..add5dd4cb 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -71,12 +71,6 @@ """ -@deprecation.deprecated( # type:ignore - deprecated_in="0.7.12", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) def build_main_annotations_lookup_table( annotation_classes: List[Dict[str, Unknown]] ) -> Dict[str, Unknown]: @@ -107,12 +101,6 @@ def build_main_annotations_lookup_table( return lookup -@deprecation.deprecated( # type:ignore - deprecated_in="0.7.12", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) def find_and_parse( # noqa: C901 importer: Callable[[Path], Union[List[dt.AnnotationFile], dt.AnnotationFile, None]], file_paths: List[PathLike], @@ -181,12 +169,6 @@ def _get_files_for_parsing(file_paths: List[PathLike]) -> List[Path]: return [file for files in packed_files for file in files] -@deprecation.deprecated( # type:ignore - deprecated_in="0.7.12", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) def build_attribute_lookup(dataset: "RemoteDataset") -> Dict[str, Unknown]: attributes: List[Dict[str, Unknown]] = dataset.fetch_remote_attributes() lookup: Dict[str, Unknown] = {} @@ -198,12 +180,6 @@ def build_attribute_lookup(dataset: "RemoteDataset") -> Dict[str, Unknown]: return lookup -@deprecation.deprecated( # type:ignore - deprecated_in="0.7.12", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) def get_remote_files( dataset: "RemoteDataset", filenames: List[str], chunk_size: int = 100 ) -> Dict[str, Tuple[int, str]]: @@ -339,9 +315,9 @@ def _import_properties( # get team properties -> List[FullProperty] team_properties = client.get_team_properties() # (property-name, annotation_class_id): FullProperty object - team_properties_annotation_lookup: Dict[ - Tuple[str, Optional[int]], FullProperty - ] = {} + team_properties_annotation_lookup: Dict[Tuple[str, Optional[int]], FullProperty] = ( + {} + ) for prop in team_properties: team_properties_annotation_lookup[(prop.name, prop.annotation_class_id)] = prop @@ -828,9 +804,9 @@ def import_annotations( # noqa: C901 # Need to re parse the files since we didn't save the annotations in memory for local_path in set(local_file.path for local_file in local_files): # noqa: C401 - imported_files: Union[ - List[dt.AnnotationFile], dt.AnnotationFile, None - ] = importer(local_path) + imported_files: Union[List[dt.AnnotationFile], dt.AnnotationFile, None] = ( + importer(local_path) + ) if imported_files is None: parsed_files = [] elif not isinstance(imported_files, List): @@ -1230,9 +1206,9 @@ def _import_annotations( # Insert the default slot name if not available in the import source annotation = _handle_slot_names(annotation, dataset.version, default_slot_name) - annotation_class_ids_map[ - (annotation_class.name, annotation_type) - ] = annotation_class_id + annotation_class_ids_map[(annotation_class.name, annotation_type)] = ( + annotation_class_id + ) serial_obj = { "annotation_class_id": annotation_class_id, "data": data, diff --git a/darwin/item.py b/darwin/item.py index 2b554e725..3ae8e7243 100644 --- a/darwin/item.py +++ b/darwin/item.py @@ -114,39 +114,3 @@ def parse(cls, raw: Dict[str, Any], dataset_slug: str = "n/a") -> "DatasetItem": "slots": [], } return DatasetItem(**data) - - -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="Use the ``DatasetItem.parse`` instead.", -) -def parse_dataset_item(raw: Dict[str, Any]) -> DatasetItem: - """ - Parses the given dictionary into a ``DatasetItem``. Performs no validations. - - Parameters - ---------- - raw : Dict[str, Any] - The dictionary to parse. - - Returns - ------- - DatasetItem - A dataset item with the parsed information. - """ - return DatasetItem( - id=raw["id"], - filename=raw["filename"], - status=raw["status"], - archived=raw["archived"], - filesize=raw["file_size"], - dataset_id=raw["dataset_id"], - dataset_slug="n/a", - seq=raw["seq"], - current_workflow_id=raw.get("current_workflow_id"), - path=raw["path"], - slots=[], - current_workflow=raw.get("current_workflow"), - ) diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index 70329c91d..3501fea00 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -91,26 +91,6 @@ def is_extension_allowed_by_filename(filename: str) -> bool: return any(filename.lower().endswith(ext) for ext in SUPPORTED_EXTENSIONS) -@deprecation.deprecated(deprecated_in="0.8.4", current_version=__version__) -def is_extension_allowed(extension: str) -> bool: - """ - Returns whether or not the given extension is allowed. - @Deprecated. Use is_extension_allowed_by_filename instead, and pass full filename. - This is due to the fact that some extensions now include multiple dots, e.g. .nii.gz - - Parameters - ---------- - extension : str - The extension. - - Returns - ------- - bool - Whether or not the given extension is allowed. - """ - return extension.lower() in SUPPORTED_EXTENSIONS - - def is_image_extension_allowed_by_filename(filename: str) -> bool: """ Returns whether or not the given image extension is allowed. @@ -146,41 +126,6 @@ def is_image_extension_allowed(extension: str) -> bool: return extension.lower() in SUPPORTED_IMAGE_EXTENSIONS -def is_video_extension_allowed_by_filename(extension: str) -> bool: - """ - Returns whether or not the given image extension is allowed. - - Parameters - ---------- - extension : str - The image extension. - - Returns - ------- - bool - Whether or not the given extension is allowed. - """ - return any(extension.lower().endswith(ext) for ext in SUPPORTED_VIDEO_EXTENSIONS) - - -@deprecation.deprecated(deprecated_in="0.8.4", current_version=__version__) -def is_video_extension_allowed(extension: str) -> bool: - """ - Returns whether or not the given video extension is allowed. - - Parameters - ---------- - extension : str - The video extension. - - Returns - ------- - bool - Whether or not the given extension is allowed. - """ - return extension.lower() in SUPPORTED_VIDEO_EXTENSIONS - - def urljoin(*parts: str) -> str: """ Take as input an unpacked list of strings and joins them to form an URL. @@ -1345,127 +1290,6 @@ def convert_polygons_to_sequences( return sequences -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="Do not use.", -) -def convert_sequences_to_polygons( - sequences: List[Union[List[int], List[float]]], - height: Optional[int] = None, - width: Optional[int] = None, -) -> Dict[str, List[dt.Polygon]]: - """ - Converts a list of polygons, encoded as a list of dictionaries of into a list of nd.arrays - of coordinates. - - Parameters - ---------- - sequences : List[Union[List[int], List[float]]] - List of arrays of coordinates in the format ``[x1, y1, x2, y2, ..., xn, yn]`` or as a list - of them as ``[[x1, y1, x2, y2, ..., xn, yn], ..., [x1, y1, x2, y2, ..., xn, yn]]``. - height : Optional[int], default: None - Maximum height for a polygon coordinate. - width : Optional[int], default: None - Maximum width for a polygon coordinate. - - Returns - ------- - Dict[str, List[dt.Polygon]] - Dictionary with the key ``path`` containing a list of coordinates in the format of - ``[[{x: x1, y:y1}, ..., {x: xn, y:yn}], ..., [{x: x1, y:y1}, ..., {x: xn, y:yn}]]``. - - Raises - ------ - ValueError - If sequences is a falsy value (such as ``[]``) or if it is in an incorrect format. - """ - if not sequences: - raise ValueError("No sequences provided") - # If there is a single sequences composing the instance then this is - # transformed to polygons = [[x1, y1, ..., xn, yn]] - if not isinstance(sequences[0], list): - sequences = [sequences] - - if not isinstance(sequences[0][0], (int, float)): - raise ValueError("Unknown input format") - - def grouped(iterable, n): - return zip(*[iter(iterable)] * n) - - polygons = [] - for sequence in sequences: - path = [] - for x, y in grouped(sequence, 2): - # Clip coordinates to the image size - x = max(min(x, width - 1) if width else x, 0) - y = max(min(y, height - 1) if height else y, 0) - path.append({"x": x, "y": y}) - polygons.append(path) - return {"path": polygons} - - -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="Do not use.", -) -def convert_xyxy_to_bounding_box(box: List[Union[int, float]]) -> dt.BoundingBox: - """ - Converts a list of xy coordinates representing a bounding box into a dictionary. - - Parameters - ---------- - box : List[Union[int, float]] - List of arrays of coordinates in the format [x1, y1, x2, y2] - - Returns - ------- - BoundingBox - Bounding box in the format ``{x: x1, y: y1, h: height, w: width}``. - - Raises - ------ - ValueError - If ``box`` has an incorrect format. - """ - if not isinstance(box[0], float) and not isinstance(box[0], int): - raise ValueError("Unknown input format") - - x1, y1, x2, y2 = box - width = x2 - x1 - height = y2 - y1 - return {"x": x1, "y": y1, "w": width, "h": height} - - -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="Do not use.", -) -def convert_bounding_box_to_xyxy(box: dt.BoundingBox) -> List[float]: - """ - Converts dictionary representing a bounding box into a list of xy coordinates. - - Parameters - ---------- - box : BoundingBox - Bounding box in the format ``{x: x1, y: y1, h: height, w: width}``. - - Returns - ------- - List[float] - List of arrays of coordinates in the format ``[x1, y1, x2, y2]``. - """ - - x2 = box["x"] + box["width"] - y2 = box["y"] + box["height"] - return [box["x"], box["y"], x2, y2] - - def convert_polygons_to_mask( polygons: List, height: int, width: int, value: Optional[int] = 1 ) -> np.ndarray: diff --git a/tests/darwin/utils/find_files_test.py b/tests/darwin/utils/find_files_test.py index 64c4dadce..8e8fbd6ba 100644 --- a/tests/darwin/utils/find_files_test.py +++ b/tests/darwin/utils/find_files_test.py @@ -132,9 +132,8 @@ def dependency_factory(self) -> Dependencies: """ from darwin.utils import is_extension_allowed_by_filename as ieabf from darwin.utils import is_image_extension_allowed_by_filename as iieabf - from darwin.utils import is_video_extension_allowed_by_filename as iveabf - return self.Dependencies(ieabf=ieabf, iveabf=iveabf, iieabf=iieabf) + return self.Dependencies(ieabf=ieabf, iieabf=iieabf) def test_ieabf_returns_true_for_a_valid_extension(self): valid_extensions = [ @@ -152,21 +151,6 @@ def test_ieabf_returns_false_for_an_invalid_extension(self): self.assertFalse(all(results)) - def test_iveabf_returns_true_for_a_valid_extension(self): - results = [ - self.dependency_factory().iveabf(file) - for file in SUPPORTED_VIDEO_EXTENSIONS - ] - - self.assertTrue(all(results)) - - def test_iveabf_returns_false_for_an_invalid_extension(self): - results = [ - self.dependency_factory().iveabf(file) for file in self.fake_invalid_files - ] - - self.assertFalse(all(results)) - def test_iieabf_returns_true_for_a_valid_extension(self): results = [ self.dependency_factory().iieabf(file) From ac8fd93ed093e3f0d76f987a3bb47b13d7e351c1 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 22 Feb 2024 12:07:55 +0000 Subject: [PATCH 10/28] Fixed teststhat broke when removing deprecated functions --- darwin/dataset/download_manager.py | 63 ---------------------- darwin/dataset/utils.py | 18 ++++--- darwin/exporter/formats/dataloop.py | 81 ----------------------------- darwin/utils/utils.py | 1 - tests/darwin/utils_test.py | 17 +----- 5 files changed, 12 insertions(+), 168 deletions(-) diff --git a/darwin/dataset/download_manager.py b/darwin/dataset/download_manager.py index c54e9b126..03c16a413 100644 --- a/darwin/dataset/download_manager.py +++ b/darwin/dataset/download_manager.py @@ -31,12 +31,6 @@ from darwin.version import __version__ -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="The api_url parameter will be removed.", -) def download_all_images_from_annotations( api_key: str, api_url: str, @@ -152,12 +146,6 @@ def download_all_images_from_annotations( return lambda: download_functions, len(download_functions) -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="The api_url parameter will be removed.", -) def download_image_from_annotation( api_key: str, api_url: str, @@ -454,12 +442,6 @@ def _update_local_path(annotation: AnnotationFile, url, local_path): file.write(op) -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="Use the ``download_image_from_annotation`` instead.", -) def download_image_from_json_annotation( api_key: str, api_url: str, @@ -506,51 +488,6 @@ def download_image_from_json_annotation( _download_image(image_url, image_path, api_key) -@deprecation.deprecated( - deprecated_in="0.7.5", - removed_in="0.8.0", - current_version=__version__, - details="Use the ``download_image_from_annotation`` instead.", -) -def download_image(url: str, path: Path, api_key: str) -> None: - """ - Helper function: downloads one image from url. - - Parameters - ---------- - url : str - Url of the image to download - path : Path - Path where to download the image, with filename - api_key : str - API Key of the current team - """ - if path.exists(): - return - TIMEOUT: int = 60 - start: float = time.time() - while True: - if "token" in url: - response: requests.Response = requests.get(url, stream=True) - else: - response = requests.get( - url, headers={"Authorization": f"ApiKey {api_key}"}, stream=True - ) - # Correct status: download image - if response.ok: - with open(str(path), "wb") as file: - for chunk in response: - file.write(chunk) - return - # Fatal-error status: fail - if 400 <= response.status_code <= 499: - raise Exception(response.status_code, response.json()) - # Timeout - if time.time() - start > TIMEOUT: - raise Exception(f"Timeout url request ({url}) after {TIMEOUT} seconds.") - time.sleep(1) - - def _download_image( url: str, path: Path, api_key: str, slot: Optional[dt.Slot] = None ) -> None: diff --git a/darwin/dataset/utils.py b/darwin/dataset/utils.py index 7e9ecfee9..c1b04b147 100644 --- a/darwin/dataset/utils.py +++ b/darwin/dataset/utils.py @@ -366,9 +366,11 @@ def create_polygon_object(obj, box_mode, classes=None): "segmentation": segmentation, "bbox": [np.min(all_px), np.min(all_py), np.max(all_px), np.max(all_py)], "bbox_mode": box_mode, - "category_id": classes.index(obj.annotation_class.name) - if classes - else obj.annotation_class.name, + "category_id": ( + classes.index(obj.annotation_class.name) + if classes + else obj.annotation_class.name + ), "iscrowd": 0, } @@ -380,9 +382,11 @@ def create_bbox_object(obj, box_mode, classes=None): new_obj = { "bbox": [bbox["x"], bbox["y"], bbox["x"] + bbox["w"], bbox["y"] + bbox["h"]], "bbox_mode": box_mode, - "category_id": classes.index(obj.annotation_class.name) - if classes - else obj.annotation_class.name, + "category_id": ( + classes.index(obj.annotation_class.name) + if classes + else obj.annotation_class.name + ), "iscrowd": 0, } @@ -698,7 +702,7 @@ def convert_to_rgb(pic: PILImage.Image) -> PILImage.Image: def compute_max_density(annotations_dir: Path) -> int: """ Calculates the maximum density of all of the annotations in the given folder. - Density is calculated as the number of polygons / complex_polygons present in an annotation + Density is calculated as the number of polygons present in an annotation file. Parameters diff --git a/darwin/exporter/formats/dataloop.py b/darwin/exporter/formats/dataloop.py index 96fc47bdd..89ba76937 100644 --- a/darwin/exporter/formats/dataloop.py +++ b/darwin/exporter/formats/dataloop.py @@ -31,87 +31,6 @@ def export(annotation_files: Iterable[dt.AnnotationFile], output_dir: Path) -> N _export_file(annotation_file, id, output_dir) -@deprecation.deprecated( - deprecated_in="0.7.8", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def export_file(annotation_file: dt.AnnotationFile, id: int, output_dir: Path) -> None: - output: Dict[str, Any] = _build_json(annotation_file, id) - output_file_path: Path = (output_dir / annotation_file.filename).with_suffix( - ".json" - ) - with open(output_file_path, "w") as f: - op = json.dumps( - output, option=json.OPT_INDENT_2 | json.OPT_SERIALIZE_NUMPY - ).decode("utf-8") - f.write(op) - - -@deprecation.deprecated( - deprecated_in="0.7.8", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_json(annotation_file: dt.AnnotationFile, id: int) -> Dict[str, Any]: - return { - "_id": id, - "filename": annotation_file.filename, - "itemMetadata": [], - "annotations": _build_annotations(annotation_file, id), - } - - -@deprecation.deprecated( - deprecated_in="0.7.8", - removed_in="0.8.0", - current_version=__version__, - details=DEPRECATION_MESSAGE, -) -def build_annotations( - annotation_file: dt.AnnotationFile, id: int -) -> Iterable[Dict[str, Any]]: - output = [] - for annotation_id, annotation in enumerate(annotation_file.annotations): - print(annotation) - if annotation.annotation_class.annotation_type == "bounding_box": - entry = { - "id": annotation_id, - "datasetId": "darwin", - "type": "box", - "label": annotation.annotation_class.name, - "attributes": [], - "coordinates": [ - {"x": annotation.data["x"], "y": annotation.data["y"], "z": 0}, - { - "x": annotation.data["x"] + annotation.data["w"], - "y": annotation.data["y"] + annotation.data["h"], - "z": 0, - }, - ], - "metadata": {}, - } - output.append(entry) - elif annotation.annotation_class.annotation_type == "polygon": - entry = { - "id": annotation_id, - "datasetId": "darwin", - "type": "segment", - "label": annotation.annotation_class.name, - "attributes": [], - "coordinates": [ - {"x": point["x"], "y": point["y"], "z": 0} - for point in annotation.data["path"] - ], - "metadata": {}, - } - output.append(entry) - - return output - - def _export_file(annotation_file: dt.AnnotationFile, id: int, output_dir: Path) -> None: output: Dict[str, Any] = _build_json(annotation_file, id) output_file_path: Path = (output_dir / annotation_file.filename).with_suffix( diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index 3501fea00..2cc27600a 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -108,7 +108,6 @@ def is_image_extension_allowed_by_filename(filename: str) -> bool: return any(filename.lower().endswith(ext) for ext in SUPPORTED_IMAGE_EXTENSIONS) -@deprecation.deprecated(deprecated_in="0.8.4", current_version=__version__) def is_image_extension_allowed(extension: str) -> bool: """ Returns whether or not the given image extension is allowed. diff --git a/tests/darwin/utils_test.py b/tests/darwin/utils_test.py index 7e1c52b8b..d06f73f2e 100644 --- a/tests/darwin/utils_test.py +++ b/tests/darwin/utils_test.py @@ -8,11 +8,9 @@ from darwin.utils import ( get_response_content, has_json_content_type, - is_extension_allowed, is_image_extension_allowed, is_project_dir, is_unix_like_os, - is_video_extension_allowed, parse_darwin_json, urljoin, validate_data_against_schema, @@ -46,24 +44,12 @@ def test_validates_correct_data(self): class TestExtensions: - def test_returns_true_for_allowed_extensions(self): - assert is_extension_allowed(".png") - - def test_returns_false_for_unknown_extensions(self): - assert not is_extension_allowed(".mkv") - def test_returns_true_for_allowed_image_extensions(self): assert is_image_extension_allowed(".png") def test_returns_false_for_unknown_image_extensions(self): assert not is_image_extension_allowed(".not_an_image") - def test_returns_true_for_allowed_video_extensions(self): - assert is_video_extension_allowed(".mp4") - - def test_returns_false_for_unknown_video_extensions(self): - assert not is_video_extension_allowed(".not_video") - class TestUrlJoin: def test_returns_an_url(self): @@ -670,8 +656,7 @@ def test_imports_a_skeleton(self, tmp_path): annotation_file: dt.AnnotationFile = parse_darwin_json(import_file, None) assert ( - annotation_file.annotations[0].annotation_class.annotation_type - == "polygon" + annotation_file.annotations[0].annotation_class.annotation_type == "polygon" ) assert ( annotation_file.annotations[1].annotation_class.annotation_type From 7b0a55d6e6abc5b7beba3d89dff8ccb36844935a Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Sat, 24 Feb 2024 16:10:10 +0000 Subject: [PATCH 11/28] WIP --- tests/darwin/dataset/dataset_utils_test.py | 12 +++--- tests/darwin/datatypes_test.py | 20 ++++----- .../exporter/formats/export_mask_test.py | 40 +++++++++--------- .../importer/formats/import_labelbox_test.py | 10 +++-- .../formats/import_superannotate_test.py | 14 +++--- tests/data.zip | Bin 239047 -> 240572 bytes 6 files changed, 51 insertions(+), 45 deletions(-) diff --git a/tests/darwin/dataset/dataset_utils_test.py b/tests/darwin/dataset/dataset_utils_test.py index 893224829..96943e73e 100644 --- a/tests/darwin/dataset/dataset_utils_test.py +++ b/tests/darwin/dataset/dataset_utils_test.py @@ -26,12 +26,12 @@ def open_resource_file(): def parsed_annotation_file(): return { "annotations": [ - {"name": "class_1", "polygon": {"path": []}}, - {"name": "class_1", "polygon": {"path": []}}, - {"name": "class_2", "polygon": {"path": []}}, - {"name": "class_2", "polygon": {"path": []}}, - {"name": "class_2", "polygon": {"path": []}}, - {"name": "class_3", "polygon": {"path": []}}, + {"name": "class_1", "polygon": {"paths": [{"x": 0, "y": 0}]}}, + {"name": "class_1", "polygon": {"paths": [{"x": 0, "y": 0}]}}, + {"name": "class_2", "polygon": {"paths": [{"x": 0, "y": 0}]}}, + {"name": "class_2", "polygon": {"paths": [{"x": 0, "y": 0}]}}, + {"name": "class_2", "polygon": {"paths": [{"x": 0, "y": 0}]}}, + {"name": "class_3", "polygon": {"paths": [{"x": 0, "y": 0}]}}, ], "image": { "filename": "test.jpg", diff --git a/tests/darwin/datatypes_test.py b/tests/darwin/datatypes_test.py index 5b5b87143..e3ef0411c 100644 --- a/tests/darwin/datatypes_test.py +++ b/tests/darwin/datatypes_test.py @@ -17,24 +17,24 @@ class TestMakePolygon: def test_it_returns_annotation_with_default_params(self): class_name: str = "class_name" - points: List[Point] = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 1, "y": 2}] + points: List[Point] = [[{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 1, "y": 2}]] annotation = make_polygon(class_name, points) - assert_annotation_class(annotation, class_name, "polygon") + assert_annotation_class(annotation, class_name, "polygon", "polygon") - path = annotation.data.get("path") - assert path == points + paths = annotation.data.get("paths") + assert paths == points def test_it_returns_annotation_with_bounding_box(self): class_name: str = "class_name" - points: List[Point] = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 1, "y": 2}] + points: List[Point] = [[{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 1, "y": 2}]] bbox: Dict[str, float] = {"x": 1, "y": 2, "w": 2, "h": 2} annotation = make_polygon(class_name, points, bbox) - assert_annotation_class(annotation, class_name, "polygon") + assert_annotation_class(annotation, class_name, "polygon", "polygon") - path = annotation.data.get("path") - assert path == points + paths = annotation.data.get("paths") + assert paths == points class_bbox = annotation.data.get("bounding_box") assert class_bbox == bbox @@ -49,7 +49,7 @@ def test_it_returns_annotation_with_default_params(self): ] annotation = make_polygon(class_name, points) - assert_annotation_class(annotation, class_name, "complex_polygon", "polygon") + assert_annotation_class(annotation, class_name, "polygon", "polygon") paths = annotation.data.get("paths") assert paths == points @@ -63,7 +63,7 @@ def test_it_returns_annotation_with_bounding_box(self): bbox: Dict[str, float] = {"x": 1, "y": 2, "w": 2, "h": 2} annotation = make_polygon(class_name, points, bbox) - assert_annotation_class(annotation, class_name, "complex_polygon", "polygon") + assert_annotation_class(annotation, class_name, "polygon", "polygon") paths = annotation.data.get("paths") assert paths == points diff --git a/tests/darwin/exporter/formats/export_mask_test.py b/tests/darwin/exporter/formats/export_mask_test.py index 4e932d2d7..eee0eebe1 100644 --- a/tests/darwin/exporter/formats/export_mask_test.py +++ b/tests/darwin/exporter/formats/export_mask_test.py @@ -174,9 +174,7 @@ def annotations() -> List[dt.Annotation]: ), dt.Annotation(dt.AnnotationClass("class_2", "mask"), data={"sparse_rle": []}), dt.Annotation(dt.AnnotationClass("class_3", "polygon"), data={"path": "data"}), - dt.Annotation( - dt.AnnotationClass("class_4", "complex_polygon"), data={"paths": "data"} - ), + dt.Annotation(dt.AnnotationClass("class_4", "polygon"), data={"paths": "data"}), ] @@ -277,12 +275,14 @@ def test_beyond_polygon_beyond_window() -> None: dt.Annotation( dt.AnnotationClass("cat1", "polygon"), { - "path": [ - {"x": -1, "y": -1}, - {"x": -1, "y": 1}, - {"x": 1, "y": 1}, - {"x": 1, "y": -1}, - {"x": -1, "y": -1}, + "paths": [ + [ + {"x": -1, "y": -1}, + {"x": -1, "y": 1}, + {"x": 1, "y": 1}, + {"x": 1, "y": -1}, + {"x": -1, "y": -1}, + ] ], "bounding_box": {"x": -1, "y": -1, "w": 2, "h": 2}, }, @@ -319,7 +319,7 @@ def test_beyond_complex_polygon() -> None: categories: dt.MaskTypes.CategoryList = ["__background__"] annotations: List[dt.AnnotationLike] = [ dt.Annotation( - dt.AnnotationClass("cat3", "complex_polygon"), + dt.AnnotationClass("cat3", "polygon"), { "paths": [ [ @@ -378,7 +378,7 @@ def test_render_polygons() -> None: dt.Annotation( dt.AnnotationClass("cat1", "polygon"), { - "path": [ + "paths": [ {"x": 10, "y": 10}, {"x": 20, "y": 10}, {"x": 20, "y": 20}, @@ -390,7 +390,7 @@ def test_render_polygons() -> None: dt.Annotation( dt.AnnotationClass("cat2", "polygon"), { - "path": [ + "paths": [ {"x": 30, "y": 30}, {"x": 40, "y": 30}, {"x": 40, "y": 40}, @@ -402,7 +402,7 @@ def test_render_polygons() -> None: dt.Annotation( dt.AnnotationClass("cat1", "polygon"), { - "path": [ + "paths": [ {"x": 50, "y": 50}, {"x": 60, "y": 50}, {"x": 60, "y": 60}, @@ -414,12 +414,12 @@ def test_render_polygons() -> None: dt.Annotation( dt.AnnotationClass("cat1", "polygon"), { - "path": [{"x": 10, "y": 80}, {"x": 20, "y": 80}, {"x": 20, "y": 60}], + "paths": [{"x": 10, "y": 80}, {"x": 20, "y": 80}, {"x": 20, "y": 60}], "bounding_box": base_bb, }, ), dt.Annotation( - dt.AnnotationClass("cat3", "complex_polygon"), + dt.AnnotationClass("cat3", "polygon"), { "paths": [ [ @@ -785,7 +785,7 @@ def test_class_mappings_preserved_on_large_export(tmpdir) -> None: dt.Annotation( dt.AnnotationClass("cat1", "polygon"), { - "path": [ + "paths": [ {"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 1, "y": 1}, @@ -797,7 +797,7 @@ def test_class_mappings_preserved_on_large_export(tmpdir) -> None: dt.Annotation( dt.AnnotationClass("cat2", "polygon"), { - "path": [ + "paths": [ {"x": 2, "y": 2}, {"x": 4, "y": 2}, {"x": 4, "y": 4}, @@ -809,7 +809,7 @@ def test_class_mappings_preserved_on_large_export(tmpdir) -> None: dt.Annotation( dt.AnnotationClass("cat3", "polygon"), { - "path": [ + "paths": [ {"x": 5, "y": 5}, {"x": 8, "y": 5}, {"x": 8, "y": 8}, @@ -821,7 +821,7 @@ def test_class_mappings_preserved_on_large_export(tmpdir) -> None: dt.Annotation( dt.AnnotationClass("cat1", "polygon"), { - "path": [ + "paths": [ {"x": 4, "y": 0}, {"x": 5, "y": 0}, {"x": 5, "y": 1}, @@ -831,7 +831,7 @@ def test_class_mappings_preserved_on_large_export(tmpdir) -> None: }, ), dt.Annotation( - dt.AnnotationClass("cat4", "complex_polygon"), + dt.AnnotationClass("cat4", "polygon"), { "paths": [ [ diff --git a/tests/darwin/importer/formats/import_labelbox_test.py b/tests/darwin/importer/formats/import_labelbox_test.py index 730456050..755c0e93a 100644 --- a/tests/darwin/importer/formats/import_labelbox_test.py +++ b/tests/darwin/importer/formats/import_labelbox_test.py @@ -384,9 +384,11 @@ def test_it_imports_polygon_images(self, file_path: Path): assert_polygon( polygon_annotation, [ - {"x": 3665.814, "y": 351.628}, - {"x": 3762.93, "y": 810.419}, - {"x": 3042.93, "y": 914.233}, + [ + {"x": 3665.814, "y": 351.628}, + {"x": 3762.93, "y": 810.419}, + {"x": 3042.93, "y": 914.233}, + ], ], ) @@ -728,7 +730,7 @@ def assert_bbox(annotation: Annotation, x: float, y: float, h: float, w: float) assert data.get("h") == h -def assert_polygon(annotation: Annotation, points: List[Point]) -> None: +def assert_polygon(annotation: Annotation, points: List[List[Point]]) -> None: actual_points = annotation.data.get("paths") assert actual_points assert actual_points == points diff --git a/tests/darwin/importer/formats/import_superannotate_test.py b/tests/darwin/importer/formats/import_superannotate_test.py index ea7fb8bb9..5af2c5edd 100644 --- a/tests/darwin/importer/formats/import_superannotate_test.py +++ b/tests/darwin/importer/formats/import_superannotate_test.py @@ -471,14 +471,18 @@ def test_imports_polygon_vectors( assert_polygon( polygon_annotation, [ - {"x": 1053, "y": 587.2}, - {"x": 1053.1, "y": 586}, - {"x": 1053.8, "y": 585.4}, + [ + {"x": 1053, "y": 587.2}, + {"x": 1053.1, "y": 586}, + {"x": 1053.8, "y": 585.4}, + ], ], ) annotation_class = polygon_annotation.annotation_class - assert_annotation_class(annotation_class, "Person-polygon", "polygon") + assert_annotation_class( + annotation_class, "Person-polygon", "polygon", "polygon" + ) def test_raises_if_polyline_has_missing_points( self, annotations_file_path: Path, classes_file_path: Path @@ -890,7 +894,7 @@ def assert_bbox(annotation: Annotation, x: float, y: float, h: float, w: float) assert data.get("h") == h -def assert_polygon(annotation: Annotation, points: List[Point]) -> None: +def assert_polygon(annotation: Annotation, points: List[List[Point]]) -> None: actual_points = annotation.data.get("paths") assert actual_points assert actual_points == points diff --git a/tests/data.zip b/tests/data.zip index ce18d5d62af3f14c0cc48df2c00df6fd2221f8cb..a9912c164dc9982a5ff5c85ee517a09899bc644e 100644 GIT binary patch delta 72647 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 delta 72562 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

=ugPxOVf6fs8EL~AsBLR{gF+=E3Qw- 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 From 16a74ebd64521d45cef4cad27801e5da024c1ec7 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Mon, 11 Mar 2024 15:49:49 +0000 Subject: [PATCH 12/28] Updated data.zip with v2 data structures --- tests/data.zip | Bin 240572 -> 240444 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/data.zip b/tests/data.zip index a9912c164dc9982a5ff5c85ee517a09899bc644e..19825eeaae785a10e99cc3b8bbcef3564b8528aa 100644 GIT binary patch delta 75381 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%0DN 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�NhAX1xq*tQDg50B+If&TS&%vX2;BR)5Kf(q_2;r; Sr_YV>SJ;h(g-e47-~Jy9HnFb& delta 74572 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 z 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 From ff67b480b241835d4f913804186189e70b03722e Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 12 Mar 2024 15:01:49 +0000 Subject: [PATCH 13/28] Undo accidental changes --- tests/darwin/datatypes_test.py | 61 ++++++++++++++++++++++++++++++ tests/darwin/objectstore_test.py | 65 -------------------------------- 2 files changed, 61 insertions(+), 65 deletions(-) delete mode 100644 tests/darwin/objectstore_test.py diff --git a/tests/darwin/datatypes_test.py b/tests/darwin/datatypes_test.py index 569f8e285..13852c493 100644 --- a/tests/darwin/datatypes_test.py +++ b/tests/darwin/datatypes_test.py @@ -6,7 +6,11 @@ import pytest +from darwin.client import Client +from darwin.config import Config +from darwin.dataset.remote_dataset_v2 import RemoteDatasetV2 from darwin.datatypes import ( + ObjectStore, Point, make_complex_polygon, make_polygon, @@ -128,3 +132,60 @@ def test_split_paths_by_manifest( len(property_class.properties or []) for property_class in property_classes or [] ] == properties_n + + +class TestObjectStore: + @pytest.fixture + def object_store(self): + return ObjectStore( + name="test", + prefix="test_prefix", + readonly=False, + provider="aws", + default=True, + ) + + @pytest.fixture + def darwin_client( + darwin_config_path: Path, + darwin_datasets_path: Path, + team_slug_darwin_json_v2: str, + ) -> Client: + config = Config(darwin_config_path) + config.put(["global", "api_endpoint"], "http://localhost/api") + config.put(["global", "base_url"], "http://localhost") + config.put(["teams", team_slug_darwin_json_v2, "api_key"], "mock_api_key") + config.put( + ["teams", team_slug_darwin_json_v2, "datasets_dir"], + str(darwin_datasets_path), + ) + return Client(config) + + @pytest.fixture + def remote_dataset_v2(self): + return RemoteDatasetV2( + client=self.darwin_client, + team="test_team", + name="Test dataset", + slug="test-dataset", + dataset_id=1, + ) + + def test_init(self, object_store): + assert object_store.name == "test" + assert object_store.prefix == "test_prefix" + assert object_store.readonly is False + assert object_store.provider == "aws" + assert object_store.default is True + + def test_str(self, object_store): + assert ( + str(object_store) + == "Storage configuration:\n- Name: test\n- Prefix: test_prefix\n- Readonly: False\n- Provider: aws\n- Default: True" + ) + + def test_repr(self, object_store): + assert ( + repr(object_store) + == "ObjectStore(name=test, prefix=test_prefix, readonly=False, provider=aws)" + ) diff --git a/tests/darwin/objectstore_test.py b/tests/darwin/objectstore_test.py deleted file mode 100644 index 43ede8f9f..000000000 --- a/tests/darwin/objectstore_test.py +++ /dev/null @@ -1,65 +0,0 @@ -from pathlib import Path - -import pytest - -from darwin.client import Client -from darwin.config import Config -from darwin.dataset.remote_dataset_v2 import RemoteDatasetV2 -from darwin.datatypes import ObjectStore - - -class TestObjectStore: - @pytest.fixture - def object_store(self): - return ObjectStore( - name="test", - prefix="test_prefix", - readonly=False, - provider="aws", - default=True, - ) - - @pytest.fixture - def darwin_client( - darwin_config_path: Path, - darwin_datasets_path: Path, - team_slug_darwin_json_v2: str, - ) -> Client: - config = Config(darwin_config_path) - config.put(["global", "api_endpoint"], "http://localhost/api") - config.put(["global", "base_url"], "http://localhost") - config.put(["teams", team_slug_darwin_json_v2, "api_key"], "mock_api_key") - config.put( - ["teams", team_slug_darwin_json_v2, "datasets_dir"], - str(darwin_datasets_path), - ) - return Client(config) - - @pytest.fixture - def remote_dataset_v2(self): - return RemoteDatasetV2( - client=self.darwin_client, - team="test_team", - name="Test dataset", - slug="test-dataset", - dataset_id=1, - ) - - def test_init(self, object_store): - assert object_store.name == "test" - assert object_store.prefix == "test_prefix" - assert object_store.readonly is False - assert object_store.provider == "aws" - assert object_store.default is True - - def test_str(self, object_store): - assert ( - str(object_store) - == "Storage configuration:\n- Name: test\n- Prefix: test_prefix\n- Readonly: False\n- Provider: aws\n- Default: True" - ) - - def test_repr(self, object_store): - assert ( - repr(object_store) - == "ObjectStore(name=test, prefix=test_prefix, readonly=False, provider=aws)" - ) From c9e6428609183f379ef72edd1864da1254bdfd4a Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 12 Mar 2024 17:31:10 +0000 Subject: [PATCH 14/28] 1 failing test remaining (Video annotation parsing) (WIP) --- darwin/dataset/download_manager.py | 2 - darwin/dataset/local_dataset.py | 2 +- darwin/exporter/formats/coco.py | 7 +- darwin/exporter/formats/cvat.py | 2 - darwin/exporter/formats/darwin.py | 9 +- darwin/exporter/formats/dataloop.py | 2 - .../formats/helpers/yolo_class_builder.py | 2 +- darwin/exporter/formats/nifti.py | 6 - darwin/exporter/formats/pascalvoc.py | 9 +- darwin/exporter/formats/yolo.py | 2 +- darwin/importer/formats/coco.py | 2 - darwin/importer/importer.py | 28 +- darwin/item.py | 2 - darwin/torch/dataset.py | 2 - darwin/utils/utils.py | 12 +- tests/darwin/cli_functions_test.py | 4 +- tests/darwin/dataset/dataset_utils_test.py | 174 +++++- .../exporter/formats/export_mask_test.py | 2 +- .../exporter/formats/export_pascalvoc_test.py | 4 +- .../importer/formats/import_darwin_test.py | 579 ++++++++++++++---- tests/darwin/importer/importer_test.py | 56 +- tests/darwin/torch/dataset_test.py | 5 +- tests/darwin/utils/find_files_test.py | 1 - tests/darwin/utils_test.py | 571 +++++++++++------ 24 files changed, 1040 insertions(+), 445 deletions(-) diff --git a/darwin/dataset/download_manager.py b/darwin/dataset/download_manager.py index 03c16a413..6fb0d400c 100644 --- a/darwin/dataset/download_manager.py +++ b/darwin/dataset/download_manager.py @@ -9,7 +9,6 @@ from tempfile import TemporaryDirectory from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple -import deprecation import numpy as np import orjson as json import requests @@ -28,7 +27,6 @@ is_image_extension_allowed, parse_darwin_json, ) -from darwin.version import __version__ def download_all_images_from_annotations( diff --git a/darwin/dataset/local_dataset.py b/darwin/dataset/local_dataset.py index 5ba891dd7..cff6fedc8 100644 --- a/darwin/dataset/local_dataset.py +++ b/darwin/dataset/local_dataset.py @@ -137,7 +137,7 @@ def _setup_annotations_and_images( keep_empty_annotations: bool = False, ): # Find all the annotations and their corresponding images - with_folders = any([item.is_dir() for item in images_dir.iterdir()]) + with_folders = any(item.is_dir() for item in images_dir.iterdir()) annotation_filepaths = get_annotation_filepaths( release_path, annotations_dir, annotation_type, split, partition, split_type ) diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index 21f802f8b..d4785067e 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -3,14 +3,11 @@ from typing import Any, Dict, Iterator, List, Optional from zlib import crc32 -import deprecation import numpy as np import orjson as json -from upolygon import draw_polygon, rle_encode import darwin.datatypes as dt from darwin.utils import convert_polygons_to_sequences -from darwin.version import __version__ DEPRECATION_MESSAGE = """ @@ -69,7 +66,7 @@ def _calculate_categories(annotation_files: List[dt.AnnotationFile]) -> Dict[str categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return {k: v for k, v in sorted(categories.items(), key=lambda item: item[1])} + return dict(sorted(categories.items(), key=lambda item: item[1])) def _calculate_tag_categories( @@ -85,7 +82,7 @@ def _calculate_tag_categories( categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return {k: v for k, v in sorted(categories.items(), key=lambda item: item[1])} + return dict(sorted(categories.items(), key=lambda item: item[1])) def _calculate_category_id(annotation_class: dt.AnnotationClass) -> int: diff --git a/darwin/exporter/formats/cvat.py b/darwin/exporter/formats/cvat.py index 1aa133964..88e685dcd 100644 --- a/darwin/exporter/formats/cvat.py +++ b/darwin/exporter/formats/cvat.py @@ -3,10 +3,8 @@ from typing import Any, Dict, Iterator, List, Optional from xml.etree.ElementTree import Element, SubElement, tostring -import deprecation import darwin.datatypes as dt -from darwin.version import __version__ DEPRECATION_MESSAGE = """ diff --git a/darwin/exporter/formats/darwin.py b/darwin/exporter/formats/darwin.py index fd436057a..becf4a179 100644 --- a/darwin/exporter/formats/darwin.py +++ b/darwin/exporter/formats/darwin.py @@ -1,11 +1,9 @@ from typing import Any, Dict, List -import deprecation import darwin.datatypes as dt # from darwin.datatypes import PolygonPath, PolygonPaths -from darwin.version import __version__ DEPRECATION_MESSAGE = """ @@ -91,12 +89,7 @@ def _build_polygon_data(data: Dict[str, Any]) -> Dict[str, Any]: Dict[str, List[List[Dict[str, float]]]] The polygon data in the format required for Darwin v2 annotations. """ - - # Complex polygon - if "paths" in data: - return {"paths": data["paths"]} - else: - return {"paths": [data["path"]]} + return {"paths": data["paths"]} def _build_item_data( diff --git a/darwin/exporter/formats/dataloop.py b/darwin/exporter/formats/dataloop.py index 89ba76937..5f5bee622 100644 --- a/darwin/exporter/formats/dataloop.py +++ b/darwin/exporter/formats/dataloop.py @@ -1,11 +1,9 @@ from pathlib import Path from typing import Any, Dict, Iterable -import deprecation import orjson as json import darwin.datatypes as dt -from darwin.version import __version__ DEPRECATION_MESSAGE = """ diff --git a/darwin/exporter/formats/helpers/yolo_class_builder.py b/darwin/exporter/formats/helpers/yolo_class_builder.py index ac20a495b..fd5c3e41f 100644 --- a/darwin/exporter/formats/helpers/yolo_class_builder.py +++ b/darwin/exporter/formats/helpers/yolo_class_builder.py @@ -8,7 +8,7 @@ def build_class_index( annotation_files: Iterable[AnnotationFile], - include_types: List[str] = ["bounding_box", "polygon", "complex_polygon"], + include_types: List[str] = ["bounding_box", "polygon"], ) -> ClassIndex: classes = set() for annotation_file in annotation_files: diff --git a/darwin/exporter/formats/nifti.py b/darwin/exporter/formats/nifti.py index da4d1b8bd..c0baec33a 100644 --- a/darwin/exporter/formats/nifti.py +++ b/darwin/exporter/formats/nifti.py @@ -340,12 +340,6 @@ def populate_output_volumes_from_polygons( shift_polygon_coords(polygon_path, pixdims) for polygon_path in frame_data["paths"] ] - elif "path" in frame_data: - # Dealing with a simple polygon - polygons = shift_polygon_coords( - frame_data["path"], - pixdims, - ) else: continue im_mask = convert_polygons_to_mask(polygons, height=height, width=width) diff --git a/darwin/exporter/formats/pascalvoc.py b/darwin/exporter/formats/pascalvoc.py index c3f681081..8c698ed93 100644 --- a/darwin/exporter/formats/pascalvoc.py +++ b/darwin/exporter/formats/pascalvoc.py @@ -1,12 +1,9 @@ from pathlib import Path -from typing import Any, Dict, Iterable +from typing import Any, Iterable from xml.etree.ElementTree import Element, SubElement, tostring -import deprecation import darwin.datatypes as dt -from darwin.utils import attempt_decode -from darwin.version import __version__ DEPRECATION_MESSAGE = """ @@ -66,7 +63,7 @@ def _build_xml(annotation_file: dt.AnnotationFile) -> Element: for annotation in annotation_file.annotations: annotation_type = annotation.annotation_class.annotation_type - if annotation_type not in ["bounding_box", "polygon", "complex_polygon"]: + if annotation_type not in ["bounding_box", "polygon"]: continue data = annotation.data @@ -77,7 +74,7 @@ def _build_xml(annotation_file: dt.AnnotationFile) -> Element: _add_subelement_text(sub_annotation, "difficult", "0") bndbox = SubElement(sub_annotation, "bndbox") - if annotation_type == "polygon" or annotation_type == "complex_polygon": + if annotation_type == "polygon": data = data.get("bounding_box") xmin = data.get("x") diff --git a/darwin/exporter/formats/yolo.py b/darwin/exporter/formats/yolo.py index 73ac5ddb3..3aeb993d6 100644 --- a/darwin/exporter/formats/yolo.py +++ b/darwin/exporter/formats/yolo.py @@ -45,7 +45,7 @@ def _build_txt(annotation_file: dt.AnnotationFile, class_index: ClassIndex) -> s if annotation_type == "bounding_box": data = annotation.data - elif annotation_type in ["polygon", "complex_polygon"]: + elif annotation_type == "polygon": data = annotation.data data = data.get("bounding_box") else: diff --git a/darwin/importer/formats/coco.py b/darwin/importer/formats/coco.py index 6b500dd6b..616a4eeb5 100644 --- a/darwin/importer/formats/coco.py +++ b/darwin/importer/formats/coco.py @@ -2,14 +2,12 @@ from pathlib import Path from typing import Dict, Iterator, List, Optional -import deprecation import orjson as json from upolygon import find_contours, rle_decode import darwin.datatypes as dt from darwin.path_utils import deconstruct_full_path from darwin.utils import attempt_decode -from darwin.version import __version__ DEPRECATION_MESSAGE = """ diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index caf084130..7e342a6fd 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -35,7 +35,6 @@ from darwin.client import Client from darwin.dataset.remote_dataset import RemoteDataset -import deprecation from rich.console import Console from rich.progress import track from rich.theme import Theme @@ -45,7 +44,6 @@ from darwin.exceptions import IncompatibleOptions, RequestEntitySizeExceeded from darwin.utils import secure_continue_request from darwin.utils.flatten_list import flatten_list -from darwin.version import __version__ logger = getLogger(__name__) @@ -1011,15 +1009,17 @@ def _handle_subs( return data -def _handle_complex_polygon( +def _format_polygon_for_import( annotation: dt.Annotation, data: dt.DictFreeForm ) -> dt.DictFreeForm: - if "complex_polygon" in data: - del data["complex_polygon"] - data["polygon"] = { - "path": annotation.data["paths"][0], - "additional_paths": annotation.data["paths"][1:], - } + if "polygon" in data: + if len(annotation.data["paths"]) > 1: + data["polygon"] = { + "path": annotation.data["paths"][0], + "additional_paths": annotation.data["paths"][1:], + } + elif len(annotation.data["paths"]) == 1: + data["polygon"] = {"path": annotation.data["paths"][0]} return data @@ -1087,14 +1087,14 @@ def _get_annotation_data( only_keyframes=True, post_processing=lambda annotation, data: _handle_subs( annotation, - _handle_complex_polygon(annotation, data), + _format_polygon_for_import(annotation, data), annotation_class_id, attributes, ), ) else: data = {annotation_class.annotation_type: annotation.data} - data = _handle_complex_polygon(annotation, data) + data = _format_polygon_for_import(annotation, data) data = _handle_subs(annotation, data, annotation_class_id, attributes) return data @@ -1273,9 +1273,9 @@ def _import_annotations( # Insert the default slot name if not available in the import source annotation = _handle_slot_names(annotation, dataset.version, default_slot_name) - annotation_class_ids_map[(annotation_class.name, annotation_type)] = ( - annotation_class_id - ) + annotation_class_ids_map[ + (annotation_class.name, annotation_type) + ] = annotation_class_id serial_obj = { "annotation_class_id": annotation_class_id, "data": data, diff --git a/darwin/item.py b/darwin/item.py index 3ae8e7243..6b11fbffc 100644 --- a/darwin/item.py +++ b/darwin/item.py @@ -1,11 +1,9 @@ from dataclasses import dataclass from typing import Any, Dict, List, Optional -import deprecation from pydantic import BaseModel from darwin.path_utils import construct_full_path -from darwin.version import __version__ @dataclass(frozen=True, eq=True) diff --git a/darwin/torch/dataset.py b/darwin/torch/dataset.py index b98592e0c..256a519db 100644 --- a/darwin/torch/dataset.py +++ b/darwin/torch/dataset.py @@ -326,8 +326,6 @@ def get_target(self, index: int) -> Dict[str, Any]: annotations = [] for annotation in target["annotations"]: - annotation_type: str = annotation.annotation_class.annotation_type - path_key = "paths" if annotation_type == "complex_polygon" else "path" # Darwin V2 only has paths (TODO it might be more robust fixes) if "paths" in annotation.data: diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index ec1b8f371..fb00ff84e 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -19,7 +19,6 @@ cast, ) -import deprecation import json_stream import numpy as np import orjson as json @@ -39,7 +38,6 @@ UnsupportedFileType, ) from darwin.future.data_objects.properties import SelectedProperty -from darwin.version import __version__ if TYPE_CHECKING: from darwin.client import Client @@ -408,13 +406,7 @@ def parse_darwin_json( if "annotations" not in data: return None - if version.major == 2: - return _parse_darwin_v2(path, data) - else: - if "fps" in data["image"] or "frame_count" in data["image"]: - return _parse_darwin_video(path, data, count) - else: - return _parse_darwin_image(path, data, count) + return _parse_darwin_v2(path, data) def stream_darwin_json(path: Path) -> PersistentStreamingJSONObject: @@ -1230,7 +1222,7 @@ def ispolygon(annotation: dt.AnnotationClass) -> bool: ------- ``True`` is the given ``AnnotationClass`` is a polygon, ``False`` otherwise. """ - return annotation.annotation_type in ["polygon", "complex_polygon"] + return annotation.annotation_type == "polygon" def convert_polygons_to_sequences( diff --git a/tests/darwin/cli_functions_test.py b/tests/darwin/cli_functions_test.py index d1055ffce..718c92433 100644 --- a/tests/darwin/cli_functions_test.py +++ b/tests/darwin/cli_functions_test.py @@ -47,7 +47,7 @@ def test_default_non_verbose( remote_dataset: RemoteDataset, request_upload_endpoint: str, ): - request_upload_response = response = { + request_upload_response = { "blocked_items": [ { "id": "3b241101-e2bb-4255-8caf-4136c566a964", @@ -150,7 +150,7 @@ def test_with_verbose_flag( remote_dataset: RemoteDataset, request_upload_endpoint: str, ): - request_upload_response = response = { + request_upload_response = { "blocked_items": [ { "id": "3b241101-e2bb-4255-8caf-4136c566a964", diff --git a/tests/darwin/dataset/dataset_utils_test.py b/tests/darwin/dataset/dataset_utils_test.py index 96943e73e..cdd9dfe0f 100644 --- a/tests/darwin/dataset/dataset_utils_test.py +++ b/tests/darwin/dataset/dataset_utils_test.py @@ -25,20 +25,58 @@ def open_resource_file(): def parsed_annotation_file(): return { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "test.jpg", + "path": "/", + "slots": [ + { + "type": "image", + "slot_name": "0", + "width": 1920, + "height": 1080, + "source_files": [ + { + "file_name": "test.jpg", + "url": "https://darwin.v7labs.com/test.jpg", + } + ], + } + ], + }, "annotations": [ - {"name": "class_1", "polygon": {"paths": [{"x": 0, "y": 0}]}}, - {"name": "class_1", "polygon": {"paths": [{"x": 0, "y": 0}]}}, - {"name": "class_2", "polygon": {"paths": [{"x": 0, "y": 0}]}}, - {"name": "class_2", "polygon": {"paths": [{"x": 0, "y": 0}]}}, - {"name": "class_2", "polygon": {"paths": [{"x": 0, "y": 0}]}}, - {"name": "class_3", "polygon": {"paths": [{"x": 0, "y": 0}]}}, + { + "name": "class_1", + "polygon": {"paths": [[{"x": 0, "y": 0}]]}, + "slot_names": ["0"], + }, + { + "name": "class_1", + "polygon": {"paths": [[{"x": 0, "y": 0}]]}, + "slot_names": ["0"], + }, + { + "name": "class_2", + "polygon": {"paths": [[{"x": 0, "y": 0}]]}, + "slot_names": ["0"], + }, + { + "name": "class_2", + "polygon": {"paths": [[{"x": 0, "y": 0}]]}, + "slot_names": ["0"], + }, + { + "name": "class_2", + "polygon": {"paths": [[{"x": 0, "y": 0}]]}, + "slot_names": ["0"], + }, + { + "name": "class_3", + "polygon": {"paths": [[{"x": 0, "y": 0}]]}, + "slot_names": ["0"], + }, ], - "image": { - "filename": "test.jpg", - "height": 1080, - "url": "https://darwin.v7labs.com/test.jpg", - "width": 1920, - }, } @@ -71,6 +109,21 @@ def annotations_path(self, tmp_path: Path): def test_builds_correct_mapping_dictionaries(self, annotations_path: Path): payload = { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "0.jpg", + "path": "/", + "slots": [ + { + "type": "image", + "slot_name": "0", + "source_files": [ + {"file_name": "0.jpg", "url": "https://example.com/0.jpg"} + ], + } + ], + }, "annotations": [ {"name": "class_1", "polygon": {"paths": [[]]}}, { @@ -81,11 +134,25 @@ def test_builds_correct_mapping_dictionaries(self, annotations_path: Path): {"name": "class_4", "tag": {}}, {"name": "class_1", "polygon": {"paths": [[]]}}, ], - "image": {"filename": "0.jpg"}, } _create_annotation_file(annotations_path, "0.json", payload) payload = { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "1.jpg", + "path": "/", + "slots": [ + { + "type": "image", + "slot_name": "0", + "source_files": [ + {"file_name": "1.jpg", "url": "https://example.com/1.jpg"} + ], + } + ], + }, "annotations": [ {"name": "class_5", "polygon": {"paths": [[]]}}, { @@ -96,7 +163,6 @@ def test_builds_correct_mapping_dictionaries(self, annotations_path: Path): {"name": "class_4", "tag": {}}, {"name": "class_1", "polygon": {"paths": [[]]}}, ], - "image": {"filename": "1.jpg"}, } _create_annotation_file(annotations_path, "1.json", payload) class_dict, index_dict = extract_classes(annotations_path, "polygon") @@ -121,34 +187,94 @@ def test_extract_multiple_annotation_types(self, annotations_path: Path): annotations_path, "0.json", { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "0.jpg", + "path": "/", + "slots": [ + { + "type": "image", + "slot_name": "0", + "source_files": [ + { + "file_name": "0.jpg", + "url": "https://example.com/0.jpg", + } + ], + } + ], + }, "annotations": [ - {"name": "class_1", "polygon": {"paths": [[]]}}, + { + "name": "class_1", + "polygon": {"paths": [[]]}, + "slot_names": ["0"], + }, { "name": "class_2", "bounding_box": {"x": 0, "y": 0, "w": 100, "h": 100}, + "slot_names": ["0"], + }, + { + "name": "class_3", + "polygon": {"paths": [[]]}, + "slot_names": ["0"], + }, + {"name": "class_4", "slot_names": ["0"]}, + { + "name": "class_1", + "polygon": {"paths": [[]]}, + "slot_names": ["0"], }, - {"name": "class_3", "polygon": {"paths": [[]]}}, - {"name": "class_4", "tag": {}}, - {"name": "class_1", "polygon": {"paths": [[]]}}, ], - "image": {"filename": "0.jpg"}, }, ) _create_annotation_file( annotations_path, "1.json", { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "1.jpg", + "path": "/", + "slots": [ + { + "type": "image", + "slot_name": "0", + "source_files": [ + { + "file_name": "1.jpg", + "url": "https://example.com/1.jpg", + } + ], + } + ], + }, "annotations": [ - {"name": "class_5", "polygon": {"paths": [[]]}}, + { + "name": "class_5", + "polygon": {"paths": [[]]}, + "slot_names": ["0"], + }, { "name": "class_6", "bounding_box": {"x": 0, "y": 0, "w": 100, "h": 100}, + "slot_names": ["0"], + }, + { + "name": "class_1", + "polygon": {"paths": [[]]}, + "slot_names": ["0"], + }, + {"name": "class_4", "slot_names": ["0"]}, + { + "name": "class_1", + "polygon": {"paths": [[]]}, + "slot_names": ["0"], }, - {"name": "class_1", "polygon": {"paths": [[]]}}, - {"name": "class_4", "tag": {}}, - {"name": "class_1", "polygon": {"paths": [[]]}}, ], - "image": {"filename": "1.jpg"}, }, ) diff --git a/tests/darwin/exporter/formats/export_mask_test.py b/tests/darwin/exporter/formats/export_mask_test.py index eee0eebe1..a9d9b268f 100644 --- a/tests/darwin/exporter/formats/export_mask_test.py +++ b/tests/darwin/exporter/formats/export_mask_test.py @@ -313,7 +313,7 @@ def test_beyond_polygon_beyond_window() -> None: assert not errors -def test_beyond_complex_polygon() -> None: +def test_beyond_multi_path_polygons() -> None: mask = np.zeros((5, 5), dtype=np.uint8) colours: dt.MaskTypes.ColoursDict = {} categories: dt.MaskTypes.CategoryList = ["__background__"] diff --git a/tests/darwin/exporter/formats/export_pascalvoc_test.py b/tests/darwin/exporter/formats/export_pascalvoc_test.py index d31251267..1bff42daf 100644 --- a/tests/darwin/exporter/formats/export_pascalvoc_test.py +++ b/tests/darwin/exporter/formats/export_pascalvoc_test.py @@ -80,10 +80,10 @@ def test_xml_has_bounding_boxes_of_polygons(self): assert_xml_element_text(bndbox, "xmax", "1803") assert_xml_element_text(bndbox, "ymax", "983") - def test_xml_has_bounding_boxes_of_complex_polygons(self): + def test_xml_has_bounding_boxes_of_multi_path_polygons(self): annotation_class = AnnotationClass( name="rubber", - annotation_type="complex_polygon", + annotation_type="polygon", annotation_internal_type="polygon", ) annotation = Annotation( diff --git a/tests/darwin/importer/formats/import_darwin_test.py b/tests/darwin/importer/formats/import_darwin_test.py index 6dd6c2335..d6db42aec 100644 --- a/tests/darwin/importer/formats/import_darwin_test.py +++ b/tests/darwin/importer/formats/import_darwin_test.py @@ -22,53 +22,148 @@ def test_it_parses_slot_names_properly_if_present_for_sequences( self, file_path: Path ): json: str = """ - { - "dataset": "test", - "image": { - "width": 2479, - "height": 3508, - "fps": 30.0, - "original_filename": "Invoice.pdf", - "filename": "Invoice.pdf", - "url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/1a46356d-005b-4095-98fc-fc4ea6d7294a/original", - "path": "/", - "workview_url": "https://staging.v7labs.com/teams/rafals-team/items/0182e9d2-d217-3260-52db-d7828422f86b/workview", - "frame_count": 2, - "frame_urls": [ - "https://staging.v7labs.com/api/v2/teams/rafals-team/files/1a46356d-005b-4095-98fc-fc4ea6d7294a/sections/0", - "https://staging.v7labs.com/api/v2/teams/rafals-team/files/1a46356d-005b-4095-98fc-fc4ea6d7294a/sections/1" - ] + { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "Invoice.pdf", + "path": "/", + "source_info": { + "item_id": "018e3385-822c-fbab-e766-acd624a8a273", + "dataset": { + "name": "folder_test", + "slug": "folder_test", + "dataset_management_url": "https://darwin.v7labs.com/datasets/722603/dataset-management" + }, + "team": { + "name": "V7 John", + "slug": "v7-john" + }, + "workview_url": "https://darwin.v7labs.com/workview?dataset=722603&item=018e3385-822c-fbab-e766-acd624a8a273" }, - "annotations": [ + "slots": [ { - "frames": { - "0": { - "bounding_box": { - "h": 338.29, - "w": 444.87, - "x": 845.6, - "y": 1056.57 - }, - "keyframe": true, - "text": { - "text": "some weird text" - } - } - }, - "id": "d89a5895-c721-420b-9c7d-d71880e3679b", - "interpolate_algorithm": "linear-1.1", - "interpolated": true, - "name": "address", - "segments": [ - [0, 2] - ], - "slot_names": [ - "my_slot" - ] + "type": "video", + "slot_name": "0", + "width": 1920, + "height": 1080, + "fps": 1, + "thumbnail_url": "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/thumbnail", + "source_files": [ + { + "file_name": "mini_uct.mp4", + "url": "https://darwin.v7labs.com/api/v2/teams/v7-john/uploads/db035ac4-4327-4b11-85b7-432c0e09c896" + } + ], + "frame_count": 8, + "frame_urls": [ + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/0", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/1", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/2", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/3", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/4", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/5", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/6", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/7" + ] } ] + }, + "annotations": [ + { + "frames": { + "0": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": true + }, + "1": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "2": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "3": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "4": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "5": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "6": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "7": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": true + } + }, + "hidden_areas": [], + "id": "06865ac8-d2f8-4b8f-a653-9cd08df5b3f5", + "interpolate_algorithm": "linear-1.1", + "interpolated": true, + "name": "curia", + "properties": [], + "ranges": [ + [ + 0, + 8 + ] + ], + "slot_names": [ + "0" + ] } - """ + ] + } + """ file_path.write_text(json) @@ -83,37 +178,151 @@ def test_it_parses_slot_names_properly_if_present_for_sequences( assert annotation_file.annotations for annotation in annotation_file.annotations: - assert annotation.slot_names == ["my_slot"] + assert annotation.slot_names == ["0"] def test_it_parses_slot_names_properly_if_present_for_images(self, file_path: Path): json: str = """ { - "dataset": "test", - "image": { - "width": 500, - "height": 375, - "original_filename": "my_image.jpg", - "filename": "my_image.jpg", - "url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/d119a57f-bbbb-4b9b-a7a2-6dcb16a59e98/original", - "thumbnail_url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/d119a57f-bbbb-4b9b-a7a2-6dcb16a59e98/thumbnail", - "path": "/", - "workview_url": "https://staging.v7labs.com/teams/rafals-team/items/0182e9d2-d217-681d-2448-197904d2e05c/workview" + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "ferrari-laferrari.jpg", + "path": "/", + "source_info": { + "item_id": "018c4450-d91d-ff3e-b226-60d48b66f86e", + "dataset": { + "name": "bbox", + "slug": "bbox", + "dataset_management_url": "https://darwin.v7labs.com/datasets/623079/dataset-management" + }, + "team": { + "name": "V7 John", + "slug": "v7-john" + }, + "workview_url": "https://darwin.v7labs.com/workview?dataset=623079&item=018c4450-d91d-ff3e-b226-60d48b66f86e" }, - "annotations": [ + "slots": [ { - "bounding_box": { - "h": 151.76, - "w": 140.89, - "x": 252.09, - "y": 173.49 - }, - "id": "ab8035d0-61b8-4294-b348-085461555df8", - "name": "dog", - "slot_names": [ - "my_slot" - ] + "type": "image", + "slot_name": "0", + "width": 640, + "height": 425, + "thumbnail_url": "https://darwin.v7labs.com/api/v2/teams/v7-john/files/ddc5cbc2-8438-4e36-8ab6-43e2f3746bf1/thumbnail", + "source_files": [ + { + "file_name": "000000007751.jpg", + "url": "https://darwin.v7labs.com/api/v2/teams/v7-john/uploads/3395d29a-7539-4a51-a3ca-c7a95f460345" + } + ] } ] + }, + "annotations": [ + { + "bounding_box": { + "h": 53.963699999999996, + "w": 83.7195, + "x": 32.7817, + "y": 53.9638 + }, + "id": "8940a690-d8a9-4c83-9f59-38f0ef780246", + "name": "new-class-2", + "polygon": { + "paths": [ + [ + { + "x": 65.0591, + "y": 53.9638 + }, + { + "x": 32.7817, + "y": 107.9275 + }, + { + "x": 116.5012, + "y": 104.9015 + } + ] + ] + }, + "properties": [], + "slot_names": [ + "0" + ] + }, + { + "id": "782618fb-4c69-436e-80cb-71765d255dbf", + "name": "skeleton-test", + "properties": [], + "skeleton": { + "nodes": [ + { + "name": "node", + "occluded": false, + "x": 264.7754, + "y": 121.5445 + }, + { + "name": "2", + "occluded": false, + "x": 245.1335, + "y": 107.3425 + }, + { + "name": "3", + "occluded": false, + "x": 240.4646, + "y": 125.4178 + }, + { + "name": "4", + "occluded": false, + "x": 280.3923, + "y": 137.468 + } + ] + }, + "slot_names": [ + "0" + ] + }, + { + "id": "b6bea00c-c8a4-4d34-b72f-88567d9e8cd5", + "name": "skeleton-test", + "properties": [], + "skeleton": { + "nodes": [ + { + "name": "node", + "occluded": false, + "x": 136.1702, + "y": 306.1308 + }, + { + "name": "2", + "occluded": false, + "x": 145.1629, + "y": 291.263 + }, + { + "name": "3", + "occluded": false, + "x": 147.3005, + "y": 310.1857 + }, + { + "name": "4", + "occluded": false, + "x": 129.0203, + "y": 322.8007 + } + ] + }, + "slot_names": [ + "0" + ] + } + ] } """ @@ -123,64 +332,159 @@ def test_it_parses_slot_names_properly_if_present_for_images(self, file_path: Pa assert annotation_file is not None assert annotation_file.path == file_path - assert annotation_file.filename == "my_image.jpg" + assert annotation_file.filename == "ferrari-laferrari.jpg" assert annotation_file.annotation_classes assert annotation_file.remote_path == "/" assert annotation_file.annotations for annotation in annotation_file.annotations: - assert annotation.slot_names == ["my_slot"] + assert annotation.slot_names == ["0"] def test_it_skips_slot_names_when_no_slot_names_for_sequences( self, file_path: Path ): json: str = """ - { - "dataset": "test", - "image": { - "width": 2479, - "height": 3508, - "fps": 30.0, - "original_filename": "Invoice.pdf", - "filename": "Invoice.pdf", - "url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/1a46356d-005b-4095-98fc-fc4ea6d7294a/original", - "path": "/", - "workview_url": "https://staging.v7labs.com/teams/rafals-team/items/0182e9d2-d217-3260-52db-d7828422f86b/workview", - "frame_count": 2, - "frame_urls": [ - "https://staging.v7labs.com/api/v2/teams/rafals-team/files/1a46356d-005b-4095-98fc-fc4ea6d7294a/sections/0", - "https://staging.v7labs.com/api/v2/teams/rafals-team/files/1a46356d-005b-4095-98fc-fc4ea6d7294a/sections/1" - ] + { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "Invoice.pdf", + "path": "/", + "source_info": { + "item_id": "018e3385-822c-fbab-e766-acd624a8a273", + "dataset": { + "name": "folder_test", + "slug": "folder_test", + "dataset_management_url": "https://darwin.v7labs.com/datasets/722603/dataset-management" + }, + "team": { + "name": "V7 John", + "slug": "v7-john" + }, + "workview_url": "https://darwin.v7labs.com/workview?dataset=722603&item=018e3385-822c-fbab-e766-acd624a8a273" }, - "annotations": [ + "slots": [ { - "frames": { - "0": { - "bounding_box": { - "h": 338.29, - "w": 444.87, - "x": 845.6, - "y": 1056.57 - }, - "keyframe": true, - "text": { - "text": "some weird text" - } - } - }, - "id": "d89a5895-c721-420b-9c7d-d71880e3679b", - "interpolate_algorithm": "linear-1.1", - "interpolated": true, - "name": "address", - "segments": [ - [0, 2] - ] + "type": "video", + "slot_name": "", + "width": 1920, + "height": 1080, + "fps": 1, + "thumbnail_url": "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/thumbnail", + "source_files": [ + { + "file_name": "mini_uct.mp4", + "url": "https://darwin.v7labs.com/api/v2/teams/v7-john/uploads/db035ac4-4327-4b11-85b7-432c0e09c896" + } + ], + "frame_count": 8, + "frame_urls": [ + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/0", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/1", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/2", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/3", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/4", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/5", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/6", + "https://darwin.v7labs.com/api/v2/teams/v7-john/files/926ee041-03c0-4354-aea2-8b9db422341d/sections/7" + ] } ] + }, + "annotations": [ + { + "frames": { + "0": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": true + }, + "1": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "2": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "3": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "4": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "5": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "6": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": false + }, + "7": { + "bounding_box": { + "h": 152.502, + "w": 309.579, + "x": 466.6561, + "y": 338.5544 + }, + "keyframe": true + } + }, + "hidden_areas": [], + "id": "06865ac8-d2f8-4b8f-a653-9cd08df5b3f5", + "interpolate_algorithm": "linear-1.1", + "interpolated": true, + "name": "curia", + "properties": [], + "ranges": [ + [ + 0, + 8 + ] + ], + "slot_names": [] } - """ - + ] + } + """ file_path.write_text(json) annotation_file: Optional[AnnotationFile] = parse_path(file_path) @@ -199,29 +503,54 @@ def test_it_skips_slot_names_when_no_slot_names_for_sequences( def test_it_skips_slot_names_when_no_slot_names_for_images(self, file_path: Path): json: str = """ { - "dataset": "test", - "image": { - "width": 500, - "height": 375, - "original_filename": "my_image.jpg", - "filename": "my_image.jpg", - "url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/d119a57f-bbbb-4b9b-a7a2-6dcb16a59e98/original", - "thumbnail_url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/d119a57f-bbbb-4b9b-a7a2-6dcb16a59e98/thumbnail", - "path": "/", + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "my_image.jpg", + "path": "/", + "source_info": { + "item_id": "0182e9d2-d217-681d-2448-197904d2e05c", + "dataset": { + "name": "test", + "slug": "test", + "dataset_management_url": "https://staging.v7labs.com/teams/rafals-team/items/0182e9d2-d217-681d-2448-197904d2e05c/workview" + }, + "team": { + "name": "rafals-team", + "slug": "rafals-team" + }, "workview_url": "https://staging.v7labs.com/teams/rafals-team/items/0182e9d2-d217-681d-2448-197904d2e05c/workview" }, - "annotations": [ + "slots": [ { - "bounding_box": { - "h": 151.76, - "w": 140.89, - "x": 252.09, - "y": 173.49 - }, - "id": "ab8035d0-61b8-4294-b348-085461555df8", - "name": "dog" + "type": "image", + "slot_name": "", + "width": 500, + "height": 375, + "thumbnail_url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/d119a57f-bbbb-4b9b-a7a2-6dcb16a59e98/thumbnail", + "source_files": [ + { + "file_name": "my_image.jpg", + "url": "https://staging.v7labs.com/api/v2/teams/rafals-team/files/d119a57f-bbbb-4b9b-a7a2-6dcb16a59e98/original" + } + ] } ] + }, + "annotations": [ + { + "bounding_box": { + "h": 151.76, + "w": 140.89, + "x": 252.09, + "y": 173.49 + }, + "id": "ab8035d0-61b8-4294-b348-085461555df8", + "name": "dog", + "properties": [], + "slot_names": [] + } + ] } """ diff --git a/tests/darwin/importer/importer_test.py b/tests/darwin/importer/importer_test.py index 963dc4df4..9e8bf404f 100644 --- a/tests/darwin/importer/importer_test.py +++ b/tests/darwin/importer/importer_test.py @@ -106,29 +106,35 @@ def test_handle_subs() -> None: assert result == expected_result -def test__handle_complex_polygon() -> None: - from darwin.importer.importer import _handle_complex_polygon +def test__format_polygon_for_import() -> None: + from darwin.importer.importer import _format_polygon_for_import - assert _handle_complex_polygon( - {}, - { - "example": "data", - "example2": "data2", - "example3": "data3", - }, - ) == { # type: ignore - "example": "data", - "example2": "data2", - "example3": "data3", - } - assert _handle_complex_polygon( + # Test case when "polygon" key is not in data + assert _format_polygon_for_import( dt.Annotation( - dt.AnnotationClass("Class", "bbox"), {"paths": [1, 2, 3, 4, 5]}, [], [] + dt.AnnotationClass("Class", "polygon"), {"paths": [1, 2, 3, 4, 5]}, [], [] ), - {"complex_polygon": "test_data"}, - ) == { - "polygon": {"path": 1, "additional_paths": [2, 3, 4, 5]}, - } + {"example": "data"}, + ) == {"example": "data"} + + # Test case when "polygon" key is in data and there is more than one path + assert _format_polygon_for_import( + dt.Annotation( + dt.AnnotationClass("Class", "polygon"), + {"paths": [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]}, + [], + [], + ), + {"polygon": {"paths": [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]}}, + ) == {"polygon": {"path": [1, 2, 3, 4, 5], "additional_paths": [[6, 7, 8, 9, 10]]}} + + # Test case when "polygon" key is in data and there is only one path + assert _format_polygon_for_import( + dt.Annotation( + dt.AnnotationClass("Class", "polygon"), {"paths": [[1, 2, 3, 4, 5]]}, [], [] + ), + {"polygon": {"paths": [[1, 2, 3, 4, 5]]}}, + ) == {"polygon": {"path": [1, 2, 3, 4, 5]}} def test__annotators_or_reviewers_to_payload() -> None: @@ -189,7 +195,7 @@ def test__get_annotation_data() -> None: annotation.data = "TEST DATA" - with patch_factory("_handle_complex_polygon") as mock_hcp, patch_factory( + with patch_factory("_format_polygon_for_import") as mock_hcp, patch_factory( "_handle_subs" ) as mock_hs, patch.object( dt.VideoAnnotation, "get_data", return_value="TEST VIDEO DATA" @@ -208,7 +214,7 @@ def test__get_annotation_data() -> None: assert mock_hcp.call_count == 1 assert mock_hs.call_count == 1 - with patch_factory("_handle_complex_polygon") as mock_hcp, patch_factory( + with patch_factory("_format_polygon_for_import") as mock_hcp, patch_factory( "_handle_subs" ) as mock_hs: from darwin.importer.importer import _get_annotation_data @@ -287,7 +293,7 @@ def test_get_overwrite_value() -> None: @pytest.fixture def raster_layer_annotations(): annotation_raster_layer_data = ( - Path(__file__).parent.parent / f"data/annotation_raster_layer_data.json" + Path(__file__).parent.parent / "data/annotation_raster_layer_data.json" ) with open(annotation_raster_layer_data) as f: data = json.load(f) @@ -389,7 +395,7 @@ def raster_layer_annotations(): @pytest.fixture def raster_layer_video_annotations(): annotation_raster_layer_data = ( - Path(__file__).parent.parent / f"data/video_annotation_raster_layer_data.json" + Path(__file__).parent.parent / "data/video_annotation_raster_layer_data.json" ) with open(annotation_raster_layer_data) as f: data = json.load(f) @@ -482,7 +488,7 @@ def test__parse_empty_masks_video(raster_layer_video_annotations) -> None: def test__import_annotations() -> None: - with patch_factory("_handle_complex_polygon") as mock_hcp, patch_factory( + with patch_factory("_format_polygon_for_import") as mock_hcp, patch_factory( "_handle_reviewers" ) as mock_hr, patch_factory("_handle_annotators") as mock_ha, patch_factory( "_handle_subs" diff --git a/tests/darwin/torch/dataset_test.py b/tests/darwin/torch/dataset_test.py index 3928d81fc..fac24bf6c 100644 --- a/tests/darwin/torch/dataset_test.py +++ b/tests/darwin/torch/dataset_test.py @@ -4,7 +4,6 @@ from unittest.mock import patch import numpy as np -import pytest import torch from darwin.config import Config @@ -182,7 +181,7 @@ def test_loads_object_detection_dataset_from_polygon_annotations( "iscrowd": [0], } - def test_loads_object_detection_dataset_from_complex_polygon_annotations( + def test_loads_object_detection_dataset_from_multi_path_polygon_annotations( self, team_slug_darwin_json_v2: str, local_config_file: Config, @@ -261,7 +260,7 @@ def test_loads_instance_segmentation_dataset_from_polygon_annotations( assert label["image_path"] == str(dataset.dataset_path / "images" / "0.png") assert label["width"] == 50 - def test_loads_instance_segmentation_dataset_from_complex_polygon_annotations( + def test_loads_instance_segmentation_dataset_from_multi_path_polygon_annotations( self, team_slug_darwin_json_v2: str, local_config_file: Config, diff --git a/tests/darwin/utils/find_files_test.py b/tests/darwin/utils/find_files_test.py index 8e8fbd6ba..d56fd352b 100644 --- a/tests/darwin/utils/find_files_test.py +++ b/tests/darwin/utils/find_files_test.py @@ -8,7 +8,6 @@ from darwin.utils import ( SUPPORTED_EXTENSIONS, SUPPORTED_IMAGE_EXTENSIONS, - SUPPORTED_VIDEO_EXTENSIONS, find_files, ) diff --git a/tests/darwin/utils_test.py b/tests/darwin/utils_test.py index e05b3a572..1c9dcb872 100644 --- a/tests/darwin/utils_test.py +++ b/tests/darwin/utils_test.py @@ -97,31 +97,51 @@ class TestParseDarwinJson: def test_parses_darwin_images_correctly(self, tmp_path): content = """ { - "image": { - "width": 497, - "height": 778, - "original_filename": "P49-RediPad-ProPlayLEFTY_442.jpg", - "filename": "P49-RediPad-ProPlayLEFTY_442.jpg", - "url": "", - "path": "/tmp_files" + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "P49-RediPad-ProPlayLEFTY_442.jpg", + "path": "/tmp_files", + "slots": [ + { + "type": "image", + "slot_name": "0", + "width": 497, + "height": 778, + "source_files": [ + { + "file_name": "P49-RediPad-ProPlayLEFTY_442.jpg", + "url": "" + } + ] + } + ] }, "annotations": [ { - "keypoint": { - "x": 207.97048950195312, - "y": 449.39691162109375 - }, - "name": "left_knee" + "id": "unique_id_1", + "name": "left_knee", + "keypoint": { + "x": 207.97048950195312, + "y": 449.39691162109375 + }, + "slot_names": [ + "0" + ] }, { - "keypoint": { - "x": 302.9606018066406, - "y": 426.13946533203125 - }, - "name": "left_ankle" + "id": "unique_id_2", + "name": "left_ankle", + "keypoint": { + "x": 302.9606018066406, + "y": 426.13946533203125 + }, + "slot_names": [ + "0" + ] } ] - } + } """ directory = tmp_path / "imports" @@ -135,7 +155,7 @@ def test_parses_darwin_images_correctly(self, tmp_path): assert annotation_file.filename == "P49-RediPad-ProPlayLEFTY_442.jpg" assert annotation_file.dataset_name is None assert annotation_file.version == dt.AnnotationFileVersion( - major=1, minor=0, suffix="" + major=2, minor=0, suffix="" ) assert len(annotation_file.annotations) == 2 @@ -159,10 +179,7 @@ def test_parses_darwin_videos_correctly(self, tmp_path): "path": "/", "source_info": { "item_id": "018a4ad2-41cb-5b6a-8141-fe1afeb65746", - "team": { - "name": "Test Team", - "slug": "test-team" - }, + "team": {"name": "Test Team", "slug": "test-team"}, "dataset": { "name": "My dataset", "slug": "my-dataset", @@ -185,9 +202,7 @@ def test_parses_darwin_videos_correctly(self, tmp_path): } ], "frame_count": 343, - "frame_urls": [ - "https://my-website.com/api/videos/209/frames/0" - ] + "frame_urls": ["https://my-website.com/api/videos/209/frames/0"] } ] }, @@ -195,30 +210,16 @@ def test_parses_darwin_videos_correctly(self, tmp_path): { "frames": { "3": { - "bounding_box": { - "h": 547.0, - "w": 400.0, - "x": 363.0, - "y": 701.0 - }, - "instance_id": { - "value": 119 - }, + "bounding_box": {"h": 547.0, "w": 400.0, "x": 363.0, "y": 701.0}, + "instance_id": {"value": 119}, "keyframe": true, "polygon": { "paths": [ - { - "x": 748.0, - "y": 732.0 - }, - { - "x": 751.0, - "y": 735.0 - }, - { - "x": 748.0, - "y": 733.0 - } + [ + {"x": 748.0, "y": 732.0}, + {"x": 751.0, "y": 735.0}, + {"x": 748.0, "y": 733.0} + ] ] } } @@ -227,21 +228,9 @@ def test_parses_darwin_videos_correctly(self, tmp_path): "interpolate_algorithm": "linear-1.1", "interpolated": true, "name": "Hand", - "ranges": [ - [ - 3, - 46 - ] - ], - "hidden_areas": [ - [ - 5, - 8 - ] - ], - "slot_names": [ - "0" - ] + "ranges": [[3, 46]], + "hidden_areas": [[5, 8]], + "slot_names": ["0"] } ] } @@ -284,21 +273,23 @@ def test_parses_darwin_videos_correctly(self, tmp_path): dt.VideoAnnotation( annotation_class=dt.AnnotationClass( name="Hand", - annotation_type="complex_polygon", + annotation_type="polygon", annotation_internal_type="polygon", ), frames={ 3: dt.Annotation( annotation_class=dt.AnnotationClass( name="Hand", - annotation_type="complex_polygon", + annotation_type="polygon", annotation_internal_type="polygon", ), data={ "paths": [ - {"x": 748.0, "y": 732.0}, - {"x": 751.0, "y": 735.0}, - {"x": 748.0, "y": 733.0}, + [ + {"x": 748.0, "y": 732.0}, + {"x": 751.0, "y": 735.0}, + {"x": 748.0, "y": 733.0}, + ] ], "bounding_box": { "x": 363.0, @@ -573,21 +564,56 @@ def test_returns_None_if_no_annotations_exist(self, tmp_path): def test_uses_a_default_path_if_one_is_missing(self, tmp_path): content = """ + { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "P49-RediPad-ProPlayLEFTY_442.jpg", + "path": "/", + "source_info": { + "item_id": "unknown", + "dataset": { + "name": "unknown", + "slug": "unknown", + "dataset_management_url": "unknown" + }, + "team": { + "name": "unknown", + "slug": "unknown" + }, + "workview_url": "unknown" + }, + "slots": [ { - "image": { - "original_filename": "P49-RediPad-ProPlayLEFTY_442.jpg", - "filename": "P49-RediPad-ProPlayLEFTY_442.jpg" - }, - "annotations": [ - { - "keypoint": { - "x": 207.97048950195312, - "y": 449.39691162109375 - }, - "name": "left_knee" - } + "type": "image", + "slot_name": "0", + "width": 640, + "height": 425, + "thumbnail_url": "unknown", + "source_files": [ + { + "file_name": "P49-RediPad-ProPlayLEFTY_442.jpg", + "url": "unknown" + } ] } + ] + }, + "annotations": [ + { + "id": "unknown", + "name": "left_knee", + "properties": [], + "keypoint": { + "x": 207.97048950195312, + "y": 449.39691162109375 + }, + "slot_names": [ + "0" + ] + } + ] + } """ directory = tmp_path / "imports" @@ -601,58 +627,148 @@ def test_uses_a_default_path_if_one_is_missing(self, tmp_path): def test_imports_a_skeleton(self, tmp_path): content = """ + { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "ferrari-laferrari.jpg", + "path": "/", + "source_info": { + "item_id": "018c4450-d91d-ff3e-b226-60d48b66f86e", + "dataset": { + "name": "bbox", + "slug": "bbox", + "dataset_management_url": "https://darwin.v7labs.com/datasets/623079/dataset-management" + }, + "team": { + "name": "V7 John", + "slug": "v7-john" + }, + "workview_url": "https://darwin.v7labs.com/workview?dataset=623079&item=018c4450-d91d-ff3e-b226-60d48b66f86e" + }, + "slots": [ { - "dataset": "cars", - "image": { - "filename": "ferrari-laferrari.jpg" - }, - "annotations": [ + "type": "image", + "slot_name": "0", + "width": 640, + "height": 425, + "thumbnail_url": "https://darwin.v7labs.com/api/v2/teams/v7-john/files/ddc5cbc2-8438-4e36-8ab6-43e2f3746bf1/thumbnail", + "source_files": [ + { + "file_name": "000000007751.jpg", + "url": "https://darwin.v7labs.com/api/v2/teams/v7-john/uploads/3395d29a-7539-4a51-a3ca-c7a95f460345" + } + ] + } + ] + }, + "annotations": [ + { + "bounding_box": { + "h": 53.963699999999996, + "w": 83.7195, + "x": 32.7817, + "y": 53.9638 + }, + "id": "8940a690-d8a9-4c83-9f59-38f0ef780246", + "name": "new-class-2", + "polygon": { + "paths": [ + [ { - "bounding_box": { - "h": 547.0, - "w": 1709.0, - "x": 96.0, - "y": 437.0 - }, - "name": "car", - "polygon": { - "paths": [ - { - "x": 1805.0, - "y": 586.0 - }, - { - "x": 1802.0, - "y": 586.0 - }, - { - "x": 1805.0, - "y": 588.0 - } - ] - } + "x": 65.0591, + "y": 53.9638 }, { - "name": "wheels", - "skeleton": { - "nodes": [ - { - "name": "1", - "occluded": false, - "x": 829.56, - "y": 824.5 - }, - { - "name": "2", - "occluded": false, - "x": 1670.5, - "y": 741.76 - } - ] - } + "x": 32.7817, + "y": 107.9275 + }, + { + "x": 116.5012, + "y": 104.9015 } ] + ] + }, + "properties": [], + "slot_names": [ + "0" + ] + }, + { + "id": "782618fb-4c69-436e-80cb-71765d255dbf", + "name": "skeleton-test", + "properties": [], + "skeleton": { + "nodes": [ + { + "name": "node", + "occluded": false, + "x": 264.7754, + "y": 121.5445 + }, + { + "name": "2", + "occluded": false, + "x": 245.1335, + "y": 107.3425 + }, + { + "name": "3", + "occluded": false, + "x": 240.4646, + "y": 125.4178 + }, + { + "name": "4", + "occluded": false, + "x": 280.3923, + "y": 137.468 + } + ] + }, + "slot_names": [ + "0" + ] + }, + { + "id": "b6bea00c-c8a4-4d34-b72f-88567d9e8cd5", + "name": "skeleton-test", + "properties": [], + "skeleton": { + "nodes": [ + { + "name": "node", + "occluded": false, + "x": 136.1702, + "y": 306.1308 + }, + { + "name": "2", + "occluded": false, + "x": 145.1629, + "y": 291.263 + }, + { + "name": "3", + "occluded": false, + "x": 147.3005, + "y": 310.1857 + }, + { + "name": "4", + "occluded": false, + "x": 129.0203, + "y": 322.8007 + } + ] + }, + "slot_names": [ + "0" + ] } + ] + } """ directory = tmp_path / "imports" @@ -672,89 +788,148 @@ def test_imports_a_skeleton(self, tmp_path): def test_imports_multiple_skeletetons(self, tmp_path): content = """ + { + "version": "2.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json", + "item": { + "name": "ferrari-laferrari.jpg", + "path": "/", + "source_info": { + "item_id": "018c4450-d91d-ff3e-b226-60d48b66f86e", + "dataset": { + "name": "bbox", + "slug": "bbox", + "dataset_management_url": "https://darwin.v7labs.com/datasets/623079/dataset-management" + }, + "team": { + "name": "V7 John", + "slug": "v7-john" + }, + "workview_url": "https://darwin.v7labs.com/workview?dataset=623079&item=018c4450-d91d-ff3e-b226-60d48b66f86e" + }, + "slots": [ { - "dataset":"cars", - "image":{ - "filename":"ferrari-laferrari.jpg" - }, - "annotations":[ + "type": "image", + "slot_name": "0", + "width": 640, + "height": 425, + "thumbnail_url": "https://darwin.v7labs.com/api/v2/teams/v7-john/files/ddc5cbc2-8438-4e36-8ab6-43e2f3746bf1/thumbnail", + "source_files": [ + { + "file_name": "000000007751.jpg", + "url": "https://darwin.v7labs.com/api/v2/teams/v7-john/uploads/3395d29a-7539-4a51-a3ca-c7a95f460345" + } + ] + } + ] + }, + "annotations": [ + { + "bounding_box": { + "h": 53.963699999999996, + "w": 83.7195, + "x": 32.7817, + "y": 53.9638 + }, + "id": "8940a690-d8a9-4c83-9f59-38f0ef780246", + "name": "new-class-2", + "polygon": { + "paths": [ + [ { - "bounding_box":{ - "h":547.0, - "w":1709.0, - "x":96.0, - "y":437.0 - }, - "name":"car", - "polygon":{ - "paths":[ - { - "x":1805.0, - "y":586.0 - }, - { - "x":1802.0, - "y":586.0 - }, - { - "x":1805.0, - "y":588.0 - } - ] - } + "x": 65.0591, + "y": 53.9638 }, { - "name":"wheels", - "skeleton":{ - "nodes":[ - { - "name":"1", - "occluded":false, - "x":829.56, - "y":824.5 - }, - { - "name":"2", - "occluded":false, - "x":1670.5, - "y":741.76 - } - ] - } + "x": 32.7817, + "y": 107.9275 }, { - "name":"door", - "skeleton":{ - "nodes":[ - { - "name":"1", - "occluded":false, - "x":867.86, - "y":637.16 - }, - { - "name":"2", - "occluded":false, - "x":1100.21, - "y":810.09 - }, - { - "name":"3", - "occluded":false, - "x":1298.45, - "y":856.56 - }, - { - "name":"4", - "occluded":false, - "x":1234.63, - "y":492.12 - } - ] - } + "x": 116.5012, + "y": 104.9015 } ] + ] + }, + "properties": [], + "slot_names": [ + "0" + ] + }, + { + "id": "782618fb-4c69-436e-80cb-71765d255dbf", + "name": "skeleton-test", + "properties": [], + "skeleton": { + "nodes": [ + { + "name": "node", + "occluded": false, + "x": 264.7754, + "y": 121.5445 + }, + { + "name": "2", + "occluded": false, + "x": 245.1335, + "y": 107.3425 + }, + { + "name": "3", + "occluded": false, + "x": 240.4646, + "y": 125.4178 + }, + { + "name": "4", + "occluded": false, + "x": 280.3923, + "y": 137.468 + } + ] + }, + "slot_names": [ + "0" + ] + }, + { + "id": "b6bea00c-c8a4-4d34-b72f-88567d9e8cd5", + "name": "skeleton-test", + "properties": [], + "skeleton": { + "nodes": [ + { + "name": "node", + "occluded": false, + "x": 136.1702, + "y": 306.1308 + }, + { + "name": "2", + "occluded": false, + "x": 145.1629, + "y": 291.263 + }, + { + "name": "3", + "occluded": false, + "x": 147.3005, + "y": 310.1857 + }, + { + "name": "4", + "occluded": false, + "x": 129.0203, + "y": 322.8007 + } + ] + }, + "slot_names": [ + "0" + ] } + ] + } """ directory = tmp_path / "imports" From 143292c042a1f6e88ce3bc7f3a18d56735c0ceb0 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 14 Mar 2024 13:31:07 +0000 Subject: [PATCH 15/28] Moved ObjectStore tests to datatypes_test --- darwin/torch/dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/torch/dataset.py b/darwin/torch/dataset.py index 256a519db..4e7c5c331 100644 --- a/darwin/torch/dataset.py +++ b/darwin/torch/dataset.py @@ -326,6 +326,7 @@ def get_target(self, index: int) -> Dict[str, Any]: annotations = [] for annotation in target["annotations"]: + annotation_type: str = annotation.annotation_class.annotation_type # Darwin V2 only has paths (TODO it might be more robust fixes) if "paths" in annotation.data: From 7258d447916af35c7b2b2a2057f5fb9e4848ec27 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 14 Mar 2024 14:33:48 +0000 Subject: [PATCH 16/28] Fixed video annotation import test --- tests/darwin/utils_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/darwin/utils_test.py b/tests/darwin/utils_test.py index 1c9dcb872..6f7e16459 100644 --- a/tests/darwin/utils_test.py +++ b/tests/darwin/utils_test.py @@ -312,6 +312,11 @@ def test_parses_darwin_videos_correctly(self, tmp_path): segments=[[3, 46]], hidden_areas=[[5, 8]], interpolated=True, + slot_names=["0"], + annotators=None, + reviewers=None, + id="f8f5f235-bd47-47be-b4fe-07d49e0177a7", + properties=None, ) ] From e5dc371881199f7e57e7f37436be4213c94a0a3a Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 14 Mar 2024 14:50:24 +0000 Subject: [PATCH 17/28] Added support for simple table annotations incase exports are compressed to avoid OOM --- darwin/torch/dataset.py | 1 - darwin/utils/utils.py | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/darwin/torch/dataset.py b/darwin/torch/dataset.py index 4e7c5c331..256a519db 100644 --- a/darwin/torch/dataset.py +++ b/darwin/torch/dataset.py @@ -326,7 +326,6 @@ def get_target(self, index: int) -> Dict[str, Any]: annotations = [] for annotation in target["annotations"]: - annotation_type: str = annotation.annotation_class.annotation_type # Darwin V2 only has paths (TODO it might be more robust fixes) if "paths" in annotation.data: diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index fb00ff84e..1f8ec5d37 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -841,6 +841,13 @@ def make_keyframe_annotation( return dt.make_table( name, annotation_data["bounding_box"], annotation_data["cells"] ) + elif annotation_type == "simple_table": + return dt.make_simple_table( + name, + annotation_data["bounding_box"], + annotation_data["col_offsets"], + annotation_data["row_offsets"], + ) elif annotation_type == "string": return dt.make_string(name, annotation_data["sources"]) elif annotation_type == "graph": From 30c37b0dd2ef9dc9b0ac291ab1007310b13bd1f9 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 14 Mar 2024 15:40:38 +0000 Subject: [PATCH 18/28] Small fix for importing the raster layer --- darwin/utils/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/darwin/utils/utils.py b/darwin/utils/utils.py index 1f8ec5d37..05a36aa2c 100644 --- a/darwin/utils/utils.py +++ b/darwin/utils/utils.py @@ -919,11 +919,12 @@ def update_annotation_data( elif annotation_type == "mask": annotation_data = {} elif annotation_type == "raster_layer": - raster_layer = main_annotation_data["raster_layer"] annotation_data = { - "dense_rle": raster_layer["dense_rle"], - "mask_annotation_ids_mapping": raster_layer["mask_annotation_ids_mapping"], - "total_pixels": raster_layer["total_pixels"], + "dense_rle": main_annotation_data["dense_rle"], + "mask_annotation_ids_mapping": main_annotation_data[ + "mask_annotation_ids_mapping" + ], + "total_pixels": main_annotation_data["total_pixels"], } return annotation_data From 8abd1616c9fc31e784a93c79ae294f270ededb60 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Thu, 14 Mar 2024 16:06:19 +0000 Subject: [PATCH 19/28] Removed 2 unused functions --- darwin/dataset/download_manager.py | 104 ----------------------------- 1 file changed, 104 deletions(-) diff --git a/darwin/dataset/download_manager.py b/darwin/dataset/download_manager.py index 6fb0d400c..d96a164a1 100644 --- a/darwin/dataset/download_manager.py +++ b/darwin/dataset/download_manager.py @@ -144,64 +144,6 @@ def download_all_images_from_annotations( return lambda: download_functions, len(download_functions) -def download_image_from_annotation( - api_key: str, - api_url: str, - annotation_path: Path, - images_path: Path, - annotation_format: str, - use_folders: bool, - video_frames: bool, - force_slots: bool, - ignore_slots: bool = False, -) -> None: - """ - Dispatches functions to download an image given an annotation. - - Parameters - ---------- - api_key : str - API Key of the current team - api_url : str - Url of the darwin API (e.g. 'https://darwin.v7labs.com/api/') - annotation_path : Path - Path where the annotation is located - images_path : Path - Path where to download the image - annotation_format : str - Format of the annotations. Currently only JSON is supported - use_folders : bool - Recreate folder structure - video_frames : bool - Pulls video frames images instead of video files - force_slots: bool - Pulls all slots of items into deeper file structure ({prefix}/{item_name}/{slot_name}/{file_name}) - - Raises - ------ - NotImplementedError - If the format of the annotation is not supported. - """ - - console = Console() - - if annotation_format == "json": - downloadables = _download_image_from_json_annotation( - api_key, - annotation_path, - images_path, - use_folders, - video_frames, - force_slots, - ignore_slots, - ) - for downloadable in downloadables: - downloadable() - else: - console.print("[bold red]Unsupported file format. Please use 'json'.") - raise NotImplementedError - - def lazy_download_image_from_annotation( api_key: str, annotation_path: Path, @@ -440,52 +382,6 @@ def _update_local_path(annotation: AnnotationFile, url, local_path): file.write(op) -def download_image_from_json_annotation( - api_key: str, - api_url: str, - annotation_path: Path, - image_path: Path, - use_folders: bool, - video_frames: bool, -) -> None: - """ - Downloads an image given a ``.json`` annotation path and renames the json after the image's - filename. - - Parameters - ---------- - api_key : str - API Key of the current team - api_url : str - Url of the darwin API (e.g. 'https://darwin.v7labs.com/api/') - annotation_path : Path - Path where the annotation is located - image_path : Path - Path where to download the image - use_folders : bool - Recreate folders - video_frames : bool - Pulls video frames images instead of video files - """ - annotation = attempt_decode(annotation_path) - - # If we are using folders, extract the path for the image and create the folder if needed - sub_path = annotation["image"].get("path", "/") if use_folders else "/" - parent_path = Path(image_path) / Path(sub_path).relative_to(Path(sub_path).anchor) - parent_path.mkdir(exist_ok=True, parents=True) - - if video_frames and "frame_urls" in annotation["image"]: - video_path: Path = parent_path / annotation_path.stem - video_path.mkdir(exist_ok=True, parents=True) - for i, frame_url in enumerate(annotation["image"]["frame_urls"]): - path = video_path / f"{i:07d}.png" - _download_image(frame_url, path, api_key) - else: - image_url = annotation["image"]["url"] - image_path = parent_path / sanitize_filename(annotation["image"]["filename"]) - _download_image(image_url, image_path, api_key) - - def _download_image( url: str, path: Path, api_key: str, slot: Optional[dt.Slot] = None ) -> None: From 61fc286f9a66d0b580408194709c00b0147ee026 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 11:51:53 +0100 Subject: [PATCH 20/28] Removed deprecated api_url parameter --- darwin/dataset/download_manager.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/darwin/dataset/download_manager.py b/darwin/dataset/download_manager.py index d96a164a1..e6cc50cbb 100644 --- a/darwin/dataset/download_manager.py +++ b/darwin/dataset/download_manager.py @@ -31,7 +31,6 @@ def download_all_images_from_annotations( api_key: str, - api_url: str, annotations_path: Path, images_path: Path, force_replace: bool = False, @@ -49,8 +48,6 @@ def download_all_images_from_annotations( ---------- api_key : str API Key of the current team - api_url : str - Url of the darwin API (e.g. 'https://darwin.v7labs.com/api/') annotations_path : Path Path where the annotations are located images_path : Path From 24345f5977fdf0a7e51e68bc869fc4cfd9cc0d44 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 12:02:01 +0100 Subject: [PATCH 21/28] Replace lambda with itemgetter() for improved performance --- darwin/exporter/formats/coco.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index d4785067e..5be6a50ff 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -1,4 +1,5 @@ from datetime import date +from operator import itemgetter from pathlib import Path from typing import Any, Dict, Iterator, List, Optional from zlib import crc32 @@ -66,7 +67,7 @@ def _calculate_categories(annotation_files: List[dt.AnnotationFile]) -> Dict[str categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return dict(sorted(categories.items(), key=lambda item: item[1])) + return dict(sorted(categories.items(), key=itemgetter(1))) def _calculate_tag_categories( @@ -82,7 +83,7 @@ def _calculate_tag_categories( categories[annotation_class.name] = _calculate_category_id( annotation_class ) - return dict(sorted(categories.items(), key=lambda item: item[1])) + return dict(sorted(categories.items(), key=itemgetter(1))) def _calculate_category_id(annotation_class: dt.AnnotationClass) -> int: From 25b2110fca9f085df92cf9a16f6f1afa99ee0613 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 12:10:31 +0100 Subject: [PATCH 22/28] Turned 4 functions private --- darwin/importer/importer.py | 8 ++++---- tests/darwin/importer/importer_mcpu_test.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 7e342a6fd..dd3303e22 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -69,7 +69,7 @@ """ -def build_main_annotations_lookup_table( +def _build_main_annotations_lookup_table( annotation_classes: List[Dict[str, Unknown]] ) -> Dict[str, Unknown]: MAIN_ANNOTATION_TYPES = [ @@ -100,7 +100,7 @@ def build_main_annotations_lookup_table( return lookup -def find_and_parse( # noqa: C901 +def _find_and_parse( # noqa: C901 importer: Callable[[Path], Union[List[dt.AnnotationFile], dt.AnnotationFile, None]], file_paths: List[PathLike], console: Optional[Console] = None, @@ -168,7 +168,7 @@ def _get_files_for_parsing(file_paths: List[PathLike]) -> List[Path]: return [file for files in packed_files for file in files] -def build_attribute_lookup(dataset: "RemoteDataset") -> Dict[str, Unknown]: +def _build_attribute_lookup(dataset: "RemoteDataset") -> Dict[str, Unknown]: attributes: List[Dict[str, Unknown]] = dataset.fetch_remote_attributes() lookup: Dict[str, Unknown] = {} for attribute in attributes: @@ -179,7 +179,7 @@ def build_attribute_lookup(dataset: "RemoteDataset") -> Dict[str, Unknown]: return lookup -def get_remote_files( +def _get_remote_files( dataset: "RemoteDataset", filenames: List[str], chunk_size: int = 100 ) -> Dict[str, Tuple[int, str]]: """ diff --git a/tests/darwin/importer/importer_mcpu_test.py b/tests/darwin/importer/importer_mcpu_test.py index ce7528325..9168fc5ea 100644 --- a/tests/darwin/importer/importer_mcpu_test.py +++ b/tests/darwin/importer/importer_mcpu_test.py @@ -99,7 +99,7 @@ def tearDown(self) -> None: def test_uses_mpire_if_use_multi_cpu_true( self, mock_wp: MagicMock, mock_gffp: MagicMock, mock_gmcus: MagicMock ) -> None: - from darwin.importer.importer import find_and_parse + from darwin.importer.importer import _find_and_parse mock_gmcus.return_value = (2, True) mock_gffp.return_value = [ @@ -123,7 +123,7 @@ def __exit__(self, *args) -> None: # type: ignore mock_wp.return_value = MockWorkerPool() mock_map.return_value = ["1", "2"] - result = find_and_parse( + result = _find_and_parse( mock_importer, [Path("example_dir")], self.mock_console, True, 2 ) @@ -138,7 +138,7 @@ def __exit__(self, *args) -> None: # type: ignore def test_runs_single_threaded_if_use_multi_cpu_false( self, mock_wp: MagicMock, mock_gffp: MagicMock ) -> None: - from darwin.importer.importer import find_and_parse + from darwin.importer.importer import _find_and_parse mock_gffp.return_value = [ Path("example_dir/file1.txt"), @@ -148,7 +148,7 @@ def test_runs_single_threaded_if_use_multi_cpu_false( mock_importer = MagicMock() mock_importer.side_effect = ["1", "2"] - result = find_and_parse( + result = _find_and_parse( mock_importer, [Path("example_dir")], self.mock_console, False ) @@ -163,7 +163,7 @@ def test_runs_single_threaded_if_use_multi_cpu_false( def test_returns_list_if_solo_value( self, mock_wp: MagicMock, mock_gffp: MagicMock, mock_gmcus: MagicMock ) -> None: - from darwin.importer.importer import find_and_parse + from darwin.importer.importer import _find_and_parse mock_gmcus.return_value = (2, True) mock_gffp.return_value = [ @@ -187,7 +187,7 @@ def __exit__(self, *args) -> None: # type: ignore mock_wp.return_value = MockWorkerPool() mock_map.return_value = "1" - result = find_and_parse( + result = _find_and_parse( mock_importer, [Path("example_dir")], self.mock_console, True, 2 ) @@ -203,7 +203,7 @@ def __exit__(self, *args) -> None: # type: ignore def test_returns_none_if_pool_raises_error( self, mock_wp: MagicMock, mock_gffp: MagicMock, mock_gmcus: MagicMock ) -> None: - from darwin.importer.importer import find_and_parse + from darwin.importer.importer import _find_and_parse mock_gmcus.return_value = (2, True) mock_gffp.return_value = [ @@ -227,7 +227,7 @@ def __exit__(self, *args) -> None: # type: ignore mock_wp.return_value = MockWorkerPool() mock_map.side_effect = Exception("Test") - result = find_and_parse( + result = _find_and_parse( mock_importer, [Path("example_dir")], self.mock_console, True, 2 ) From 1e910284ff997668550932e90897e37eda550c2a Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 12:13:30 +0100 Subject: [PATCH 23/28] Removed deprecated api_url parameter from function call --- darwin/dataset/remote_dataset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/darwin/dataset/remote_dataset.py b/darwin/dataset/remote_dataset.py index b4e28e960..ffb64aa2e 100644 --- a/darwin/dataset/remote_dataset.py +++ b/darwin/dataset/remote_dataset.py @@ -338,7 +338,6 @@ def pull( # Create the generator with the download instructions progress, count = download_all_images_from_annotations( api_key=api_key, - api_url=self.client.url, annotations_path=annotations_dir, images_path=self.local_images_path, force_replace=force_replace, From a2c9d6f11c87c2eadac8d4abbdac7fa74c1d05ee Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 14:38:44 +0100 Subject: [PATCH 24/28] Fixed private function calls --- darwin/importer/importer.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index dd3303e22..4a61077fe 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -255,9 +255,9 @@ def _get_team_properties_annotation_lookup(client): team_properties = client.get_team_properties() # (property-name, annotation_class_id): FullProperty object - team_properties_annotation_lookup: Dict[ - Tuple[str, Optional[int]], FullProperty - ] = {} + team_properties_annotation_lookup: Dict[Tuple[str, Optional[int]], FullProperty] = ( + {} + ) for prop in team_properties: team_properties_annotation_lookup[(prop.name, prop.annotation_class_id)] = prop @@ -719,28 +719,28 @@ def import_annotations( # noqa: C901 if not team_classes: raise ValueError("Unable to fetch remote class list.") - classes_in_dataset: dt.DictFreeForm = build_main_annotations_lookup_table( + classes_in_dataset: dt.DictFreeForm = _build_main_annotations_lookup_table( [ cls for cls in team_classes if cls["available"] or cls["name"] in GLOBAL_CLASSES ] ) - classes_in_team: dt.DictFreeForm = build_main_annotations_lookup_table( + classes_in_team: dt.DictFreeForm = _build_main_annotations_lookup_table( [ cls for cls in team_classes if not cls["available"] and cls["name"] not in GLOBAL_CLASSES ] ) - attributes = build_attribute_lookup(dataset) + attributes = _build_attribute_lookup(dataset) console.print("Retrieving local annotations ...", style="info") local_files = [] local_files_missing_remotely = [] # ! Other place we can use multiprocessing - hard to pass in the importer though - maybe_parsed_files: Optional[Iterable[dt.AnnotationFile]] = find_and_parse( + maybe_parsed_files: Optional[Iterable[dt.AnnotationFile]] = _find_and_parse( importer, file_paths, console, use_multi_cpu, cpu_limit ) @@ -763,7 +763,7 @@ def import_annotations( # noqa: C901 chunk_size = 100 while chunk_size > 0: try: - remote_files = get_remote_files(dataset, filenames, chunk_size) + remote_files = _get_remote_files(dataset, filenames, chunk_size) break except RequestEntitySizeExceeded: chunk_size -= 8 @@ -852,9 +852,9 @@ def import_annotations( # noqa: C901 if not maybe_remote_classes: raise ValueError("Unable to fetch remote classes.") - remote_classes = build_main_annotations_lookup_table(maybe_remote_classes) + remote_classes = _build_main_annotations_lookup_table(maybe_remote_classes) else: - remote_classes = build_main_annotations_lookup_table(team_classes) + remote_classes = _build_main_annotations_lookup_table(team_classes) if delete_for_empty: console.print( @@ -869,9 +869,9 @@ def import_annotations( # noqa: C901 # Need to re parse the files since we didn't save the annotations in memory for local_path in set(local_file.path for local_file in local_files): # noqa: C401 - imported_files: Union[ - List[dt.AnnotationFile], dt.AnnotationFile, None - ] = importer(local_path) + imported_files: Union[List[dt.AnnotationFile], dt.AnnotationFile, None] = ( + importer(local_path) + ) if imported_files is None: parsed_files = [] elif not isinstance(imported_files, List): @@ -1273,9 +1273,9 @@ def _import_annotations( # Insert the default slot name if not available in the import source annotation = _handle_slot_names(annotation, dataset.version, default_slot_name) - annotation_class_ids_map[ - (annotation_class.name, annotation_type) - ] = annotation_class_id + annotation_class_ids_map[(annotation_class.name, annotation_type)] = ( + annotation_class_id + ) serial_obj = { "annotation_class_id": annotation_class_id, "data": data, From 20f104ff20fe091fc815fbdaf766caaf889d66bc Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 14:39:43 +0100 Subject: [PATCH 25/28] Removed debugging print statements --- darwin/exporter/exporter.py | 1 - darwin/exporter/formats/coco.py | 1 - 2 files changed, 2 deletions(-) diff --git a/darwin/exporter/exporter.py b/darwin/exporter/exporter.py index 35a1e6161..8cf4d95c6 100644 --- a/darwin/exporter/exporter.py +++ b/darwin/exporter/exporter.py @@ -36,7 +36,6 @@ def darwin_to_dt_gen( for d in split_video_annotation(data): d.seq = count count += 1 - print(count) yield d else: yield data diff --git a/darwin/exporter/formats/coco.py b/darwin/exporter/formats/coco.py index 5be6a50ff..a66c1b53a 100644 --- a/darwin/exporter/formats/coco.py +++ b/darwin/exporter/formats/coco.py @@ -158,7 +158,6 @@ def _build_annotations( ) -> Iterator[Optional[Dict[str, Any]]]: annotation_id = 0 for annotation_file in annotation_files: - print(annotation_file.filename) for annotation in annotation_file.annotations: annotation_id += 1 annotation_data = _build_annotation( From 51d7e77ef4d63c5fd19354a77eb515e1b55ce2a9 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 14:40:32 +0100 Subject: [PATCH 26/28] Linting --- darwin/importer/importer.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 4a61077fe..95b49f583 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -255,9 +255,9 @@ def _get_team_properties_annotation_lookup(client): team_properties = client.get_team_properties() # (property-name, annotation_class_id): FullProperty object - team_properties_annotation_lookup: Dict[Tuple[str, Optional[int]], FullProperty] = ( - {} - ) + team_properties_annotation_lookup: Dict[ + Tuple[str, Optional[int]], FullProperty + ] = {} for prop in team_properties: team_properties_annotation_lookup[(prop.name, prop.annotation_class_id)] = prop @@ -869,9 +869,9 @@ def import_annotations( # noqa: C901 # Need to re parse the files since we didn't save the annotations in memory for local_path in set(local_file.path for local_file in local_files): # noqa: C401 - imported_files: Union[List[dt.AnnotationFile], dt.AnnotationFile, None] = ( - importer(local_path) - ) + imported_files: Union[ + List[dt.AnnotationFile], dt.AnnotationFile, None + ] = importer(local_path) if imported_files is None: parsed_files = [] elif not isinstance(imported_files, List): @@ -1273,9 +1273,9 @@ def _import_annotations( # Insert the default slot name if not available in the import source annotation = _handle_slot_names(annotation, dataset.version, default_slot_name) - annotation_class_ids_map[(annotation_class.name, annotation_type)] = ( - annotation_class_id - ) + annotation_class_ids_map[ + (annotation_class.name, annotation_type) + ] = annotation_class_id serial_obj = { "annotation_class_id": annotation_class_id, "data": data, From bae925f2c2fa2d86da19378ec40e3c69be07c524 Mon Sep 17 00:00:00 2001 From: John Wilkie <124276291+JBWilkie@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:01:25 +0100 Subject: [PATCH 27/28] Update darwin/datatypes.py Co-authored-by: saurbhc --- darwin/datatypes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/datatypes.py b/darwin/datatypes.py index b19a94fb0..185c47d11 100644 --- a/darwin/datatypes.py +++ b/darwin/datatypes.py @@ -687,7 +687,8 @@ def make_polygon( # Check if point_paths is List[Point] and convert to List[List[Point]] if ( - isinstance(point_paths[0], dict) + len(point_paths) > 1 + and isinstance(point_paths[0], dict) and "x" in point_paths[0] and "y" in point_paths[0] ): From 3bfa8157ad667a63f1e3b0b7b4eeae8ca1e25f2b Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Wed, 17 Apr 2024 15:05:14 +0100 Subject: [PATCH 28/28] docstring updates --- darwin/datatypes.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/darwin/datatypes.py b/darwin/datatypes.py index 185c47d11..d0e2f39e9 100644 --- a/darwin/datatypes.py +++ b/darwin/datatypes.py @@ -648,17 +648,26 @@ def make_polygon( slot_names: Optional[List[str]] = None, ) -> Annotation: """ - Creates and returns a complex polygon annotation. Complex polygons are those who have holes - and/or disform shapes. + Creates and returns a polygon annotation. Parameters ---------- class_name: str The name of the class for this ``Annotation``. - point_paths: List[List[Point]] - A list of lists points that comprises the complex polygon. This is needed as a complex - polygon can be effectively seen as a sum of multiple simple polygons. The list should have - a format similar to: + point_paths: List[List[Point]] | List[Point] + Either a list of points that comprises a polygon or a list of lists of points that comprises a complex polygon. + A complex polygon is a polygon that is defined by >1 path. + + A polygon should be defined by a List[Point] and have a format similar to: + + ... code-block:: python + + [ + {"x": 1, "y": 0}, + {"x": 2, "y": 1} + ] + + A complex polygon should be defined by a List[List[Point]] and have a format similar to: .. code-block:: python @@ -682,7 +691,7 @@ def make_polygon( Returns ------- Annotation - A complex polygon ``Annotation``. + A polygon ``Annotation``. """ # Check if point_paths is List[Point] and convert to List[List[Point]]