From 6bce5390c0c20f40afec419c3310c9cee9c191d5 Mon Sep 17 00:00:00 2001 From: Vaghinak Basentsyan Date: Wed, 11 Feb 2026 11:00:17 +0400 Subject: [PATCH] Update Pixel project handling --- docs/source/api_reference/api_metadata.rst | 2 +- docs/source/cli_client.rst | 2 +- docs/source/userguide/setup_project.rst | 4 +- docs/source/userguide/utilities.rst | 4 +- pytest.ini | 2 +- src/superannotate/__init__.py | 4 +- .../lib/app/analytics/aggregators.py | 8 +- src/superannotate/lib/app/analytics/common.py | 2 - src/superannotate/lib/app/helpers.py | 8 +- .../lib/app/input_converters/__init__.py | 2 - .../lib/app/input_converters/conversion.py | 58 +----- .../converters/baseStrategy.py | 37 ---- .../coco_converters/coco_converter.py | 45 +---- .../coco_converters/coco_to_sa_pixel.py | 166 ---------------- .../coco_converters/sa_pixel_to_coco.py | 57 ------ .../input_converters/converters/converters.py | 3 - .../labelbox_converters/labelbox_helper.py | 10 - .../labelbox_strategies.py | 42 ---- .../labelbox_to_sa_pixel.py | 99 ---------- .../converters/sa_json_helper.py | 15 -- .../sagemaker_to_sa_pixel.py | 126 ------------ .../supervisely_strategies.py | 7 - .../supervisely_to_sa_pixel.py | 91 --------- .../voc_converters/voc_strategies.py | 6 - .../voc_converters/voc_to_sa_pixel.py | 61 ------ .../export_from_sa_conversions.py | 10 +- .../lib/app/input_converters/sa_conversion.py | 182 ------------------ .../lib/app/interface/sdk_interface.py | 38 +--- src/superannotate/lib/core/__init__.py | 4 - .../lib/core/usecases/annotations.py | 73 +------ .../lib/core/usecases/classes.py | 33 ---- src/superannotate/lib/core/usecases/images.py | 50 +---- .../lib/infrastructure/controller.py | 71 +++---- .../test_annotation_upload_pixel.py | 77 -------- ...load_annotations_from_folder_to_project.py | 46 ----- .../classes/test_create_annotation_class.py | 35 ---- ...te_annotation_classes_from_classes_json.py | 39 ---- .../items/test_get_item_metadata.py | 20 -- tests/integration/items/test_saqul_query.py | 19 -- .../projects/test_basic_project.py | 107 ---------- .../projects/test_create_project.py | 2 +- .../projects/test_get_project_metadata.py | 1 - tests/integration/test_basic_images.py | 59 ------ tests/integration/test_cli.py | 7 - .../integration/test_convert_project_type.py | 44 ----- tests/integration/test_fuse_gen.py | 134 ------------- tests/integration/test_image_quality.py | 26 --- tests/integration/test_interface.py | 75 -------- 48 files changed, 58 insertions(+), 1955 deletions(-) delete mode 100644 src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_strategies.py delete mode 100644 src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_pixel.py delete mode 100644 src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_pixel.py delete mode 100644 src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_pixel.py delete mode 100644 tests/integration/annotations/test_annotation_upload_pixel.py delete mode 100644 tests/integration/test_convert_project_type.py delete mode 100644 tests/integration/test_fuse_gen.py diff --git a/docs/source/api_reference/api_metadata.rst b/docs/source/api_reference/api_metadata.rst index f6c2110b3..5dc78072d 100644 --- a/docs/source/api_reference/api_metadata.rst +++ b/docs/source/api_reference/api_metadata.rst @@ -18,7 +18,7 @@ Project metadata example: "creator_id": "admin@superannotate.com", "updatedAt": "2020-08-31T05:43:43.118Z", "createdAt": "2020-08-31T05:43:43.118Z" - "type": "Vector", # Pixel, Video, Multimodal + "type": "Vector", # Video, Multimodal "attachment_name": None, "attachment_path": None, "entropy_status": 1, diff --git a/docs/source/cli_client.rst b/docs/source/cli_client.rst index 704ed1ec0..6fb5c7ccf 100644 --- a/docs/source/cli_client.rst +++ b/docs/source/cli_client.rst @@ -42,7 +42,7 @@ To create a new project: .. code-block:: bash - superannotatecli create-project --name --description --type + superannotatecli create-project --name --description --type ---------- diff --git a/docs/source/userguide/setup_project.rst b/docs/source/userguide/setup_project.rst index e8af0e3e6..d0a0e48cf 100644 --- a/docs/source/userguide/setup_project.rst +++ b/docs/source/userguide/setup_project.rst @@ -249,9 +249,7 @@ you are uploading to should contain annotation class with that name. This will try uploading to the project all the JSON files in the folder that have :file:`".json"` postfix. -For pixel projects JSON files should be named :file:`"___pixel.json"` and also for -each JSON a mask image file should be present with the name -:file:`"___save.png"`. Image with :file:`` should +Image with :file:`` should already be present in the project for the upload to work. diff --git a/docs/source/userguide/utilities.rst b/docs/source/userguide/utilities.rst index 40349cdbd..076c7eb80 100644 --- a/docs/source/userguide/utilities.rst +++ b/docs/source/userguide/utilities.rst @@ -40,7 +40,7 @@ You can find more information annotation format conversion :ref:`here ___objects.json for each image. - 'Pixel' project creates ___pixel.jsons and ___save.png annotation mask for each image. :type project_type: str :param task: Task can be one of the following: ['panoptic_segmentation', 'instance_segmentation', 'keypoint_detection', 'object_detection']. (Default: "object_detection"). 'keypoint_detection' can be used to converts keypoints from/to available annotation format. 'panoptic_segmentation' will use panoptic mask for each image to generate bluemask for SuperAnnotate annotation format and use bluemask to generate panoptic mask for invert conversion. Panoptic masks should be in the input folder. - 'instance_segmentation' 'Pixel' project_type converts instance masks and 'Vector' project_type generates bounding boxes and polygons from instance masks. Masks should be in the input folder if it is 'Pixel' project_type. 'object_detection' converts objects from/to available annotation format :type task: str """ @@ -223,8 +209,6 @@ def export_annotation( _passes_value_sanity(values_info) if project_type == "Vector": extension = "___objects.json" - elif project_type == "Pixel": - extension = "___pixel.json" else: extension = ".json" with tempfile.TemporaryDirectory() as tmp_dir: @@ -264,8 +248,6 @@ def import_annotation( -------------------------------------- project_type task ============== ====================== - Pixel panoptic_segmentation - Pixel instance_segmentation Vector instance_segmentation Vector object_detection Vector keypoint_detection @@ -276,7 +258,6 @@ def import_annotation( -------------------------------------- project_type task ============== ====================== - Pixel instance_segmentation Vector instance_segmentation Vector object_detection ============== ====================== @@ -289,7 +270,6 @@ def import_annotation( Vector object_detection Vector instance_segmentation Vector vector_annotation - Pixel instance_segmentation ============== ====================== ============== ====================== @@ -311,7 +291,6 @@ def import_annotation( Vector keypoint_detection Vector vector_annotation Vector instance_segmentation - Pixel instance_segmentation ============== ====================== ============== ====================== @@ -329,7 +308,6 @@ def import_annotation( -------------------------------------- project_type task ============== ====================== - Pixel instance_segmentation Vector objcet_detection ============== ====================== @@ -368,15 +346,13 @@ def import_annotation( :type dataset_format: str :param dataset_name: Name of the json file in the input_dir, which should be converted. :type dataset_name: str - :param project_type: SuperAnnotate project type is either 'Vector' or 'Pixel' (Default: 'Vector') + :param project_type: SuperAnnotate project type is 'Vector' 'Vector' project creates ___objects.json for each image. - 'Pixel' project creates ___pixel.jsons and ___save.png annotation mask for each image. :type project_type: str :param task: Task can be one of the following: ['panoptic_segmentation', 'instance_segmentation', 'keypoint_detection', 'object_detection', 'vector_annotation']. (Default: "object_detection"). 'keypoint_detection' can be used to converts keypoints from/to available annotation format. 'panoptic_segmentation' will use panoptic mask for each image to generate bluemask for SuperAnnotate annotation format and use bluemask to generate panoptic mask for invert conversion. Panoptic masks should be in the input folder. - 'instance_segmentation' 'Pixel' project_type converts instance masks and 'Vector' project_type generates bounding boxes and polygons from instance masks. Masks should be in the input folder if it is 'Pixel' project_type. 'object_detection' converts objects from/to available annotation format 'vector_annotation' can be used to convert all annotations (point, ellipse, circule, cuboid and etc) to SuperAnnotate vector project. :param images_root: Additonal path to images directory in input_dir @@ -427,29 +403,3 @@ def import_annotation( _passes_converter_sanity(args, "import") import_to_sa(args) - - -@Tracker -def convert_project_type( - input_dir: Union[str, Path], - output_dir: Union[str, Path], - convert_to: Literal["Vector", "Pixel"], -): - """Converts SuperAnnotate 'Vector' project type to 'Pixel' or reverse. - - :param input_dir: Path to the dataset folder that you want to convert. - :type input_dir: Pathlike(str or Path) - :param output_dir: Path to the folder where you want to have converted files. - :type output_dir: Pathlike(str or Path) - :param convert_to: the project type to which the current project should be converted. - :type convert_to: str - """ - params_info = [ - (input_dir, "input_dir", (str, Path)), - (output_dir, "output_dir", (str, Path)), - ] - _passes_type_sanity(params_info) - - input_dir, output_dir = _change_type(input_dir, output_dir) - - sa_convert_project_type(input_dir, output_dir, convert_to) diff --git a/src/superannotate/lib/app/input_converters/converters/baseStrategy.py b/src/superannotate/lib/app/input_converters/converters/baseStrategy.py index c9a23a6fa..71f55f23f 100644 --- a/src/superannotate/lib/app/input_converters/converters/baseStrategy.py +++ b/src/superannotate/lib/app/input_converters/converters/baseStrategy.py @@ -2,37 +2,23 @@ """ import logging -from .coco_converters.coco_to_sa_pixel import coco_instance_segmentation_to_sa_pixel -from .coco_converters.coco_to_sa_pixel import coco_panoptic_segmentation_to_sa_pixel from .coco_converters.coco_to_sa_vector import coco_instance_segmentation_to_sa_vector from .coco_converters.coco_to_sa_vector import coco_keypoint_detection_to_sa_vector from .coco_converters.coco_to_sa_vector import coco_object_detection_to_sa_vector -from .coco_converters.sa_pixel_to_coco import sa_pixel_to_coco_instance_segmentation -from .coco_converters.sa_pixel_to_coco import sa_pixel_to_coco_panoptic_segmentation from .coco_converters.sa_vector_to_coco import sa_vector_to_coco_instance_segmentation from .coco_converters.sa_vector_to_coco import sa_vector_to_coco_keypoint_detection from .coco_converters.sa_vector_to_coco import sa_vector_to_coco_object_detection from .dataloop_converters.dataloop_to_sa_vector import dataloop_to_sa from .googlecloud_converters.googlecloud_to_sa_vector import googlecloud_to_sa_vector -from .labelbox_converters.labelbox_to_sa_pixel import ( - labelbox_instance_segmentation_to_sa_pixel, -) from .labelbox_converters.labelbox_to_sa_vector import labelbox_to_sa -from .sagemaker_converters.sagemaker_to_sa_pixel import ( - sagemaker_instance_segmentation_to_sa_pixel, -) from .sagemaker_converters.sagemaker_to_sa_vector import ( sagemaker_object_detection_to_sa_vector, ) -from .supervisely_converters.supervisely_to_sa_pixel import ( - supervisely_instance_segmentation_to_sa_pixel, -) from .supervisely_converters.supervisely_to_sa_vector import ( supervisely_keypoint_detection_to_sa_vector, ) from .supervisely_converters.supervisely_to_sa_vector import supervisely_to_sa from .vgg_converters.vgg_to_sa_vector import vgg_to_sa -from .voc_converters.voc_to_sa_pixel import voc_instance_segmentation_to_sa_pixel from .voc_converters.voc_to_sa_vector import voc_instance_segmentation_to_sa_vector from .voc_converters.voc_to_sa_vector import voc_object_detection_to_sa_vector from .vott_converters.vott_to_sa_vector import vott_to_sa @@ -48,17 +34,12 @@ "instance_segmentation": coco_instance_segmentation_to_sa_vector, "object_detection": coco_object_detection_to_sa_vector, }, - "Pixel": { - "panoptic_segmentation": coco_panoptic_segmentation_to_sa_pixel, - "instance_segmentation": coco_instance_segmentation_to_sa_pixel, - }, }, "VOC": { "Vector": { "object_detection": voc_object_detection_to_sa_vector, "instance_segmentation": voc_instance_segmentation_to_sa_vector, }, - "Pixel": {"instance_segmentation": voc_instance_segmentation_to_sa_pixel}, }, "LabelBox": { "Vector": { @@ -66,9 +47,6 @@ "instance_segmentation": labelbox_to_sa, "vector_annotation": labelbox_to_sa, }, - "Pixel": { - "instance_segmentation": labelbox_instance_segmentation_to_sa_pixel - }, }, "DataLoop": { "Vector": { @@ -76,7 +54,6 @@ "instance_segmentation": dataloop_to_sa, "vector_annotation": dataloop_to_sa, }, - "Pixel": {}, }, "Supervisely": { "Vector": { @@ -85,9 +62,6 @@ "object_detection": supervisely_to_sa, "keypoint_detection": supervisely_keypoint_detection_to_sa_vector, }, - "Pixel": { - "instance_segmentation": supervisely_instance_segmentation_to_sa_pixel - }, }, "VoTT": { "Vector": { @@ -95,13 +69,9 @@ "object_detection": vott_to_sa, "vector_annotation": vott_to_sa, }, - "Pixel": {}, }, "SageMaker": { "Vector": {"object_detection": sagemaker_object_detection_to_sa_vector}, - "Pixel": { - "instance_segmentation": sagemaker_instance_segmentation_to_sa_pixel - }, }, "VGG": { "Vector": { @@ -109,15 +79,12 @@ "instance_segmentation": vgg_to_sa, "vector_annotation": vgg_to_sa, }, - "Pixel": {}, }, "GoogleCloud": { "Vector": {"object_detection": googlecloud_to_sa_vector}, - "Pixel": {}, }, "YOLO": { "Vector": {"object_detection": yolo_object_detection_to_sa_vector}, - "Pixel": {}, }, }, "to": { @@ -127,10 +94,6 @@ "object_detection": sa_vector_to_coco_object_detection, "keypoint_detection": sa_vector_to_coco_keypoint_detection, }, - "Pixel": { - "panoptic_segmentation": sa_pixel_to_coco_panoptic_segmentation, - "instance_segmentation": sa_pixel_to_coco_instance_segmentation, - }, }, }, } diff --git a/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py b/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py index 6f2d3508f..a7b07b763 100644 --- a/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py +++ b/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py @@ -19,7 +19,6 @@ class CocoBaseStrategy(baseStrategy): project_type_to_json_ending = { - "pixel": "___pixel.json", "vector": "___objects.json", } @@ -97,8 +96,6 @@ def convert_from_old_sa_to_new(old_json_data, project_type): "annotatorEmail", "qaEmail", ] - if project_type == "pixel": - meta_keys.append("isSegmented") new_json_data["metadata"] = dict.fromkeys(meta_keys) @@ -179,12 +176,6 @@ def _parse_json_into_common_format(self, sa_annotation_json, fpath): sa_annotation_json["metadata"]["panoptic_mask"] = panoptic_mask - if self.project_type == "Pixel": - sa_annotation_json["metadata"]["sa_bluemask_path"] = str( - Path(self.export_root) - / (sa_annotation_json["metadata"]["name"] + "___save.png") - ) - if not isinstance( sa_annotation_json["metadata"].get("height", None), int ) or not isinstance(sa_annotation_json["metadata"].get("width", None), int): @@ -214,34 +205,6 @@ def get_image_dimensions(self, image_path): return img_height, img_width - def _prepare_single_image_commons_pixel(self, id_, metadata): - - ImgCommons = namedtuple( - "ImgCommons", ["image_info", "ann_mask", "sa_bluemask_rgb", "flat_mask"] - ) - sa_bluemask_path = metadata["sa_bluemask_path"] - - image_info = self._make_image_info( - metadata["name"], metadata["height"], metadata["width"], id_ - ) - - sa_bluemask_rgb = np.asarray( - Image.open(sa_bluemask_path).convert("RGB"), dtype=np.uint32 - ) - - ann_mask = np.zeros( - (image_info["height"], image_info["width"]), dtype=np.uint32 - ) - flat_mask = ( - (sa_bluemask_rgb[:, :, 0] << 16) - | (sa_bluemask_rgb[:, :, 1] << 8) - | (sa_bluemask_rgb[:, :, 2]) - ) - - res = ImgCommons(image_info, ann_mask, sa_bluemask_rgb, flat_mask) - - return res - def _prepare_single_image_commons_vector(self, id_, metadata): ImgCommons = namedtuple("ImgCommons", ["image_info"]) @@ -256,9 +219,7 @@ def _prepare_single_image_commons_vector(self, id_, metadata): def _prepare_single_image_commons(self, id_, metadata): res = None - if self.project_type == "Pixel": - res = self._prepare_single_image_commons_pixel(id_, metadata) - elif self.project_type == "Vector": + if self.project_type == "Vector": res = self._prepare_single_image_commons_vector(id_, metadata) return res @@ -302,9 +263,7 @@ def to_sa_format(self): def make_anno_json_generator(self): json_data = None - if self.project_type == "Pixel": - jsons = list(Path(self.export_root).glob("*pixel.json")) - elif self.project_type == "Vector": + if self.project_type == "Vector": jsons = list(Path(self.export_root).glob("*objects.json")) self.set_num_total_images(len(jsons)) diff --git a/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_pixel.py b/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_pixel.py index 7a0d83692..85a7206da 100644 --- a/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_pixel.py +++ b/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_pixel.py @@ -1,21 +1,8 @@ """ COCO to SA conversion method """ -import json import logging -import threading -from pathlib import Path -import cv2 -import numpy as np - -from ....common import blue_color_generator -from ....common import hex_to_rgb -from ....common import id2rgb -from ....common import tqdm_converter -from ....common import write_to_json -from ..sa_json_helper import _create_pixel_instance -from ..sa_json_helper import _create_sa_json from .coco_api import _maskfrRLE from .coco_api import decode @@ -29,156 +16,3 @@ def annot_to_bitmask(annot): bitmask = decode(annot) return bitmask - - -def coco_panoptic_segmentation_to_sa_pixel(coco_path, output_dir): - coco_json = json.load(open(coco_path)) - hex_colors = blue_color_generator(len(coco_json["categories"])) - - cat_id_to_cat = {} - for cat in coco_json["categories"]: - cat_id_to_cat[cat["id"]] = cat["name"] - - img_id_to_shape = {} - for img in coco_json["images"]: - img_id_to_shape[str(img["id"])] = { - "height": img["height"], - "width": img["width"], - } - - images_converted = [] - images_not_converted = [] - finish_event = threading.Event() - tqdm_thread = threading.Thread( - target=tqdm_converter, - args=( - len(coco_json["annotations"]), - images_converted, - images_not_converted, - finish_event, - ), - daemon=True, - ) - logger.info("Converting to SuperAnnotate JSON format") - tqdm_thread.start() - for annot in coco_json["annotations"]: - annot_name = Path(annot["file_name"]).stem - img_cv = cv2.imread(str(output_dir / ("%s.png" % annot_name))) - if img_cv is None: - images_not_converted.append(annot["file_name"]) - logger.warning( - "'%s' file dosen't exist!", output_dir / ("%s.png" % annot_name) - ) - continue - - img = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB) - H, W, C = img.shape - img = img.reshape((H * W, C)) - segments = annot["segments_info"] - hex_colors = blue_color_generator(len(segments)) - - sa_instances = [] - for i, seg in enumerate(segments): - img[np.all(img == id2rgb(seg["id"]), axis=1)] = hex_to_rgb(hex_colors[i]) - parts = [{"color": hex_colors[i]}] - sa_obj = _create_pixel_instance( - parts, [], cat_id_to_cat[seg["category_id"]] - ) - sa_instances.append(sa_obj) - - img = cv2.cvtColor(img.reshape((H, W, C)), cv2.COLOR_RGB2BGR) - cv2.imwrite(str(output_dir / ("%s___save.png" % annot["file_name"])), img) - - images_converted.append(annot["file_name"]) - file_name = f"{annot['file_name']}.json" - sa_metadata = { - "name": annot_name, - "width": img_id_to_shape[str(annot["image_id"])]["width"], - "height": img_id_to_shape[str(annot["image_id"])]["height"], - } - json_template = _create_sa_json(sa_instances, sa_metadata) - write_to_json(output_dir / file_name, json_template) - (output_dir / ("%s.png" % annot_name)).unlink() - finish_event.set() - tqdm_thread.join() - - -def coco_instance_segmentation_to_sa_pixel(coco_path, output_dir): - coco_json = json.load(open(coco_path)) - cat_id_to_cat = {} - for cat in coco_json["categories"]: - cat_id_to_cat[cat["id"]] = cat - - images_dict = {} - for img in coco_json["images"]: - images_dict[int(img["id"])] = { - "shape": (img["height"], img["width"], 4), - "file_name": img["file_name"], - "annotations": [], - } - - for annot in coco_json["annotations"]: - if int(annot["image_id"]) not in images_dict: - continue - images_dict[annot["image_id"]]["annotations"].append(annot) - - images_converted = [] - images_not_converted = [] - finish_event = threading.Event() - tqdm_thread = threading.Thread( - target=tqdm_converter, - args=( - len(images_dict.items()), - images_converted, - images_not_converted, - finish_event, - ), - daemon=True, - ) - logger.info("Converting to SuperAnnotate JSON format") - tqdm_thread.start() - for id_, annotations in images_dict.items(): - file_name = f"{annotations['file_name']}.json" - hexcolors = blue_color_generator(len(annotations["annotations"])) - mask = np.zeros(annotations["shape"]) - H, W, _ = mask.shape - - sa_instances = [] - for i, annot in enumerate(annotations["annotations"]): - hexcolor = hexcolors[i] - color = hex_to_rgb(hexcolor) - if isinstance(annot["segmentation"], dict): - bitmask = annot_to_bitmask(annot["segmentation"]) - mask[bitmask == 1] = list(color)[::-1] + [255] - else: - for segment in annot["segmentation"]: - bitmask = np.zeros((H, W)).astype(np.uint8) - pts = np.array( - [ - segment[2 * i : 2 * (i + 1)] - for i in range(len(segment) // 2) - ], - dtype=np.int32, - ) - cv2.fillPoly(bitmask, [pts], 1) - mask[bitmask == 1] = list(color)[::-1] + [255] - - parts = [{"color": hexcolor}] - sa_obj = _create_pixel_instance( - parts, [], cat_id_to_cat[annot["category_id"]]["name"] - ) - sa_instances.append(sa_obj) - - sa_metadata = { - "name": images_dict[id_]["file_name"], - "width": images_dict[id_]["shape"][1], - "height": images_dict[id_]["shape"][0], - } - images_converted.append(annotations["file_name"]) - json_template = _create_sa_json(sa_instances, sa_metadata) - write_to_json(output_dir / file_name, json_template) - cv2.imwrite( - str(output_dir / ("%s___save.png" % annotations["file_name"])), mask - ) - finish_event.set() - tqdm_thread.join() diff --git a/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_pixel_to_coco.py b/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_pixel_to_coco.py index 439d4b577..92af16383 100644 --- a/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_pixel_to_coco.py +++ b/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_pixel_to_coco.py @@ -29,60 +29,3 @@ def instance_object_commons(instances, id_generator, flat_mask): ] commons_lst = [x for x in commons_lst if x is not None] return commons_lst - - -def sa_pixel_to_coco_instance_segmentation( - make_annotation, image_commons, instances, id_generator -): - commons_lst = instance_object_commons( - instances, id_generator, image_commons.flat_mask - ) - image_info = image_commons.image_info - annotations_per_image = [] - for common in commons_lst: - bbox, area, contours, category_id, anno_id = common - segmentation = [ - contour.flatten().tolist() - for contour in contours - if len(contour.flatten().tolist()) >= 5 - ] - - if segmentation != []: - annotations_per_image.append( - make_annotation( - category_id, image_info["id"], bbox, segmentation, area, anno_id - ) - ) - - return (image_info, annotations_per_image) - - -def sa_pixel_to_coco_panoptic_segmentation(image_commons, instnaces, id_generator): - flat_mask = image_commons.flat_mask - ann_mask = image_commons.ann_mask - - segments_info = [] - - for instance in instnaces: - if "parts" not in instance: - continue - - parts = [int(part["color"][1:], 16) for part in instance["parts"]] - category_id = instance["classId"] - instance_bitmask = np.isin(flat_mask, parts) - segment_id = next(id_generator) - ann_mask[instance_bitmask] = segment_id - bbox = list(_toBbox(instance_bitmask)) - area = int(_area(instance_bitmask.astype(np.uint8))) - - segment_info = { - "id": segment_id, - "category_id": category_id, - "area": area, - "bbox": bbox, - "iscrowd": 0, - } - - segments_info.append(segment_info) - - return (image_commons.image_info, segments_info, image_commons.ann_mask) diff --git a/src/superannotate/lib/app/input_converters/converters/converters.py b/src/superannotate/lib/app/input_converters/converters/converters.py index d6c213dbb..a07a697d3 100644 --- a/src/superannotate/lib/app/input_converters/converters/converters.py +++ b/src/superannotate/lib/app/input_converters/converters/converters.py @@ -9,7 +9,6 @@ from .coco_converters.coco_strategies import CocoPanopticConverterStrategy from .dataloop_converters.dataloop_strategies import DataLoopStrategy from .googlecloud_converters.googlecloud_strategies import GoogleCloudStrategy -from .labelbox_converters.labelbox_strategies import LabelBoxStrategy from .sagemaker_converters.sagemaker_strategies import SageMakerStrategy from .supervisely_converters.supervisely_strategies import SuperviselyStrategy from .vgg_converters.vgg_strategies import VGGStrategy @@ -44,8 +43,6 @@ def _select_strategy(self, args): c_strategy = DataLoopStrategy(args) elif args.dataset_format == "GoogleCloud": c_strategy = GoogleCloudStrategy(args) - elif args.dataset_format == "LabelBox": - c_strategy = LabelBoxStrategy(args) elif args.dataset_format == "SageMaker": c_strategy = SageMakerStrategy(args) elif args.dataset_format == "Supervisely": diff --git a/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py b/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py index c8b61d6c0..928697eb2 100644 --- a/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py +++ b/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py @@ -1,19 +1,9 @@ import logging -import requests logger = logging.getLogger("sa") -def image_downloader(url, file_name): - r = requests.get(url, stream=True) - if r.status_code == 200: - with open(file_name, "wb") as f: - f.write(r.content) - return True - return False - - def _create_classes_id_map(json_data): classes = {} for d in json_data: diff --git a/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_strategies.py b/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_strategies.py deleted file mode 100644 index 4bb7b71ee..000000000 --- a/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_strategies.py +++ /dev/null @@ -1,42 +0,0 @@ -import json - -from ....common import write_to_json -from ..baseStrategy import baseStrategy - - -class LabelBoxStrategy(baseStrategy): - def __init__(self, args): - super().__init__(args) - - def to_sa_format(self): - json_data = json.load(open(self.export_root / (self.dataset_name + ".json"))) - if self.project_type == "Vector": - classes = self.conversion_algorithm(json_data, self.output_dir, self.task) - elif self.project_type == "Pixel": - classes = self.conversion_algorithm( - json_data, self.output_dir, self.export_root - ) - sa_classes = self._create_classes(classes) - (self.output_dir / "classes").mkdir(exist_ok=True) - write_to_json(self.output_dir / "classes" / "classes.json", sa_classes) - - def _create_classes(self, classes): - sa_classes_loader = [] - for key, value in classes.items(): - sa_classes = {"name": key, "color": value["color"], "attribute_groups": []} - attribute_groups = [] - for attr_group_key, attr_group in value["attribute_groups"].items(): - attr_loader = { - "name": attr_group_key, - "is_multiselect": attr_group["is_multiselect"], - "attributes": [], - } - for attr in attr_group["attributes"]: - attr_loader["attributes"].append({"name": attr}) - if attr_loader: - attribute_groups.append(attr_loader) - sa_classes["attribute_groups"] = attribute_groups - - sa_classes_loader.append(sa_classes) - - return sa_classes_loader diff --git a/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_pixel.py b/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_pixel.py deleted file mode 100644 index ef84a9bec..000000000 --- a/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_pixel.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -Labelbox to SA conversion method -""" -import logging -import threading -from pathlib import Path - -import cv2 -import numpy as np - -from ....common import blue_color_generator -from ....common import hex_to_rgb -from ....common import tqdm_converter -from ....common import write_to_json -from ..sa_json_helper import _create_pixel_instance -from ..sa_json_helper import _create_sa_json -from .labelbox_helper import _create_attributes_list -from .labelbox_helper import _create_classes_id_map -from .labelbox_helper import image_downloader - -logger = logging.getLogger("sa") - - -def labelbox_instance_segmentation_to_sa_pixel(json_data, output_dir, input_dir): - classes = _create_classes_id_map(json_data) - - images_converted = [] - images_not_converted = [] - finish_event = threading.Event() - tqdm_thread = threading.Thread( - target=tqdm_converter, - args=(len(json_data), images_converted, images_not_converted, finish_event), - daemon=True, - ) - logger.info("Converting to SuperAnnotate JSON format") - tqdm_thread.start() - for data in json_data: - file_name = data["External ID"] + ".json" - mask_name = data["External ID"] + "___save.png" - sa_metadata = {"name": data["External ID"]} - if "objects" not in data["Label"].keys(): - sa_json = _create_sa_json([], sa_metadata) - write_to_json(output_dir / file_name, sa_json) - continue - - instances = data["Label"]["objects"] - sa_instances = [] - blue_colors = blue_color_generator(len(instances)) - - for i, instance in enumerate(instances): - class_name = instance["value"] - attributes = [] - if "classifications" in instance.keys(): - attributes = _create_attributes_list(instance["classifications"]) - - if ( - "bbox" in instance.keys() - or "polygon" in instance.keys() - or "line" in instance.keys() - or "point" in instance.keys() - ): - continue - - bitmask_name = "%s.png" % instance["featureId"] - downloaded = image_downloader(instance["instanceURI"], bitmask_name) - if downloaded: - mask = cv2.imread(bitmask_name) - else: - mask = cv2.imread(str(input_dir / "bitmasks" / bitmask_name)) - bitmask_name = output_dir / bitmask_name - - if isinstance(mask, type(None)): - logger.warning("Can't open '%s' bitmask.", bitmask_name) - images_not_converted.append(bitmask_name) - continue - - if i == 0: - H, W, _ = mask.shape - sa_metadata["width"] = W - sa_metadata["height"] = H - sa_mask = np.zeros((H, W, 4)) - sa_mask[np.all(mask == [255, 255, 255], axis=2)] = list( - hex_to_rgb(blue_colors[i]) - )[::-1] + [255] - - parts = [{"color": blue_colors[i]}] - sa_obj = _create_pixel_instance(parts, attributes, class_name) - - sa_instances.append(sa_obj.copy()) - Path(bitmask_name).unlink() - - images_converted.append(data["External ID"]) - sa_json = _create_sa_json(sa_instances, sa_metadata) - write_to_json(output_dir / file_name, sa_json) - cv2.imwrite(str(output_dir / mask_name), sa_mask) - - finish_event.set() - tqdm_thread.join() - return classes diff --git a/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py b/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py index 09e0ef000..a9528d3fc 100644 --- a/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py +++ b/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py @@ -1,7 +1,3 @@ -""" -""" - - def _create_vector_instance( instance_type, points, @@ -54,17 +50,6 @@ def _create_vector_instance( return sa_instance -def _create_pixel_instance(parts, attributes, class_name=""): - sa_instance = { - "attributes": attributes, - "parts": parts, - } - if class_name: - sa_instance["className"] = class_name - - return sa_instance - - def _create_comment(points, comments): sa_comment = { "type": "comment", diff --git a/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_pixel.py b/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_pixel.py deleted file mode 100644 index 57d8f0d4c..000000000 --- a/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_pixel.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -Sagemaker to SA conversion method -""" -import json -import logging -import threading -from pathlib import Path - -import cv2 -import numpy as np - -from ....common import blue_color_generator -from ....common import hex_to_rgb -from ....common import tqdm_converter -from ....common import write_to_json -from ..sa_json_helper import _create_pixel_instance -from ..sa_json_helper import _create_sa_json - -logger = logging.getLogger("sa") - - -def sagemaker_instance_segmentation_to_sa_pixel(data_path, output_dir): - img_mapping = {} - try: - img_map_file = open(data_path / "output.manifest") - except Exception: - raise Exception("'output.manifest' file doesn't exist") - - for line in img_map_file: - dd = json.loads(line) - img_mapping[Path(dd["attribute-name-ref"]).name] = Path(dd["source-ref"]).name - - json_list = list(data_path.glob("*.json")) - classes_ids = {} - images_converted = [] - images_not_converted = [] - finish_event = threading.Event() - tqdm_thread = threading.Thread( - target=tqdm_converter, - args=(len(json_list), images_converted, images_not_converted, finish_event), - daemon=True, - ) - logger.info("Converting to SuperAnnotate JSON format") - tqdm_thread.start() - for json_file in json_list: - data_json = json.load(open(json_file)) - for annotataion in data_json: - if "consolidatedAnnotation" not in annotataion.keys(): - logger.warning("Wrong json files") - raise Exception - - mask_name = Path( - annotataion["consolidatedAnnotation"]["content"]["attribute-name-ref"] - ).name - - classes_dict = annotataion["consolidatedAnnotation"]["content"][ - "attribute-name-ref-metadata" - ]["internal-color-map"] - - try: - img = cv2.imread(str(data_path / mask_name.replace(":", "_"))) - H, W, _ = img.shape - except Exception: - logger.warning("Can't open %s mask", mask_name.replace(":", "_")) - images_not_converted.append(mask_name.replace(":", "_")) - continue - - class_contours = {} - num_of_contours = 0 - for key, _ in classes_dict.items(): - if classes_dict[key]["class-name"] == "BACKGROUND": - continue - - if key not in classes_ids.keys(): - classes_ids[key] = classes_dict[key]["class-name"] - - bitmask = np.zeros((H, W), dtype=np.int8) - bitmask[ - np.all( - img == list(hex_to_rgb(classes_dict[key]["hex-color"]))[::-1], - axis=2, - ) - ] = 255 - - bitmask = bitmask.astype(np.uint8) - contours, _ = cv2.findContours( - bitmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE - ) - class_contours[classes_dict[key]["class-name"]] = contours - num_of_contours += len(contours) - - blue_colors = blue_color_generator(num_of_contours) - idx = 0 - file_name = f"{img_mapping[mask_name]}.json" - sa_metadata = {"name": img_mapping[mask_name], "width": W, "height": H} - sa_instances = [] - sa_mask = np.zeros((H, W, 4)) - for name, contours in class_contours.items(): - parts = [] - for contour in contours: - bitmask = np.zeros((H, W)) - contour = contour.flatten().tolist() - pts = np.array( - [ - contour[2 * i : 2 * (i + 1)] # noqa: E203 - for i in range(len(contour) // 2) - ], - dtype=np.int32, - ) - cv2.fillPoly(bitmask, [pts], 1) - sa_mask[bitmask == 1] = list(hex_to_rgb(blue_colors[idx]))[::-1] + [ - 255 - ] - parts.append({"color": blue_colors[idx]}) - idx += 1 - sa_obj = _create_pixel_instance(parts, [], name) - sa_instances.append(sa_obj) - images_converted.append(img_mapping[mask_name]) - sa_json = _create_sa_json(sa_instances, sa_metadata) - write_to_json(output_dir / file_name, sa_json) - cv2.imwrite( - str(output_dir / (img_mapping[mask_name] + "___save.png")), sa_mask - ) - finish_event.set() - tqdm_thread.join() - return classes_ids diff --git a/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py b/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py index 719384689..82288a599 100644 --- a/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py +++ b/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py @@ -27,13 +27,6 @@ def to_sa_format(self): sa_jsons = self.conversion_algorithm( json_files, classes_id_map, meta_json, self.output_dir ) - elif ( - self.conversion_algorithm.__name__ - == "supervisely_instance_segmentation_to_sa_pixel" - ): - sa_jsons = self.conversion_algorithm( - json_files, classes_id_map, self.output_dir - ) else: sa_jsons = self.conversion_algorithm( json_files, classes_id_map, self.task, self.output_dir diff --git a/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_pixel.py b/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_pixel.py deleted file mode 100644 index a5cd7bf4b..000000000 --- a/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_pixel.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -Supervisely to SA conversion method -""" -import json -import logging -import threading -from pathlib import Path - -import cv2 -import numpy as np - -from ....common import blue_color_generator -from ....common import hex_to_rgb -from ....common import tqdm_converter -from ....common import write_to_json -from ..sa_json_helper import _create_pixel_instance -from ..sa_json_helper import _create_sa_json -from .supervisely_helper import _base64_to_polygon -from .supervisely_helper import _create_attribute_list - -logger = logging.getLogger("sa") - - -def supervisely_instance_segmentation_to_sa_pixel(json_files, class_id_map, output_dir): - images_converted = [] - images_not_converted = [] - finish_event = threading.Event() - tqdm_thread = threading.Thread( - target=tqdm_converter, - args=(len(json_files), images_converted, images_not_converted, finish_event), - daemon=True, - ) - logger.info("Converting to SuperAnnotate JSON format") - tqdm_thread.start() - - for json_file in json_files: - file_name = f"{Path(json_file).stem}.json" - - json_data = json.load(open(json_file)) - sa_instances = [] - - H, W = json_data["size"]["height"], json_data["size"]["width"] - mask = np.zeros((H, W, 4)) - sa_metadata = {"name": Path(json_file).stem, "width": W, "height": H} - - hex_colors = blue_color_generator(10 * len(json_data["objects"])) - index = 0 - for obj in json_data["objects"]: - if "classTitle" in obj and obj["classTitle"] in class_id_map.keys(): - attributes = [] - if "tags" in obj.keys(): - attributes = _create_attribute_list( - obj["tags"], obj["classTitle"], class_id_map - ) - parts = [] - if obj["geometryType"] == "bitmap": - segments = _base64_to_polygon(obj["bitmap"]["data"]) - for segment in segments: - ppoints = [ - x + obj["bitmap"]["origin"][0] - if i % 2 == 0 - else x + obj["bitmap"]["origin"][1] - for i, x in enumerate(segment) - ] - bitmask = np.zeros((H, W)).astype(np.uint8) - pts = np.array( - [ - ppoints[2 * i : 2 * (i + 1)] - for i in range(len(ppoints) // 2) - ], - dtype=np.int32, - ) - cv2.fillPoly(bitmask, [pts], 1) - color = hex_to_rgb(hex_colors[index]) - mask[bitmask == 1] = list(color[::-1]) + [255] - parts.append({"color": hex_colors[index]}) - index += 1 - cv2.imwrite( - str(output_dir / file_name.replace(".json", "___save.png")), - mask, - ) - sa_obj = _create_pixel_instance( - parts, attributes, obj["classTitle"] - ) - sa_instances.append(sa_obj) - - images_converted.append(Path(json_file).stem) - sa_json = _create_sa_json(sa_instances, sa_metadata) - write_to_json(output_dir / file_name, sa_json) - finish_event.set() - tqdm_thread.join() diff --git a/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py b/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py index 7dc013977..cf62b13fc 100644 --- a/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py +++ b/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py @@ -13,12 +13,6 @@ def to_sa_format(self): sa_classes = self._create_classes(classes) (self.output_dir / "classes").mkdir(exist_ok=True) write_to_json(self.output_dir / "classes" / "classes.json", sa_classes) - - # if self.project_type == 'Pixel': - # all_files = self.output_dir.glob('*.png') - # for file in all_files: - # if '___save.png' not in str(file.name): - # (self.output_dir / file.name).unlink() all_files = self.output_dir.glob("*.png") for file in all_files: if "___save.png" not in str(file.name): diff --git a/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_pixel.py b/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_pixel.py index 128ef27ed..0714307e7 100644 --- a/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_pixel.py +++ b/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_pixel.py @@ -2,19 +2,12 @@ VOC to SA conversion method """ import logging -import threading import cv2 import numpy as np from ....common import blue_color_generator from ....common import hex_to_rgb -from ....common import tqdm_converter -from ....common import write_to_json -from ..sa_json_helper import _create_pixel_instance -from ..sa_json_helper import _create_sa_json -from .voc_helper import _get_image_shape_from_xml -from .voc_helper import _get_voc_instances_from_xml from .voc_helper import _iou logger = logging.getLogger("sa") @@ -83,57 +76,3 @@ def _generate_instances(polygon_instances, voc_instances, bluemask_colors): ) i += 1 return instances - - -def voc_instance_segmentation_to_sa_pixel(voc_root, output_dir): - classes = [] - object_masks_dir = voc_root / "SegmentationObject" - annotation_dir = voc_root / "Annotations" - - file_list = list(object_masks_dir.glob("*")) - if not file_list: - logger.warning( - "You need to have both 'Annotations' and 'SegmentationObject' directories to be able to convert." - ) - images_converted = [] - images_not_converted = [] - finish_event = threading.Event() - tqdm_thread = threading.Thread( - target=tqdm_converter, - args=(len(file_list), images_converted, images_not_converted, finish_event), - daemon=True, - ) - logger.info("Converting to SuperAnnotate JSON format") - tqdm_thread.start() - for filename in file_list: - polygon_instances, sa_mask, bluemask_colors = _generate_polygons( - object_masks_dir / filename.name - ) - voc_instances = _get_voc_instances_from_xml(annotation_dir / filename.name) - for class_, _ in voc_instances: - classes.append(class_) - - maped_instances = _generate_instances( - polygon_instances, voc_instances, bluemask_colors - ) - - sa_instances = [] - for instance in maped_instances: - parts = [{"color": instance["blue_color"]}] - sa_obj = _create_pixel_instance( - parts, instance["classAttributes"], instance["className"] - ) - sa_instances.append(sa_obj) - - images_converted.append(filename) - file_name = f"{filename.stem}.json" - height, width = _get_image_shape_from_xml(annotation_dir / filename.name) - sa_metadata = {"name": filename.stem, "height": height, "width": width} - sa_json = _create_sa_json(sa_instances, sa_metadata) - write_to_json(output_dir / file_name, sa_json) - - mask_name = "%s.jpg___save.png" % (filename.stem) - cv2.imwrite(str(output_dir / mask_name), sa_mask[:, :, ::-1]) - finish_event.set() - tqdm_thread.join() - return classes diff --git a/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py b/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py index b5d6a99ac..1c4119eed 100644 --- a/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py +++ b/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py @@ -16,11 +16,7 @@ def _load_files(path_to_imgs, task, ptype): - suffix = None - if ptype == "Pixel": - suffix = "___pixel.json" - else: - suffix = "___objects.json" + suffix = "___objects.json" orig_images = path_to_imgs.glob("*" + suffix) orig_images = [str(x).replace(suffix, "") for x in orig_images] @@ -28,10 +24,6 @@ def _load_files(path_to_imgs, task, ptype): if task == "keypoint_detection": all_files = np.array([[fname, fname + suffix] for fname in orig_images]) - elif ptype == "Pixel": - all_files = np.array( - [[fname, fname + suffix, fname + "___save.png"] for fname in orig_images] - ) elif ptype == "Vector": all_files = np.array( [[fname, fname + suffix, fname + "___fuse.png"] for fname in orig_images] diff --git a/src/superannotate/lib/app/input_converters/sa_conversion.py b/src/superannotate/lib/app/input_converters/sa_conversion.py index cc4a7ac72..0c1016ed6 100644 --- a/src/superannotate/lib/app/input_converters/sa_conversion.py +++ b/src/superannotate/lib/app/input_converters/sa_conversion.py @@ -1,191 +1,9 @@ -import itertools -import json import logging import shutil -from pathlib import Path -import cv2 -import numpy as np -from lib.core import DEPRICATED_DOCUMENT_VIDEO_MESSAGE -from lib.core.exceptions import AppException - -from ..common import blue_color_generator -from ..common import hex_to_rgb -from ..common import write_to_json logger = logging.getLogger("sa") def copy_file(src_path, dst_path): shutil.copy(src_path, dst_path) - - -def from_pixel_to_vector(json_paths, output_dir): - img_names = [] - - for json_path in json_paths: - file_name = str(json_path) - pixel_postfix = "___pixel.json" - postfix = pixel_postfix if file_name.endswith(pixel_postfix) else ".json" - mask_name = file_name.replace(postfix, "___save.png") - img = cv2.imread(mask_name) - H, W, _ = img.shape - sa_json = json.load(open(file_name)) - instances = sa_json["instances"] - new_instances = [] - global_idx = itertools.count() - sa_instances = [] - - for instance in instances: - if "parts" not in instance.keys(): - if "type" in instance.keys() and instance["type"] == "meta": - sa_instances.append(instance) - continue - parts = instance["parts"] - if len(parts) > 1: - group_id = next(global_idx) - else: - group_id = 0 - from collections import defaultdict - - for part in parts: - color = list(hex_to_rgb(part["color"])) - mask = np.zeros((H, W), dtype=np.uint8) - mask[np.all((img == color[::-1]), axis=2)] = 255 - - # child contour index hierarchy[0][[i][3] - contours, hierarchy = cv2.findContours( - mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE - ) - parent_child_map = defaultdict(list) - for idx, _hierarchy in enumerate(hierarchy[0]): - - if len(contours[idx].flatten().tolist()) <= 6: - continue - if _hierarchy[3] < 0: - parent_child_map[idx] = [] - else: - parent_child_map[_hierarchy[3]].append(idx) - - for outer, inners in parent_child_map.items(): - outer_points = contours[outer].flatten().tolist() - exclude_points = [contours[i].flatten().tolist() for i in inners] - temp = instance.copy() - del temp["parts"] - temp["pointLabels"] = {} - temp["groupId"] = group_id - temp["type"] = "polygon" - temp["points"] = outer_points - temp["exclude"] = exclude_points - new_instances.append(temp) - - sa_json["instances"] = new_instances - write_to_json( - str(output_dir / Path(file_name).name.replace(postfix, ".json")), sa_json - ) - img_names.append(file_name.replace(postfix, "")) - return img_names - - -def from_vector_to_pixel(json_paths, output_dir): - img_names = [] - for json_path in json_paths: - file_name = str(json_path) - vector_postfix = "___objects.json" - postfix = vector_postfix if file_name.endswith(vector_postfix) else ".json" - img_name = file_name.replace(postfix, "") - img = cv2.imread(img_name) - H, W, _ = img.shape - - sa_json = json.load(open(file_name)) - instances = sa_json["instances"] - mask = np.zeros((H, W, 4)) - - sa_instances = [] - blue_colors = blue_color_generator(len(instances)) - instances_group = {} - for idx, instance in enumerate(instances): - if instance["type"] == "polygon": - if instance["groupId"] in instances_group.keys(): - instances_group[instance["groupId"]].append(instance) - else: - instances_group[instance["groupId"]] = [instance] - elif instance["type"] == "meta": - sa_instances.append(instance) - - idx = 0 - for key, instances in instances_group.items(): - if key == 0: - for instance in instances: - pts = np.array( - [ - instance["points"][2 * i : 2 * (i + 1)] - for i in range(len(instance["points"]) // 2) - ], - dtype=np.int32, - ) - bitmask = np.zeros((H, W)) - cv2.fillPoly(bitmask, [pts], 1) - mask[bitmask == 1] = list(hex_to_rgb(blue_colors[idx]))[::-1] + [ - 255 - ] - del instance["type"] - del instance["points"] - del instance["pointLabels"] - del instance["groupId"] - instance["parts"] = [{"color": blue_colors[idx]}] - sa_instances.append(instance.copy()) - idx += 1 - else: - parts = [] - for instance in instances: - pts = np.array( - [ - instance["points"][2 * i : 2 * (i + 1)] - for i in range(len(instance["points"]) // 2) - ], - dtype=np.int32, - ) - bitmask = np.zeros((H, W)) - cv2.fillPoly(bitmask, [pts], 1) - mask[bitmask == 1] = list(hex_to_rgb(blue_colors[idx]))[::-1] + [ - 255 - ] - parts.append({"color": blue_colors[idx]}) - idx += 1 - del instance["type"] - del instance["points"] - del instance["pointLabels"] - del instance["groupId"] - instance["parts"] = parts - sa_instances.append(instance.copy()) - - mask_name = file_name.replace(postfix, "___save.png") - cv2.imwrite(str(output_dir.joinpath(Path(mask_name).name)), mask) - - sa_json["instances"] = sa_instances - write_to_json( - str(output_dir / Path(file_name).name.replace(postfix, ".json")), sa_json - ) - img_names.append(img_name.replace(".json", "")) - return img_names - - -def sa_convert_project_type(input_dir, output_dir, convert_to): - json_paths = list(input_dir.glob("*.json")) - - output_dir.joinpath("classes").mkdir(parents=True) - copy_file( - input_dir.joinpath("classes", "classes.json"), - output_dir.joinpath("classes", "classes.json"), - ) - - if convert_to == "Vector": - img_names = from_pixel_to_vector(json_paths, output_dir) - elif convert_to == "Pixel": - img_names = from_vector_to_pixel(json_paths, output_dir) - else: - raise AppException(DEPRICATED_DOCUMENT_VIDEO_MESSAGE) - - for img_name in img_names: - copy_file(img_name, output_dir / Path(img_name).name) diff --git a/src/superannotate/lib/app/interface/sdk_interface.py b/src/superannotate/lib/app/interface/sdk_interface.py index cb2190794..d33c98278 100644 --- a/src/superannotate/lib/app/interface/sdk_interface.py +++ b/src/superannotate/lib/app/interface/sdk_interface.py @@ -85,7 +85,6 @@ PROJECT_TYPE = Literal[ "Vector", - "Pixel", "Video", "Document", "Tiled", @@ -1194,7 +1193,7 @@ def create_project( :param project_description: The new project's description. :type project_description: str - :param project_type: The project type. Supported types: 'Vector', 'Pixel', 'Video', 'Document', 'Tiled', 'PointCloud', 'Multimodal'. + :param project_type: The project type. Supported types: 'Vector', 'Video', 'Document', 'Tiled', 'PointCloud', 'Multimodal'. :type project_type: str :param settings: list of settings objects @@ -1329,10 +1328,7 @@ def clone_project( project: entities.ProjectEntity = response.data project_copy = copy.copy(project) - if project_copy.type in ( - constants.ProjectType.VECTOR, - constants.ProjectType.PIXEL, - ): + if project_copy.type == constants.ProjectType.VECTOR: project_copy.upload_state = constants.UploadState.INITIAL if project_description: project_copy.description = project_description @@ -2378,7 +2374,7 @@ def download_image_annotations( image_name: NotEmptyStr, local_dir_path: Union[str, Path], ): - """Downloads annotations of the image (JSON and mask if pixel type project) + """Downloads annotations of the image to local_dir_path. :param project: Project and folder as a tuple, folder is optional. (e.g., "project1/folder1", (project_id, folder_id)) @@ -3173,10 +3169,8 @@ def upload_annotations_from_folder_to_project( The JSON files should follow specific naming convention. For Vector projects they should be named "___objects.json" (e.g., if - image is cats.jpg the annotation filename should be cats.jpg___objects.json), for Pixel projects - JSON file should be named "___pixel.json" and also second mask - image file should be present with the name "___save.png". In both cases - image with should be already present on the platform. + image is cats.jpg the annotation filename should be cats.jpg___objects.json). + Image with should be already present on the platform. Existing annotations will be overwritten. @@ -3250,7 +3244,7 @@ def upload_image_annotations( verbose: Optional[bool] = True, keep_status: bool = None, ): - """Upload annotations from JSON (also mask for pixel annotations) + """Upload annotations from JSON to the image. :param project: Project and folder as a tuple, folder is optional. (e.g., "project1/folder1", (project_id, folder_id)) @@ -3262,7 +3256,7 @@ def upload_image_annotations( :param annotation_json: annotations in SuperAnnotate format JSON dict or path to JSON file :type annotation_json: dict or Path-like (str or Path) - :param mask: BytesIO object or filepath to mask annotation for pixel projects in SuperAnnotate format + :param mask: deprecated :type mask: BytesIO or Path-like (str or Path) :param verbose: Turns on verbose output logging during the proces. @@ -3286,19 +3280,6 @@ def upload_image_annotations( if project.type not in constants.ProjectType.images: raise AppException(LIMITED_FUNCTIONS[project.type]) - if not mask: - if not isinstance(annotation_json, dict): - mask_path = str(annotation_json).replace("___pixel.json", "___save.png") - else: - mask_path = f"{image_name}___save.png" - if os.path.exists(mask_path): - with open(mask_path, "rb") as f: - mask = f.read() - elif isinstance(mask, str) or isinstance(mask, Path): - if os.path.exists(mask): - with open(mask, "rb") as f: - mask = f.read() - if not isinstance(annotation_json, dict): if verbose: logger.info("Uploading annotations from %s.", annotation_json) @@ -3319,7 +3300,6 @@ def upload_image_annotations( image=image, annotations=annotation_json, user=self.controller.current_user, - mask=mask, verbose=verbose, keep_status=keep_status, ) @@ -3501,7 +3481,7 @@ def aggregate_annotations_as_df( :param project_root: the export path of the project :type project_root: Path-like (str or Path) - :param project_type: the project type, Vector/Pixel, Video or Document + :param project_type: the project type, Vector, Video or Document :type project_type: str :param folder_names: Aggregate the specified folders from project_root. @@ -3548,7 +3528,7 @@ def validate_annotations( ): """Validates given annotation JSON. - :param project_type: The project type Vector, Pixel, Video or Document + :param project_type: The project type Vector, Video or Document :type project_type: str :param annotations_json: path to annotation JSON diff --git a/src/superannotate/lib/core/__init__.py b/src/superannotate/lib/core/__init__.py index 9a8da45b0..4bfb72783 100644 --- a/src/superannotate/lib/core/__init__.py +++ b/src/superannotate/lib/core/__init__.py @@ -76,7 +76,6 @@ def setup_logging(level=DEFAULT_LOGGING_LEVEL, file_path=LOG_FILE_LOCATION): DEFAULT_VIDEO_EXTENSIONS = ["mp4", "avi", "mov", "webm", "flv", "mpg", "ogg"] VECTOR_ANNOTATION_POSTFIX = "___objects.json" -PIXEL_ANNOTATION_POSTFIX = "___pixel.json" ANNOTATION_MASK_POSTFIX = "___save.png" ATTACHED_VIDEO_ANNOTATION_POSTFIX = ".json" @@ -114,9 +113,6 @@ def setup_logging(level=DEFAULT_LOGGING_LEVEL, file_path=LOG_FILE_LOCATION): ProjectType.TILED: DEPRECATED_PROJECTS_MESSAGE, } -METADATA_DEPRICATED_FOR_PIXEL = ( - "custom_metadata field is not supported for project type Pixel." -) DEPRICATED_DOCUMENT_VIDEO_MESSAGE = "The function does not support projects containing videos / documents attached with URLs" UPLOAD_FOLDER_LIMIT_ERROR_MESSAGE = "The number of items you want to upload exceeds the limit of 50 000 items per folder." diff --git a/src/superannotate/lib/core/usecases/annotations.py b/src/superannotate/lib/core/usecases/annotations.py index c091ffcff..fde5dd005 100644 --- a/src/superannotate/lib/core/usecases/annotations.py +++ b/src/superannotate/lib/core/usecases/annotations.py @@ -277,10 +277,6 @@ def __init__( self._user = user self._transform_version = transform_version - def validate_project_type(self): - if self._project.type == constants.ProjectType.PIXEL.value: - raise AppException("Unsupported project type.") - def _validate_json(self, json_data: dict) -> list: if self._project.type >= int(constants.ProjectType.PIXEL): return [] @@ -558,10 +554,7 @@ def get_annotation_from_s3(bucket, path: str): def prepare_annotation(self, annotation: dict, size) -> dict: errors = None - if ( - size < BIG_FILE_THRESHOLD - and self._project.type < constants.ProjectType.PIXEL.value - ): + if size < BIG_FILE_THRESHOLD: use_case = ValidateAnnotationUseCase( reporter=self.reporter, team_id=self._project.team_id, @@ -583,31 +576,20 @@ def prepare_annotation(self, annotation: dict, size) -> dict: @staticmethod def get_mask_path(path: str) -> str: - if path.endswith(constants.PIXEL_ANNOTATION_POSTFIX): - replacement = constants.PIXEL_ANNOTATION_POSTFIX - else: - replacement = ".json" + + replacement = ".json" parts = path.rsplit(replacement, 1) return constants.ANNOTATION_MASK_POSTFIX.join(parts) async def get_annotation( self, path: str ) -> (Optional[Tuple[io.StringIO]], Optional[io.BytesIO]): - mask = None - mask_path = self.get_mask_path(path) + if self._client_s3_bucket: content = self.get_annotation_from_s3(self._client_s3_bucket, path).read() - if self._project.type == constants.ProjectType.PIXEL.value: - mask = self.get_annotation_from_s3(self._client_s3_bucket, mask_path) else: async with aiofiles.open(path, encoding="utf-8") as file: content = await file.read() - if ( - self._project.type == constants.ProjectType.PIXEL.value - and os.path.exists(mask_path) - ): - async with aiofiles.open(mask_path, "rb") as mask: - mask = await mask.read() if not isinstance(content, bytes): content = content.encode("utf8") file = io.BytesIO(content) @@ -618,7 +600,7 @@ async def get_annotation( if not annotation: self.reporter.store_message("invalid_jsons", path) raise AppException("Invalid json") - return annotation, mask, size + return annotation, None, size @staticmethod def chunks(data, size: int = 10000): @@ -630,8 +612,6 @@ def chunks(data, size: int = 10000): def extract_name(value: Path): if constants.VECTOR_ANNOTATION_POSTFIX in value.name: path = value.name.replace(constants.VECTOR_ANNOTATION_POSTFIX, "") - elif constants.PIXEL_ANNOTATION_POSTFIX in value.name: - path = value.name.replace(constants.PIXEL_ANNOTATION_POSTFIX, "") else: path = value.stem return path @@ -691,16 +671,6 @@ def s3_bucket(self): self._s3_bucket = resource.Bucket(upload_data.bucket) return self._s3_bucket - def _upload_mask(self, item_data: ItemToUpload): - if self._project.type == constants.ProjectType.PIXEL.value and item_data.mask: - self.s3_bucket.put_object( - Key=self.annotation_upload_data.images[item_data.item.id][ - "annotation_bluemap_path" - ], - Body=item_data.mask, - ContentType="image/jpeg", - ) - async def distribute_queues(self, items_to_upload: List[ItemToUpload]): data: List[List[Any, bool]] = [[i, False] for i in items_to_upload] processed_count = 0 @@ -749,7 +719,6 @@ async def run_workers(self, items_to_upload: List[ItemToUpload]): service_provider=self._service_provider, report=self._report, reporter=self.reporter, - callback=self._upload_mask, ) for _ in range(3) ], @@ -762,7 +731,6 @@ async def run_workers(self, items_to_upload: List[ItemToUpload]): service_provider=self._service_provider, reporter=self.reporter, report=self._report, - callback=self._upload_mask, ) ) @@ -914,26 +882,10 @@ def _get_annotation_json(self) -> tuple: annotation_json = json.load( self.get_s3_file(self.from_s3, self._annotation_path) ) - if self._project.type == constants.ProjectType.PIXEL.value: - self._mask = self.get_s3_file( - self.from_s3, - self._annotation_path.replace( - constants.PIXEL_ANNOTATION_POSTFIX, - constants.ANNOTATION_MASK_POSTFIX, - ), - ) else: annotation_json = json.load( open(self._annotation_path, encoding="utf-8") ) - if self._project.type == constants.ProjectType.PIXEL.value: - mask = open( - self._annotation_path.replace( - constants.PIXEL_ANNOTATION_POSTFIX, - constants.ANNOTATION_MASK_POSTFIX, - ), - "rb", - ) else: return self._annotation_json, self._mask return annotation_json, mask @@ -1025,17 +977,6 @@ def execute(self): self.reporter.log_warning( f"Couldn't find attribute {attr}." ) - - if ( - self._project.type == constants.ProjectType.PIXEL.value - and mask - ): - self.s3_bucket.put_object( - Key=self.annotation_upload_data.images[self._image.id][ - "annotation_bluemap_path" - ], - Body=mask, - ) workflow = self._service_provider.work_management.get_workflow( self._project.workflow_id ) @@ -1451,10 +1392,6 @@ def __init__( self._big_annotations_queue = None self._transform_version = transform_version - def validate_project_type(self): - if self._project.type == constants.ProjectType.PIXEL.value: - raise AppException("The function is not supported for Pixel projects.") - @staticmethod def items_duplication_validation( reporter, items: Union[List[str], List[int]] diff --git a/src/superannotate/lib/core/usecases/classes.py b/src/superannotate/lib/core/usecases/classes.py index 107ecaf87..2301a4a17 100644 --- a/src/superannotate/lib/core/usecases/classes.py +++ b/src/superannotate/lib/core/usecases/classes.py @@ -63,14 +63,6 @@ def _is_unique(self): ) def validate_project_type(self): - if ( - self._project.type == ProjectType.PIXEL - and self._annotation_class.type == "tag" - ): - raise AppException( - "Predefined tagging functionality is not supported for projects" - f" of type {ProjectType(self._project.type).name}." - ) if self._project.type != ProjectType.VECTOR: for g in self._annotation_class.attribute_groups: if g.group_type == GroupTypeEnum.OCR: @@ -79,15 +71,6 @@ def validate_project_type(self): f"{ProjectType(self._project.type).name}." ) - def validate_default_value(self): - if self._project.type == ProjectType.PIXEL.value and any( - getattr(attr_group, "default_value", None) - for attr_group in getattr(self._annotation_class, "attribute_groups", []) - ): - raise AppException( - 'The "default_value" key is not supported for project type Pixel.' - ) - def execute(self): if self.is_valid(): if self._is_unique(): @@ -125,11 +108,6 @@ def __init__( def validate_project_type(self): if self._project.type != ProjectType.VECTOR: for c in self._annotation_classes: - if self._project.type == ProjectType.PIXEL and c.type == "tag": - raise AppException( - f"Predefined tagging functionality is not supported" - f" for projects of type {ProjectType(self._project.type).name}." - ) for g in c.attribute_groups: if g.group_type == GroupTypeEnum.OCR: raise AppException( @@ -137,17 +115,6 @@ def validate_project_type(self): f"{ProjectType(self._project.type).name}." ) - def validate_default_value(self): - if self._project.type == ProjectType.PIXEL.value: - for annotation_class in self._annotation_classes: - if any( - getattr(attr_group, "default_value", None) - for attr_group in getattr(annotation_class, "attribute_groups", []) - ): - raise AppException( - 'The "default_value" key is not supported for project type Pixel.' - ) - def execute(self): if self.is_valid(): existing_annotation_classes = ( diff --git a/src/superannotate/lib/core/usecases/images.py b/src/superannotate/lib/core/usecases/images.py index f485740f0..6a53f25fc 100644 --- a/src/superannotate/lib/core/usecases/images.py +++ b/src/superannotate/lib/core/usecases/images.py @@ -29,7 +29,6 @@ from lib.core.entities import ProjectEntity from lib.core.entities import S3FileEntity from lib.core.enums import ImageQuality -from lib.core.enums import ProjectType from lib.core.exceptions import AppException from lib.core.exceptions import AppValidationException from lib.core.exceptions import ImageProcessingException @@ -343,23 +342,6 @@ def execute(self): data=json.dumps(image_annotations), ) self.to_project_s3_repo.insert(file) - - if ( - self._to_project.type == constances.ProjectType.PIXEL.value - and annotations.get("annotation_bluemap_path") - and annotations["annotation_bluemap_path"]["exist"] - ): - response = requests.get( - url=annotations["annotation_bluemap_path"]["url"], - headers=annotations["annotation_bluemap_path"]["headers"], - ) - if not response.ok: - raise AppException("Couldn't load annotations.") - self.to_project_s3_repo.insert( - S3FileEntity( - auth_data["annotation_bluemap_path"]["filePath"], response.content - ) - ) return self._response @@ -421,15 +403,7 @@ def annotations(self): @property def blue_mask_path(self): - image_path = Path(self._image_path) - if self._project_type.upper() == constances.ProjectType.PIXEL.name.upper(): - self._annotation_mask_path = str( - image_path.parent / f"{image_path.name}___save.png" - ) - else: - raise AppException("Vector project doesn't have blue mask.") - - return self._annotation_mask_path + raise AppException("Vector project doesn't have blue mask.") def execute(self): with open(self._image_path, "rb") as file: @@ -1230,8 +1204,6 @@ def __init__( @property def max_resolution(self) -> int: - if self._project.type == ProjectType.PIXEL.value: - return constances.MAX_PIXEL_RESOLUTION return constances.MAX_VECTOR_RESOLUTION def execute(self): @@ -1515,30 +1487,12 @@ def execute(self): return self._response data["annotation_json"] = response.json() data["annotation_json_filename"] = f"{self._image_name}.json" - mask_path = None - if self._project.type == constances.ProjectType.PIXEL.value: - annotation_blue_map_creds = credentials["annotation_bluemap_path"] - response = requests.get( - url=annotation_blue_map_creds["url"], - headers=annotation_blue_map_creds["headers"], - ) - data["annotation_mask_filename"] = f"{self._image_name}___save.png" - if response.ok: - data["annotation_mask"] = io.BytesIO(response.content).getbuffer() - mask_path = ( - Path(self._destination) / data["annotation_mask_filename"] - ) - with open(mask_path, "wb") as f: - f.write(data["annotation_mask"]) - else: - logger.info("There is no blue-map for the image.") - json_path = Path(self._destination) / data["annotation_json_filename"] self.fill_classes_data(data["annotation_json"]) with open(json_path, "w") as f: json.dump(data["annotation_json"], f, indent=4) - self._response.data = (str(json_path), str(mask_path)) + self._response.data = (str(json_path), "") return self._response diff --git a/src/superannotate/lib/infrastructure/controller.py b/src/superannotate/lib/infrastructure/controller.py index b61717397..0bbf5c622 100644 --- a/src/superannotate/lib/infrastructure/controller.py +++ b/src/superannotate/lib/infrastructure/controller.py @@ -1086,29 +1086,18 @@ def copy_multiple( item_names: List[str] = None, include_annotations: bool = True, ): - if project.type == ProjectType.PIXEL: - use_case = usecases.CopyItems( - reporter=Reporter(), - project=project, - from_folder=from_folder, - to_folder=to_folder, - item_names=item_names, - service_provider=self.service_provider, - include_annotations=include_annotations, - ) - else: - use_case = usecases.CopyMoveItems( - reporter=Reporter(), - project=project, - from_folder=from_folder, - to_folder=to_folder, - item_names=item_names, - service_provider=self.service_provider, - include_annotations=include_annotations, - duplicate_strategy=duplicate_strategy, - operation="copy", - chunk_size=500, - ) + use_case = usecases.CopyMoveItems( + reporter=Reporter(), + project=project, + from_folder=from_folder, + to_folder=to_folder, + item_names=item_names, + service_provider=self.service_provider, + include_annotations=include_annotations, + duplicate_strategy=duplicate_strategy, + operation="copy", + chunk_size=500, + ) return use_case.execute() def move_multiple( @@ -1119,28 +1108,18 @@ def move_multiple( duplicate_strategy: Literal["skip", "replace", "replace_annotations_only"], item_names: List[str] = None, ): - if project.type == ProjectType.PIXEL: - use_case = usecases.MoveItems( - reporter=Reporter(), - project=project, - from_folder=from_folder, - to_folder=to_folder, - item_names=item_names, - service_provider=self.service_provider, - ) - else: - use_case = usecases.CopyMoveItems( - reporter=Reporter(), - project=project, - from_folder=from_folder, - to_folder=to_folder, - item_names=item_names, - service_provider=self.service_provider, - duplicate_strategy=duplicate_strategy, - include_annotations=True, - operation="move", - chunk_size=500, - ) + use_case = usecases.CopyMoveItems( + reporter=Reporter(), + project=project, + from_folder=from_folder, + to_folder=to_folder, + item_names=item_names, + service_provider=self.service_provider, + duplicate_strategy=duplicate_strategy, + include_annotations=True, + operation="move", + chunk_size=500, + ) return use_case.execute() def set_annotation_statuses( @@ -1411,7 +1390,6 @@ def upload_image_annotations( image: ImageEntity, user: UserEntity, annotations: dict, - mask: io.BytesIO = None, verbose: bool = True, keep_status: bool = False, ): @@ -1422,7 +1400,6 @@ def upload_image_annotations( service_provider=self.service_provider, image=image, annotations=annotations, - mask=mask, verbose=verbose, reporter=Reporter(), keep_status=keep_status, diff --git a/tests/integration/annotations/test_annotation_upload_pixel.py b/tests/integration/annotations/test_annotation_upload_pixel.py deleted file mode 100644 index 6473a3164..000000000 --- a/tests/integration/annotations/test_annotation_upload_pixel.py +++ /dev/null @@ -1,77 +0,0 @@ -import os -from os.path import join -from pathlib import Path -from unittest.mock import patch - -import pytest -from src.superannotate import SAClient -from tests.integration.base import BaseTestCase - -sa = SAClient() - - -class TestRecursiveFolderPixel(BaseTestCase): - PROJECT_NAME = "test_recursive_pixel" - PROJECT_DESCRIPTION = "Desc" - PROJECT_TYPE = "Pixel" - S3_FOLDER_PATH = "sample_project_pixel" - TEST_FOLDER_PATH = "data_set/sample_project_pixel" - IMAGE_NAME = "example_image_1.jpg" - FOLDER = "f" - - @pytest.fixture(autouse=True) - def inject_fixtures(self, caplog): - self._caplog = caplog - - @property - def folder_path(self): - return os.path.join(Path(__file__).parent.parent.parent, self.TEST_FOLDER_PATH) - - @pytest.mark.flaky(reruns=4) - @patch("lib.core.usecases.annotations.UploadAnnotationUseCase.s3_bucket") - def test_recursive_annotation_upload_pixel(self, s3_bucket): - sa.create_folder(self.PROJECT_NAME, self.FOLDER) - destination = f"{self.PROJECT_NAME}/{self.FOLDER}" - sa.upload_images_from_folder_to_project( - destination, self.folder_path, recursive_subfolders=False - ) - uploaded_annotations, _, _ = sa.upload_annotations_from_folder_to_project( - destination, - self.S3_FOLDER_PATH, - from_s3_bucket="superannotate-python-sdk-test", - recursive_subfolders=False, - ) - self.assertEqual(len(uploaded_annotations), 3) - - def test_hex_color_adding(self): - sa.create_annotation_class(self.PROJECT_NAME, "test_add", color="#0000FF") - classes = sa.search_annotation_classes(self.PROJECT_NAME, "test_add") - assert classes[0]["color"] == "#0000FF" - - -class TestAnnotationUploadPixelSingle(BaseTestCase): - PROJECT_NAME = "TestAnnotationUploadPixelSingle" - PROJECT_DESCRIPTION = "Desc" - PROJECT_TYPE = "Pixel" - S3_FOLDER_PATH = "sample_project_pixel" - TEST_FOLDER_PATH = "data_set/sample_project_vector" - IMAGE_NAME = "example_image_1.jpg" - TEST_FOLDER_PATH_PIXEL = "data_set/sample_project_pixel" - - @property - def folder_path_pixel(self): - return os.path.join( - Path(__file__).parent.parent.parent, self.TEST_FOLDER_PATH_PIXEL - ) - - @pytest.mark.flaky(reruns=2) - @patch("lib.core.usecases.annotations.UploadAnnotationUseCase.s3_bucket") - def test_annotation_upload_pixel(self, s3_bucket): - annotation_path = join( - self.folder_path_pixel, f"{self.IMAGE_NAME}___pixel.json" - ) - sa.upload_image_to_project( - self.PROJECT_NAME, join(self.folder_path_pixel, self.IMAGE_NAME) - ) - sa.upload_image_annotations(self.PROJECT_NAME, self.IMAGE_NAME, annotation_path) - self.assertEqual(len(s3_bucket.method_calls), 1) diff --git a/tests/integration/annotations/test_upload_annotations_from_folder_to_project.py b/tests/integration/annotations/test_upload_annotations_from_folder_to_project.py index a20de7f95..4c13a6091 100644 --- a/tests/integration/annotations/test_upload_annotations_from_folder_to_project.py +++ b/tests/integration/annotations/test_upload_annotations_from_folder_to_project.py @@ -1,5 +1,4 @@ import os -import tempfile from pathlib import Path from src.superannotate import SAClient @@ -121,48 +120,3 @@ def test_annotation_folder_upload_download(self): self.PROJECT_NAME, self.folder_path ) assert len(uploaded) == 1 - - -class TestExportUploadPixel(BaseTestCase): - PROJECT_NAME = "Test-TestExportUploadPixel" - PROJECT_DESCRIPTION = "Desc" - PROJECT_TYPE = "PIXEL" - TEST_FOLDER_PATH = "data_set/sample_explore_export_pixel" - ITEM_NAME = "file_example.jpg" - - @property - def data_set(self): - return Path(__file__).parent.parent.parent - - @property - def folder_path(self): - return os.path.join(Path(__file__).parent.parent.parent, self.TEST_FOLDER_PATH) - - def test_annotation_folder_upload_download(self): - with tempfile.TemporaryDirectory() as tmpdir: - sa.attach_items( - self.PROJECT_NAME, - [{"name": self.ITEM_NAME, "url": "url_"}], - ) - sa.create_annotation_classes_from_classes_json( - self.PROJECT_NAME, f"{self.folder_path}/classes/classes.json" - ) - uploaded, _, _ = sa.upload_annotations_from_folder_to_project( - self.PROJECT_NAME, self.folder_path - ) - assert len(uploaded) == 1 - export_name = sa.prepare_export(self.PROJECT_NAME)["name"] - sa.download_export( - project=self.PROJECT_NAME, export=export_name, folder_path=tmpdir - ) - list_dir = os.listdir(tmpdir) - assert f"{self.ITEM_NAME}___save.png" in list_dir - assert f"{self.ITEM_NAME}.json" in list_dir - assert "classes" in list_dir - - with open(f"{tmpdir}/{self.ITEM_NAME}___save.png", "rb") as f1, open( - f"{self.folder_path}/{self.ITEM_NAME}___save.png", "rb" - ) as f2: - contents1 = f1.read() - contents2 = f2.read() - assert contents1 == contents2 diff --git a/tests/integration/classes/test_create_annotation_class.py b/tests/integration/classes/test_create_annotation_class.py index 22631ab1d..250e9279f 100644 --- a/tests/integration/classes/test_create_annotation_class.py +++ b/tests/integration/classes/test_create_annotation_class.py @@ -1,11 +1,9 @@ -import os import tempfile import pytest from src.superannotate import AppException from src.superannotate import SAClient from src.superannotate.lib.core.entities.classes import AnnotationClassEntity -from tests import DATA_SET_PATH from tests.integration.base import BaseTestCase sa = SAClient() @@ -352,39 +350,6 @@ def test_create_annotation_class_via_ocr_group_type(self): ) -class TestPixelCreateAnnotationClass(BaseTestCase): - PROJECT_NAME = "TestCreateAnnotationClassPixel" - PROJECT_TYPE = "Pixel" - PROJECT_DESCRIPTION = "Example " - TEST_LARGE_CLASSES_JSON = "large_classes_json.json" - - @property - def large_json_path(self): - return os.path.join(DATA_SET_PATH, self.TEST_LARGE_CLASSES_JSON) - - def test_create_annotation_class_with_default_attribute(self): - with self.assertRaisesRegexp( - AppException, - 'The "default_value" key is not supported for project type Pixel.', - ): - sa.create_annotation_class( - self.PROJECT_NAME, - "test_add", - "#FF0000", - attribute_groups=[ - { - "name": "test", - "attributes": [ - {"name": "Car"}, - {"name": "Track"}, - {"name": "Bus"}, - ], - "default_value": "Bus", - } - ], - ) - - class TestDocumentAnnotationClasses(BaseTestCase): PROJECT_NAME = "TestDocumentAnnotationClasses" PROJECT_DESCRIPTION = "desc" diff --git a/tests/integration/classes/test_create_annotation_classes_from_classes_json.py b/tests/integration/classes/test_create_annotation_classes_from_classes_json.py index f80705183..d311b6b6d 100644 --- a/tests/integration/classes/test_create_annotation_classes_from_classes_json.py +++ b/tests/integration/classes/test_create_annotation_classes_from_classes_json.py @@ -157,42 +157,3 @@ def test_create_annotation_class_via_json_and_ocr_group_type(self): sa.create_annotation_classes_from_classes_json( self.PROJECT_NAME, temp_path ) - - -class TestPixelCreateAnnotationClass(BaseTestCase): - PROJECT_NAME = "TestCreateAnnotationClassPixel" - PROJECT_TYPE = "Pixel" - PROJECT_DESCRIPTION = "Example " - TEST_LARGE_CLASSES_JSON = "large_classes_json.json" - - @property - def large_json_path(self): - return os.path.join(DATA_SET_PATH, self.TEST_LARGE_CLASSES_JSON) - - def test_create_annotation_classes_with_default_attribute(self): - with self.assertRaisesRegexp( - AppException, - 'The "default_value" key is not supported for project type Pixel.', - ): - sa.create_annotation_classes_from_classes_json( - self.PROJECT_NAME, - classes_json=[ - { - "name": "Personal vehicle", - "color": "#ecb65f", - "createdAt": "2020-10-12T11:35:20.000Z", - "updatedAt": "2020-10-12T11:48:19.000Z", - "attribute_groups": [ - { - "name": "test", - "attributes": [ - {"name": "Car"}, - {"name": "Track"}, - {"name": "Bus"}, - ], - "default_value": "Bus", - } - ], - } - ], - ) diff --git a/tests/integration/items/test_get_item_metadata.py b/tests/integration/items/test_get_item_metadata.py index 413772d9a..0786bd353 100644 --- a/tests/integration/items/test_get_item_metadata.py +++ b/tests/integration/items/test_get_item_metadata.py @@ -61,26 +61,6 @@ def test_get_item_by_id(self): assert item_by_id["name"] == self.IMAGE_NAME -class TestGetEntityMetadataPixel(BaseTestCase): - PROJECT_NAME = "TestGetEntityMetadataPixel" - PROJECT_DESCRIPTION = "TestGetEntityMetadataPixel" - PROJECT_TYPE = "Pixel" - TEST_FOLDER_PATH = "data_set/sample_project_vector" - IMAGE_NAME = "example_image_1.jpg" - - @property - def folder_path(self): - return os.path.join(Path(__file__).parent.parent.parent, self.TEST_FOLDER_PATH) - - def test_get_item_metadata(self): - sa.upload_images_from_folder_to_project( - self.PROJECT_NAME, self.folder_path, annotation_status="InProgress" - ) - item_metadata = sa.get_item_metadata(self.PROJECT_NAME, self.IMAGE_NAME) - assert item_metadata["path"] == f"{self.PROJECT_NAME}" - assert item_metadata["annotation_status"] == "InProgress" - - class TestGetEntityMetadataVideo(BaseTestCase): PROJECT_NAME = "TestGetEntityMetadataVideo" PROJECT_DESCRIPTION = "TestGetEntityMetadataVideo" diff --git a/tests/integration/items/test_saqul_query.py b/tests/integration/items/test_saqul_query.py index d74281df6..1e964c3ba 100644 --- a/tests/integration/items/test_saqul_query.py +++ b/tests/integration/items/test_saqul_query.py @@ -1,5 +1,4 @@ import os -from pathlib import Path from src.superannotate import SAClient from tests import DATA_SET_PATH @@ -74,21 +73,3 @@ def test_validate_saqul_query(self): ) except Exception as e: self.assertEqual(str(e), "Incorrect query.") - - -class TestUnsupportedProjectEntitiesSearchVector(BaseTestCase): - PROJECT_NAME = "TestUnsupportedProjectEntitiesSearchVector" - PROJECT_DESCRIPTION = "TestEntitiesSearchVector" - PROJECT_TYPE = "Pixel" - TEST_QUERY = "instance(type =bbox )" - TEST_INVALID_QUERY = "!instance(type =bbox )!" - - @property - def folder_path(self): - return os.path.join(Path(__file__).parent.parent.parent, self.TEST_FOLDER_PATH) - - def test_query(self): - try: - sa.query(self.PROJECT_NAME, self.TEST_QUERY) - except Exception as e: - self.assertEqual(str(e), "Unsupported project type.") diff --git a/tests/integration/projects/test_basic_project.py b/tests/integration/projects/test_basic_project.py index 726ced58b..9cceba2a3 100644 --- a/tests/integration/projects/test_basic_project.py +++ b/tests/integration/projects/test_basic_project.py @@ -1,11 +1,8 @@ import json import os -import tempfile from pathlib import Path -import pytest from src.superannotate import SAClient -from tests import DATA_SET_PATH from tests.integration.base import BaseTestCase sa = SAClient() @@ -169,107 +166,3 @@ def test_include_complete_image_count(self): self.PROJECT_NAME, include_complete_item_count=True ) assert metadata["completed_items_count"] == 4 - - -class TestProject(BaseTestCase): - PROJECT_NAME = "sample_basic_project" - PROJECT_TYPE = "Pixel" - PROJECT_DESCRIPTION = "DESCRIPTION" - TEST_IMAGE_NAME = "example_image_1.jpg" - TEST_FOLDER_PATH = "sample_project_pixel" - TEST_ANNOTATION_PATH = "sample_annotation_no_class" - PNG_POSTFIX = "*___save.png" - FUSE_PNG_POSTFIX = "*___fuse.png" - - @property - def folder_path(self): - return Path(Path(os.path.join(DATA_SET_PATH, self.TEST_FOLDER_PATH))) - - @property - def annotations_path(self): - return Path(Path(os.path.join(DATA_SET_PATH, self.TEST_ANNOTATION_PATH))) - - @property - def classes_json_path(self): - return self.folder_path / "classes" / "classes.json" - - def test_basic_project(self): - - sa.upload_images_from_folder_to_project( - self.PROJECT_NAME, self.folder_path, annotation_status="InProgress" - ) - - count_in_folder = len(list(self.folder_path.glob("*.jpg"))) + len( - list(self.folder_path.glob("*.png")) - ) - count_in_folder -= len(list(self.folder_path.glob(self.FUSE_PNG_POSTFIX))) - count_in_folder -= len(list(self.folder_path.glob(self.PNG_POSTFIX))) - images = sa.search_items(self.PROJECT_NAME) - assert count_in_folder == len(images) - - sa.create_annotation_classes_from_classes_json( - self.PROJECT_NAME, self.classes_json_path - ) - classes_in_file = json.load(open(self.classes_json_path)) - classes_in_project = sa.search_annotation_classes(self.PROJECT_NAME) - with tempfile.TemporaryDirectory() as temp_dir: - json.dump(classes_in_project, open(Path(temp_dir) / "tmp_c.json", "w")) - self.assertEqual(len(classes_in_file), len(classes_in_project)) - classes_in_file_names = [ - annotation_class["name"] for annotation_class in classes_in_file - ] - classes_in_project_names = [ - annotation_class["name"] for annotation_class in classes_in_project - ] - self.assertTrue(set(classes_in_file_names) & set(classes_in_project_names)) - - sa.upload_annotations_from_folder_to_project( - self.PROJECT_NAME, str(self.folder_path) - ) - - export = sa.prepare_export(self.PROJECT_NAME) - - sa.download_export(self.PROJECT_NAME, export, temp_dir) - for image in self.folder_path.glob("*.[jpg|png]"): - found = False - for image_in_project in Path(temp_dir).glob("*.jpg"): - if image.name == image_in_project.name: - found = True - break - assert found, image - - for json_in_folder in self.folder_path.glob("*.json"): - found = False - for json_in_project in Path(temp_dir).glob("*.json"): - if ( - json_in_folder.name.replace("___pixel", "") - == json_in_project.name - ): - found = True - break - assert found, json_in_folder - if self.PROJECT_TYPE == "Pixel": - for mask_in_folder in self.folder_path.glob(self.PNG_POSTFIX): - found = False - for mask_in_project in Path(temp_dir).glob(self.PNG_POSTFIX): - if mask_in_folder.name == mask_in_project.name: - found = True - break - self.assertTrue(found and mask_in_folder) - - @pytest.mark.skip(reason="response from backend does not match with expected") - def test_upload_annotations(self): - sa.upload_images_from_folder_to_project( - self.PROJECT_NAME, self.annotations_path, annotation_status="InProgress" - ) - sa.create_annotation_classes_from_classes_json( - self.PROJECT_NAME, self.classes_json_path - ) - sa.upload_annotations_from_folder_to_project( - self.PROJECT_NAME, str(self.folder_path) - ) - annotations = sa.get_annotations(self.PROJECT_NAME, [self.TEST_IMAGE_NAME])[0] - truth_path = self.annotations_path / "truth.json" - with open(truth_path) as f: - data = json.loads(f.read()) - self.assertEqual(data, annotations) diff --git a/tests/integration/projects/test_create_project.py b/tests/integration/projects/test_create_project.py index e873b7534..c89b0935d 100644 --- a/tests/integration/projects/test_create_project.py +++ b/tests/integration/projects/test_create_project.py @@ -91,7 +91,7 @@ def test_create_project_datetime(self): def test_create_project_with_wrong_type(self): with self.assertRaisesRegexp( AppException, - "Available values are 'Vector', 'Pixel', 'Video', 'Document', 'Tiled', 'PointCloud', 'Multimodal'.", + "Available values are 'Vector', 'Video', 'Document', 'Tiled', 'PointCloud', 'Multimodal'.", ): sa.create_project(self.PROJECT, "desc", "wrong_type") diff --git a/tests/integration/projects/test_get_project_metadata.py b/tests/integration/projects/test_get_project_metadata.py index aaf005e66..012cc5215 100644 --- a/tests/integration/projects/test_get_project_metadata.py +++ b/tests/integration/projects/test_get_project_metadata.py @@ -26,7 +26,6 @@ class TestGetProjectMetadata(BaseTestCase): "workflow_id", "workflow", "upload_state", - "users", "contributors", "settings", "classes", diff --git a/tests/integration/test_basic_images.py b/tests/integration/test_basic_images.py index 51bf3527b..bda57c896 100644 --- a/tests/integration/test_basic_images.py +++ b/tests/integration/test_basic_images.py @@ -1,7 +1,5 @@ import os -import tempfile from os.path import dirname -from pathlib import Path import pytest from src.superannotate import AppException @@ -109,60 +107,3 @@ def folder_path(self): @property def classes_json_path(self): return f"{self.folder_path}/classes/classes.json" - - -class TestPixelImages(BaseTestCase): - PROJECT_NAME = "sample_project_pixel" - PROJECT_TYPE = "Pixel" - PROJECT_DESCRIPTION = "Example Project test pixel basic images" - TEST_FOLDER_PTH = "data_set/sample_project_pixel" - EXAMPLE_IMAGE_1 = "example_image_1.jpg" - - @property - def folder_path(self): - return os.path.join(dirname(dirname(__file__)), self.TEST_FOLDER_PTH) - - @property - def classes_json_path(self): - return f"{self.folder_path}/classes/classes.json" - - def test_basic_images(self): - with tempfile.TemporaryDirectory() as temp_dir: - sa.upload_images_from_folder_to_project( - self.PROJECT_NAME, self.folder_path, annotation_status="InProgress" - ) - sa.create_annotation_classes_from_classes_json( - self.PROJECT_NAME, self.classes_json_path - ) - image = sa.get_item_metadata(self.PROJECT_NAME, "example_image_1.jpg") - del image["createdAt"] - del image["updatedAt"] - truth = { - "name": "example_image_1.jpg", - "annotation_status": "InProgress", - "approval_status": None, - "annotator_email": None, - "qa_email": None, - "entropy_value": None, - } - - assert all([truth[i] == image[i] for i in truth]) - - sa.upload_image_annotations( - project=self.PROJECT_NAME, - image_name=self.EXAMPLE_IMAGE_1, - annotation_json=f"{self.folder_path}/{self.EXAMPLE_IMAGE_1}___pixel.json", - ) - downloaded = sa.download_image( - project=self.PROJECT_NAME, - image_name=self.EXAMPLE_IMAGE_1, - local_dir_path=temp_dir, - include_annotations=True, - ) - self.assertNotEqual(downloaded[1], (None, None)) - self.assertGreater(len(downloaded[0]), 0) - - sa.download_image_annotations( - self.PROJECT_NAME, self.EXAMPLE_IMAGE_1, temp_dir - ) - self.assertEqual(len(list(Path(temp_dir).glob("*"))), 3) diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 99fcf0fd1..bb4bf2405 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -28,7 +28,6 @@ class CLITest(TestCase): ) TEST_VIDEO_PATH = "data_set/sample_videos/single" TEST_VECTOR_FOLDER_PATH = "data_set/sample_project_vector" - TEST_PIXEL_FOLDER_PATH = "data_set/sample_project_pixel" TEST_RECURSIVE_FOLDER_PATH = "data_set/sample_recursive_test" TEST_TEXT_CSV_PATH = "data_set/csv_files/text_urls.csv" TEST_IMAGE_CSV_PATH = "data_set/csv_files/image_urls.csv" @@ -61,12 +60,6 @@ def vector_folder_path(self): Path(os.path.join(dirname(dirname(__file__)), self.TEST_VECTOR_FOLDER_PATH)) ) - @property - def pixel_folder_path(self): - return Path( - Path(os.path.join(dirname(dirname(__file__)), self.TEST_PIXEL_FOLDER_PATH)) - ) - @property def recursive_folder_path(self): return Path( diff --git a/tests/integration/test_convert_project_type.py b/tests/integration/test_convert_project_type.py deleted file mode 100644 index bccd375ea..000000000 --- a/tests/integration/test_convert_project_type.py +++ /dev/null @@ -1,44 +0,0 @@ -import json -import os -import tempfile -from pathlib import Path -from unittest import TestCase - -from src.superannotate import convert_project_type -from tests import DATA_SET_PATH - - -class TestConvertProjectType(TestCase): - TEST_FOLDER_PATH = "pixel_with_holes" - FIRST_IMAGE = "1.jpg" - SECOND_IMAGE = "2.webp" - - @property - def folder_path(self): - return os.path.join(DATA_SET_PATH, self.TEST_FOLDER_PATH) - - def test_convert_pixel_with_holes_to_vector(self): - with tempfile.TemporaryDirectory() as temp_dir: - convert_project_type(self.folder_path, temp_dir, "Vector") - - assert len(list(Path(temp_dir).glob("*"))) == 5 - annotation_files = [i.name for i in Path(temp_dir).glob("*.json")] - assert len(annotation_files) == 2 - with open(os.path.join(temp_dir, f"{self.SECOND_IMAGE}.json")) as file: - data = json.load(file) - assert len(data["instances"][0]["exclude"]) == 4 - - -class TestConvertProjectTypeVector(TestCase): - TEST_FOLDER_PATH = "sample_project_vector" - - @property - def folder_path(self): - return os.path.join(DATA_SET_PATH, self.TEST_FOLDER_PATH) - - def test_convert_pixel_with_holes_to_vector(self): - with tempfile.TemporaryDirectory() as temp_dir: - convert_project_type(self.folder_path, temp_dir, "Pixel") - assert len(list(Path(temp_dir).glob("*"))) == 13 - annotation_files = [i.name for i in Path(temp_dir).glob("*.json")] - assert len(annotation_files) == 4 diff --git a/tests/integration/test_fuse_gen.py b/tests/integration/test_fuse_gen.py deleted file mode 100644 index deac7285e..000000000 --- a/tests/integration/test_fuse_gen.py +++ /dev/null @@ -1,134 +0,0 @@ -import os -import pathlib -import tempfile -from os.path import dirname -from unittest import TestCase - -import numpy as np -import pytest -from PIL import Image -from src.superannotate import SAClient - -sa = SAClient() - - -class TestFolders(TestCase): - VECTOR_PROJECT_NAME = "vector test fuse" - PIXEL_PROJECT_NAME = "pixel test fuse" - PIXEL_PROJECT_NAME_FOR_FUSE = "pixel test fuse 1" - TEST_VECTOR_FOLDER_PATH = "data_set/sample_project_vector" - TEST_PIXEL_FOLDER_PATH = "data_set/sample_project_pixel" - PROJECT_DESCRIPTION = "desc" - PROJECT_TYPE = "Pixel" - TEST_FOLDER_NAME_1 = "folder_1" - TEST_FOLDER_NAME_2 = "folder_2" - EXAMPLE_IMAGE_1 = "example_image_1.jpg" - EXAMPLE_IMAGE_2 = "example_image_2" - - @classmethod - def setUpClass(cls): - cls.tearDownClass() - cls._vector_project = sa.create_project( - cls.VECTOR_PROJECT_NAME, cls.PROJECT_DESCRIPTION, "Vector" - ) - cls._pixel_project = sa.create_project( - cls.PIXEL_PROJECT_NAME, cls.PROJECT_DESCRIPTION, "Pixel" - ) - - cls._pixel_project_2 = sa.create_project( - cls.PIXEL_PROJECT_NAME_FOR_FUSE, cls.PROJECT_DESCRIPTION, "Pixel" - ) - - @classmethod - def tearDownClass(cls) -> None: - projects = ( - sa.search_projects(cls.VECTOR_PROJECT_NAME, return_metadata=True) - + sa.search_projects(cls.PIXEL_PROJECT_NAME, return_metadata=True) - + sa.search_projects(cls.PIXEL_PROJECT_NAME_FOR_FUSE, return_metadata=True) - ) - for project in projects: - sa.delete_project(project) - - @property - def vector_folder_path(self): - return os.path.join(dirname(dirname(__file__)), self.TEST_VECTOR_FOLDER_PATH) - - @property - def pixel_folder_path(self): - return os.path.join(dirname(dirname(__file__)), self.TEST_PIXEL_FOLDER_PATH) - - @property - def vector_classes_json(self): - return f"{self.vector_folder_path}/classes/classes.json" - - @property - def pixel_classes_json(self): - return f"{self.pixel_folder_path}/classes/classes.json" - - def test_fuse_image_create_pixel(self): - with tempfile.TemporaryDirectory() as temp_dir: - temp_dir = pathlib.Path(temp_dir) - - sa.upload_image_to_project( - self.PIXEL_PROJECT_NAME, - f"{self.pixel_folder_path}/{self.EXAMPLE_IMAGE_1}", - annotation_status="QualityCheck", - ) - - sa.create_annotation_classes_from_classes_json( - self.PIXEL_PROJECT_NAME, self.pixel_classes_json - ) - sa.upload_image_annotations( - project=self.PIXEL_PROJECT_NAME, - image_name=self.EXAMPLE_IMAGE_1, - annotation_json=f"{self.pixel_folder_path}/{self.EXAMPLE_IMAGE_1}___pixel.json", - mask=f"{self.pixel_folder_path}/{self.EXAMPLE_IMAGE_1}___save.png", - ) - - export = sa.prepare_export(self.PIXEL_PROJECT_NAME, include_fuse=True)[ - "name" - ] - (temp_dir / "export").mkdir() - sa.download_export(self.PIXEL_PROJECT_NAME, export, (temp_dir / "export")) - - paths = sa.download_image( - self.PIXEL_PROJECT_NAME, - self.EXAMPLE_IMAGE_1, - temp_dir, - include_annotations=True, - include_fuse=True, - ) - im1 = Image.open(temp_dir / "export" / f"{self.EXAMPLE_IMAGE_1}___fuse.png") - im1_array = np.array(im1) - - im2 = Image.open(paths[2][0]) - im2_array = np.array(im2) - - self.assertEqual(im1_array.shape, im2_array.shape) - self.assertEqual(im1_array.dtype, im2_array.dtype) - self.assertTrue(np.array_equal(im1_array, im2_array)) - - @pytest.mark.flaky(reruns=4) - def test_fuse_image_create_pixel_with_no_classes(self): - with tempfile.TemporaryDirectory() as temp_dir: - temp_dir = pathlib.Path(temp_dir) - - sa.upload_image_to_project( - self.PIXEL_PROJECT_NAME_FOR_FUSE, - f"{self.pixel_folder_path}/{self.EXAMPLE_IMAGE_1}", - annotation_status="QualityCheck", - ) - sa.upload_image_annotations( - project=self.PIXEL_PROJECT_NAME_FOR_FUSE, - image_name=self.EXAMPLE_IMAGE_1, - annotation_json=f"{self.pixel_folder_path}/{self.EXAMPLE_IMAGE_1}___pixel.json", - mask=f"{self.pixel_folder_path}/{self.EXAMPLE_IMAGE_1}___save.png", - ) - - sa.download_image( - self.PIXEL_PROJECT_NAME_FOR_FUSE, - self.EXAMPLE_IMAGE_1, - temp_dir, - include_annotations=True, - include_fuse=True, - ) diff --git a/tests/integration/test_image_quality.py b/tests/integration/test_image_quality.py index 0d5a1adc1..b4534b989 100644 --- a/tests/integration/test_image_quality.py +++ b/tests/integration/test_image_quality.py @@ -3,8 +3,6 @@ import tempfile from os.path import dirname -import pytest -from src.superannotate import AppException from src.superannotate import SAClient from tests.integration.base import BaseTestCase @@ -65,30 +63,6 @@ def test_image_quality_setting2(self): ) -class TestPixelImageQuality(BaseTestCase): - PROJECT_NAME = "pixel image q" - PROJECT_DESCRIPTION = "Desc" - PROJECT_TYPE = "Pixel" - TEST_FOLDER_PATH = "data_set/big_img" - BIG_IMAGE = "big.jpg" - MESSAGE = ( - "Image resolution 44761088 too large. Max supported for resolution is 4000000" - ) - - @property - def folder_path(self): - return os.path.join(dirname(dirname(__file__)), self.TEST_FOLDER_PATH) - - @pytest.mark.flaky(reruns=2) - def test_big_image(self): - try: - sa.upload_image_to_project( - self.PROJECT_NAME, f"{self.folder_path}/{self.BIG_IMAGE}" - ) - except AppException as e: - self.assertEqual(str(e), self.MESSAGE) - - class TestImageQualitySetting(BaseTestCase): PROJECT_NAME = "TestImageQualitySetting Test" PROJECT_DESCRIPTION = "Desc" diff --git a/tests/integration/test_interface.py b/tests/integration/test_interface.py index a1b867a6b..8c2ded987 100644 --- a/tests/integration/test_interface.py +++ b/tests/integration/test_interface.py @@ -2,8 +2,6 @@ import tempfile from os.path import dirname -import pytest -from src.superannotate import export_annotation from src.superannotate import SAClient from tests.integration.base import BaseTestCase @@ -89,76 +87,3 @@ def test_download_fuse_without_classes(self): include_annotations=True, ) self.assertIsNotNone(result) - - -class TestPixelInterface(BaseTestCase): - PROJECT_NAME = "Interface Pixel test" - TEST_FOLDER_PATH = "sample_project_pixel" - PROJECT_DESCRIPTION = "desc" - PROJECT_TYPE = "Pixel" - TEST_FOLDER_NAME = "folder" - EXAMPLE_IMAGE_1 = "example_image_1.jpg" - - @property - def data_set_path(self): - return os.path.join(dirname(dirname(__file__)), "data_set") - - @property - def folder_path(self): - return os.path.join(self.data_set_path, self.TEST_FOLDER_PATH) - - @pytest.mark.flaky(reruns=2) - def test_export_annotation(self): - sa.upload_image_to_project( - self.PROJECT_NAME, f"{self.folder_path}/{self.EXAMPLE_IMAGE_1}" - ) - sa.create_annotation_classes_from_classes_json( - self.PROJECT_NAME, f"{self.folder_path}/classes/classes.json" - ) - sa.upload_image_annotations( - self.PROJECT_NAME, - self.EXAMPLE_IMAGE_1, - f"{self.folder_path}/{self.EXAMPLE_IMAGE_1}___pixel.json", - ) - with tempfile.TemporaryDirectory() as export_dir: - result = sa.prepare_export( - self.PROJECT_NAME, - ) - sa.download_export(self.PROJECT_NAME, result, export_dir, True) - with tempfile.TemporaryDirectory() as convert_path: - export_annotation( - export_dir, - convert_path, - "COCO", - "data_set_name", - "Pixel", - "panoptic_segmentation", - ) - pass - - @pytest.mark.skip(reason="Need to adjust") - def test_create_folder_with_special_character(self): - with self.assertLogs() as logs: - folder_1 = sa.create_folder(self.PROJECT_NAME, "**abc") - folder_2 = sa.create_folder(self.PROJECT_NAME, "**abc") - self.assertEqual(folder_1["name"], "__abc") - self.assertEqual(folder_2["name"], "__abc (1)") - self.assertIn( - "New folder name has special characters. Special characters will be replaced by underscores.", - logs.output[0], - ) - self.assertIn( - "Folder __abc created in project Interface Pixel test", logs.output[1] - ) - self.assertIn( - "New folder name has special characters. Special characters will be replaced by underscores.", - logs.output[2], - ) - self.assertIn( - "Created folder has name __abc (1), since folder with name __abc already existed.", - logs.output[3], - ) - self.assertIn( - "Folder __abc (1) created in project Interface Pixel test", - logs.output[4], - )