From ab70fd9fc54795d569407449f110d69523a314fd Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 22:29:07 +0900 Subject: [PATCH 1/7] add vscode config --- .vscode/extensions.json | 7 +++++++ .vscode/settings.json | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..49cd1e1 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "ms-python.python", + "streetsidesoftware.code-spell-checker" + ], + "unwantedRecommendations": [] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..01d8fa0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.formatting.provider": "black", + "python.linting.flake8Enabled": true, + "python.linting.enabled": true +} From c5069bcccd27778fa8a9408607ff409705dbfb61 Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 22:33:55 +0900 Subject: [PATCH 2/7] add ignore directory --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e4e9bcf..8e0f50c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ annotation.json *.png *.mp4 tests/ -output/ \ No newline at end of file +output/ +.eggs/ +venv/ From 48247a34745aba8a08e1dcbd41051c7bd7e8b3e8 Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 23:03:15 +0900 Subject: [PATCH 3/7] Organize linter and formatter settings --- tox.ini | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 2859084..c2f0717 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,10 @@ -[tool.isort] +[isort] +profile = black include_trailing_comma = true line_length = 88 -multi_line_output = 3 \ No newline at end of file +multi_line_output = 3 + +[flake8] +max-line-length = 88 +extend-ignore = E203 +exclude = .git,__pycache__,.tox,.eggs,*.egg,build,dist,venv From 1b084dcdf2b113694be5b041bfc453c25c38d147 Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 23:21:53 +0900 Subject: [PATCH 4/7] upgrade black --- .pre-commit-config.yaml | 2 +- contributing/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6312378..c2ff8d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.10.0 hooks: - id: black files: '^.*\.py' diff --git a/contributing/requirements.txt b/contributing/requirements.txt index e892e8f..70c1345 100644 --- a/contributing/requirements.txt +++ b/contributing/requirements.txt @@ -1,4 +1,4 @@ -black==20.8b1 +black==22.10.0 flake8==3.8.4 isort==5.6.4 pre-commit==2.16.0 From abcf43a978e1442a5da2f44410412d78120eeccf Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 23:36:19 +0900 Subject: [PATCH 5/7] disable line text character count check in flake8 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c2f0717..7b5a44d 100644 --- a/tox.ini +++ b/tox.ini @@ -6,5 +6,5 @@ multi_line_output = 3 [flake8] max-line-length = 88 -extend-ignore = E203 +extend-ignore = E203,E501 exclude = .git,__pycache__,.tox,.eggs,*.egg,build,dist,venv From c31cef412db11364591fcabee67d0bc03b47b4a8 Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 23:40:58 +0900 Subject: [PATCH 6/7] Fix so that each pre-commit tool refers to config --- .pre-commit-config.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c2ff8d9..be50f73 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,13 +9,11 @@ repos: rev: 3.8.4 hooks: - id: flake8 - args: ['--extend-ignore=E203', '--max-line-length=88'] files: '^.*\.py' types: [file] - repo: https://github.com/pycqa/isort rev: 5.6.4 hooks: - id: isort - args: ['--profile', 'black'] files: '^.*\.py' types: [file] From 985edf014467f130c82b326c44d9d2b54d4bd1c7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Fujimoto Date: Sun, 30 Oct 2022 23:42:56 +0900 Subject: [PATCH 7/7] format all --- examples/create_image_task.py | 34 ++-- examples/download_images.py | 13 +- fastlabel/__init__.py | 35 ++-- fastlabel/api.py | 13 +- fastlabel/converters.py | 296 ++++++++++++++++++++-------------- setup.py | 4 +- 6 files changed, 215 insertions(+), 180 deletions(-) diff --git a/examples/create_image_task.py b/examples/create_image_task.py index a7c5b95..cab5402 100644 --- a/examples/create_image_task.py +++ b/examples/create_image_task.py @@ -8,27 +8,21 @@ project = "YOUR_PROJECT_SLUG" name = "YOUR_DATA_NAME" file_path = "YOUR_DATA_FILE_PATH" # e.g.) ./cat.jpg -annotations = [{ - "type": "bbox", - "value": "cat", - "attributes": [ - { - "key": "kind", - "value": "Scottish field" - } - ], - "points": [ - 100, # top-left x - 100, # top-left y - 200, # bottom-right x - 200 # bottom-right y - ] -}] +annotations = [ + { + "type": "bbox", + "value": "cat", + "attributes": [{"key": "kind", "value": "Scottish field"}], + "points": [ + 100, # top-left x + 100, # top-left y + 200, # bottom-right x + 200, # bottom-right y + ], + } +] task_id = client.create_image_task( - project=project, - name=name, - file_path=file_path, - annotations=annotations + project=project, name=name, file_path=file_path, annotations=annotations ) pprint(task_id) diff --git a/examples/download_images.py b/examples/download_images.py index 3c4797f..25f54a4 100644 --- a/examples/download_images.py +++ b/examples/download_images.py @@ -1,14 +1,15 @@ -import fastlabel import os import time import urllib.request from multiprocessing import Pool, cpu_count +import fastlabel client = fastlabel.Client() IMAGE_DIR = "images" PROJECT_SLUG = "YOUR_PROJECT_SLUG" + def get_all_tasks() -> list: # Iterate pages until new tasks are empty. all_tasks = [] @@ -17,9 +18,7 @@ def get_all_tasks() -> list: time.sleep(1) tasks = client.get_image_classification_tasks( - project=PROJECT_SLUG, - limit=1000, - offset=offset + project=PROJECT_SLUG, limit=1000, offset=offset ) all_tasks.extend(tasks) @@ -30,13 +29,15 @@ def get_all_tasks() -> list: return all_tasks + def download_image(task: dict): urllib.request.urlretrieve(task["url"], os.path.join(IMAGE_DIR, task["name"])) -if __name__ == '__main__': + +if __name__ == "__main__": os.makedirs(IMAGE_DIR, exist_ok=True) tasks = get_all_tasks() with Pool(cpu_count()) as p: - p.map(download_image, tasks) \ No newline at end of file + p.map(download_image, tasks) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index e3de462..29e5776 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -1,14 +1,16 @@ import glob import json +import logging import os import re -import logging -from typing import List from concurrent.futures import ThreadPoolExecutor +from typing import List + import cv2 import numpy as np import xmltodict from PIL import Image, ImageColor, ImageDraw + from fastlabel import const, converters, utils from fastlabel.const import ( EXPORT_IMAGE_WITH_ANNOTATIONS_SUPPORTED_IMAGE_TYPES, @@ -1614,7 +1616,7 @@ def find_integrated_video_task_by_prefix( """ endpoint = "tasks/integrate/videos" params = {"project": project, "prefix": prefix} - + return self.api.get_request(endpoint, params=params) def find_integrated_audio_task_by_prefix( @@ -1630,9 +1632,8 @@ def find_integrated_audio_task_by_prefix( """ endpoint = "tasks/integrate/audios" params = {"project": project, "prefix": prefix} - - return self.api.get_request(endpoint, params=params) + return self.api.get_request(endpoint, params=params) # Convert to Fastlabel @@ -1673,7 +1674,9 @@ def convert_coco_to_fastlabel(self, file_path: str, annotation_type: str) -> dic ] } """ - return converters.execute_coco_to_fastlabel(json.load(open(file_path)), annotation_type) + return converters.execute_coco_to_fastlabel( + json.load(open(file_path)), annotation_type + ) def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: """ @@ -2826,15 +2829,11 @@ def copy_project(self, project_id: str) -> None: return self.api.post_request(endpoint, payload=payload) def update_aws_s3_storage( - self, - project: str, - bucket_name: str, - bucket_region: str, - prefix: str = None + self, project: str, bucket_name: str, bucket_region: str, prefix: str = None ) -> str: """ Insert or update AWS S3 storage settings. - + project is a slug of the project (Required). bucket_name is a bucket name of the aws s3 (Required). bucket_region is a bucket region of the aws s3 (Required). @@ -2842,7 +2841,7 @@ def update_aws_s3_storage( If sample_dir is specified as a prefix in the case of a hierarchical structure like the bucket below, only the data under the sample_dir directory will be linked. If not specified, everything under the bucket will be linked. - + [tree structure] fastlabel ├── sample1.jpg @@ -2858,9 +2857,9 @@ def update_aws_s3_storage( if prefix: payload["prefix"] = prefix return self.api.put_request(endpoint, payload=payload) - + def create_task_from_aws_s3( - self, + self, project: str, status: str = "registered", tags: List[str] = [], @@ -2868,7 +2867,7 @@ def create_task_from_aws_s3( ) -> dict: """ Insert or update AWS S3 storage settings. - + project is a slug of the project (Required). status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', 'approved', 'declined' (default: registered) (Optional). @@ -2888,9 +2887,9 @@ def create_task_from_aws_s3( "priority": priority, } return self.api.post_request(endpoint, payload=payload) - + def get_aws_s3_import_status_by_project( - self, + self, project: str, ) -> dict: """ diff --git a/fastlabel/api.py b/fastlabel/api.py index fafca89..c8f187b 100644 --- a/fastlabel/api.py +++ b/fastlabel/api.py @@ -1,4 +1,5 @@ import os + import requests from .exceptions import FastLabelException, FastLabelInvalidException @@ -13,8 +14,7 @@ class Api: def __init__(self): if not os.environ.get("FASTLABEL_ACCESS_TOKEN"): raise ValueError("FASTLABEL_ACCESS_TOKEN is not configured.") - self.access_token = "Bearer " + \ - os.environ.get("FASTLABEL_ACCESS_TOKEN") + self.access_token = "Bearer " + os.environ.get("FASTLABEL_ACCESS_TOKEN") def get_request(self, endpoint: str, params=None) -> dict: """Makes a get request to an endpoint. @@ -27,8 +27,7 @@ def get_request(self, endpoint: str, params=None) -> dict: "Content-Type": "application/json", "Authorization": self.access_token, } - r = requests.get(FASTLABEL_ENDPOINT + endpoint, - headers=headers, params=params) + r = requests.get(FASTLABEL_ENDPOINT + endpoint, headers=headers, params=params) if r.status_code == 200: return r.json() @@ -78,8 +77,7 @@ def post_request(self, endpoint, payload=None): "Content-Type": "application/json", "Authorization": self.access_token, } - r = requests.post(FASTLABEL_ENDPOINT + endpoint, - json=payload, headers=headers) + r = requests.post(FASTLABEL_ENDPOINT + endpoint, json=payload, headers=headers) if r.status_code == 200: return r.json() @@ -104,8 +102,7 @@ def put_request(self, endpoint, payload=None): "Content-Type": "application/json", "Authorization": self.access_token, } - r = requests.put(FASTLABEL_ENDPOINT + endpoint, - json=payload, headers=headers) + r = requests.put(FASTLABEL_ENDPOINT + endpoint, json=payload, headers=headers) if r.status_code == 200: return r.json() diff --git a/fastlabel/converters.py b/fastlabel/converters.py index f9b4745..dc220fe 100644 --- a/fastlabel/converters.py +++ b/fastlabel/converters.py @@ -1,15 +1,15 @@ +import copy +import math +import os from concurrent.futures import ThreadPoolExecutor from datetime import datetime from decimal import Decimal from typing import List -import copy import geojson import numpy as np -import math -from fastlabel.const import AnnotationType -import os +from fastlabel.const import AnnotationType from fastlabel.exceptions import FastLabelInvalidException # COCO @@ -37,8 +37,10 @@ def to_coco(tasks: list, annotations: list = []) -> dict: } images.append(image) - data = [{"annotation": annotation, "categories": categories, - "image": image} for annotation in task["annotations"]] + data = [ + {"annotation": annotation, "categories": categories, "image": image} + for annotation in task["annotations"] + ] with ThreadPoolExecutor(max_workers=8) as executor: results = executor.map(__to_annotation, data) @@ -69,7 +71,7 @@ def __get_coco_skeleton(keypoints: list) -> list: edges = keypoint["edges"] for edge in edges: edge_skeleton_index = keypoint_id_skeleton_index_map[edge] - if not edge_skeleton_index in filtered_skeleton_indexes: + if edge_skeleton_index not in filtered_skeleton_indexes: skeleton.append([skeleton_index, edge_skeleton_index]) filtered_skeleton_indexes.append(skeleton_index) return skeleton @@ -80,7 +82,12 @@ def __get_categories(tasks: list, annotations: list) -> list: values = [] for task in tasks: for task_annotation in task["annotations"]: - if task_annotation["type"] not in [AnnotationType.bbox.value, AnnotationType.polygon.value, AnnotationType.segmentation.value, AnnotationType.pose_estimation.value]: + if task_annotation["type"] not in [ + AnnotationType.bbox.value, + AnnotationType.polygon.value, + AnnotationType.segmentation.value, + AnnotationType.pose_estimation.value, + ]: continue values.append(task_annotation["value"]) values = list(set(values)) @@ -95,7 +102,7 @@ def __get_categories(tasks: list, annotations: list) -> list: "color": task_annotation["color"], "supercategory": value, "id": index, - "name": value + "name": value, } categories.append(category) return categories @@ -114,13 +121,15 @@ def __get_categories(tasks: list, annotations: list) -> list: coco_keypoints.append(keypoint["key"]) coco_keypoint_colors.append(keypoint["color"]) coco_skeleton = __get_coco_skeleton(keypoints) - category = {"skeleton": coco_skeleton, - "keypoints": coco_keypoints, - "keypoint_colors": coco_keypoint_colors, - "color": annotation["color"], - "supercategory": annotation["value"], - "id": index, - "name": annotation["value"]} + category = { + "skeleton": coco_skeleton, + "keypoints": coco_keypoints, + "keypoint_colors": coco_keypoint_colors, + "color": annotation["color"], + "supercategory": annotation["value"], + "id": index, + "name": annotation["value"], + } index += 1 categories.append(category) return categories @@ -135,22 +144,32 @@ def __to_annotation(data: dict) -> dict: annotation_type = annotation["type"] annotation_id = 0 - if annotation_type not in [AnnotationType.bbox.value, AnnotationType.polygon.value, AnnotationType.segmentation.value, AnnotationType.pose_estimation.value]: + if annotation_type not in [ + AnnotationType.bbox.value, + AnnotationType.polygon.value, + AnnotationType.segmentation.value, + AnnotationType.pose_estimation.value, + ]: return None - if annotation_type != AnnotationType.pose_estimation.value and (not points or len(points)) == 0: + if ( + annotation_type != AnnotationType.pose_estimation.value + and (not points or len(points)) == 0 + ): return None - if annotation_type == AnnotationType.bbox.value and (int(points[0]) == int(points[2]) or int(points[1]) == int(points[3])): + if annotation_type == AnnotationType.bbox.value and ( + int(points[0]) == int(points[2]) or int(points[1]) == int(points[3]) + ): return None category = __get_category_by_name(categories, annotation["value"]) return __get_annotation( - annotation_id, points, keypoints, category["id"], image, annotation_type) + annotation_id, points, keypoints, category["id"], image, annotation_type + ) def __get_category_by_name(categories: list, name: str) -> str: - category = [ - category for category in categories if category["name"] == name][0] + category = [category for category in categories if category["name"] == name][0] return category @@ -167,13 +186,20 @@ def __get_coco_annotation_keypoints(keypoints: list) -> list: return coco_annotation_keypoints -def __get_annotation(id_: int, points: list, keypoints: list, category_id: int, image: dict, annotation_type: str) -> dict: +def __get_annotation( + id_: int, + points: list, + keypoints: list, + category_id: int, + image: dict, + annotation_type: str, +) -> dict: annotation = {} annotation["num_keypoints"] = len(keypoints) if keypoints else 0 - annotation["keypoints"] = __get_coco_annotation_keypoints( - keypoints) if keypoints else [] - annotation["segmentation"] = __to_coco_segmentation( - annotation_type, points) + annotation["keypoints"] = ( + __get_coco_annotation_keypoints(keypoints) if keypoints else [] + ) + annotation["segmentation"] = __to_coco_segmentation(annotation_type, points) annotation["iscrowd"] = 0 annotation["area"] = __to_area(annotation_type, points) annotation["image_id"] = image["id"] @@ -205,7 +231,7 @@ def __to_bbox(annotation_type: str, points: list) -> list: else: base_points = points points_splitted = [ - base_points[idx: idx + 2] for idx in range(0, len(base_points), 2) + base_points[idx : idx + 2] for idx in range(0, len(base_points), 2) ] polygon_geo = geojson.Polygon(points_splitted) coords = np.array(list(geojson.utils.coords(polygon_geo))) @@ -237,12 +263,13 @@ def __calc_area(annotation_type: str, points: list) -> float: width = points[0] - points[2] height = points[1] - points[3] return width * height - elif annotation_type in [AnnotationType.polygon.value, AnnotationType.segmentation.value]: + elif annotation_type in [ + AnnotationType.polygon.value, + AnnotationType.segmentation.value, + ]: x = points[0::2] y = points[1::2] - return 0.5 * np.abs( - np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)) - ) + return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) else: raise Exception(f"Unsupported annotation type: {annotation_type}") @@ -288,8 +315,8 @@ def __coco2yolo(coco: dict) -> tuple: annos = [] for image in coco["images"]: - dw = 1. / image["width"] - dh = 1. / image["height"] + dw = 1.0 / image["width"] + dh = 1.0 / image["height"] # Get objects objs = [] @@ -322,10 +349,7 @@ def __coco2yolo(coco: dict) -> tuple: objs.append(" ".join(obj)) # get annotation - anno = { - "filename": image["file_name"], - "object": objs - } + anno = {"filename": image["file_name"], "object": objs} annos.append(anno) return annos, categories @@ -337,18 +361,17 @@ def __to_yolo(tasks: list, classes: list) -> tuple: if task["height"] == 0 or task["width"] == 0: continue objs = [] - data = [{"annotation": annotation, "task": task, "classes": classes} - for annotation in task["annotations"]] + data = [ + {"annotation": annotation, "task": task, "classes": classes} + for annotation in task["annotations"] + ] with ThreadPoolExecutor(max_workers=8) as executor: results = executor.map(__get_yolo_annotation, data) for result in results: if not result: continue objs.append(" ".join(result)) - anno = { - "filename": task["name"], - "object": objs - } + anno = {"filename": task["name"], "object": objs} annos.append(anno) categories = map(lambda val: {"name": val}, classes) @@ -363,17 +386,22 @@ def __get_yolo_annotation(data: dict) -> dict: value = annotation["value"] classes = list(data["classes"]) task = data["task"] - if annotation_type != AnnotationType.bbox.value and annotation_type != AnnotationType.polygon.value: + if ( + annotation_type != AnnotationType.bbox.value + and annotation_type != AnnotationType.polygon.value + ): return None if not points or len(points) == 0: return None - if annotation_type == AnnotationType.bbox.value and (int(points[0]) == int(points[2]) or int(points[1]) == int(points[3])): + if annotation_type == AnnotationType.bbox.value and ( + int(points[0]) == int(points[2]) or int(points[1]) == int(points[3]) + ): return None if not annotation["value"] in classes: return None - dw = 1. / task["width"] - dh = 1. / task["height"] + dw = 1.0 / task["width"] + dh = 1.0 / task["height"] bbox = __to_bbox(annotation_type, points) xmin = bbox[0] @@ -395,7 +423,7 @@ def __get_yolo_annotation(data: dict) -> dict: def _truncate(n, decimals=0) -> float: - multiplier = 10 ** decimals + multiplier = 10**decimals return int(n * multiplier) / multiplier @@ -409,8 +437,7 @@ def to_pascalvoc(tasks: list) -> list: continue pascal_objs = [] - data = [{"annotation": annotation} - for annotation in task["annotations"]] + data = [{"annotation": annotation} for annotation in task["annotations"]] with ThreadPoolExecutor(max_workers=8) as executor: results = executor.map(__get_pascalvoc_obj, data) @@ -428,7 +455,7 @@ def to_pascalvoc(tasks: list) -> list: "depth": 3, }, "segmented": 0, - "object": pascal_objs + "object": pascal_objs, } } pascalvoc.append(voc) @@ -439,11 +466,16 @@ def __get_pascalvoc_obj(data: dict) -> dict: annotation = data["annotation"] points = annotation["points"] annotation_type = annotation["type"] - if annotation_type != AnnotationType.bbox.value and annotation_type != AnnotationType.polygon.value: + if ( + annotation_type != AnnotationType.bbox.value + and annotation_type != AnnotationType.polygon.value + ): return None if not points or len(points) == 0: return None - if annotation_type == AnnotationType.bbox.value and (int(points[0]) == int(points[2]) or int(points[1]) == int(points[3])): + if annotation_type == AnnotationType.bbox.value and ( + int(points[0]) == int(points[2]) or int(points[1]) == int(points[3]) + ): return None bbox = __to_bbox(annotation_type, points) x = bbox[0] @@ -454,14 +486,14 @@ def __get_pascalvoc_obj(data: dict) -> dict: return { "name": annotation["value"], "pose": "Unspecified", - "truncated": __get_pascalvoc_tag_value(annotation, "truncated"), - "occluded": __get_pascalvoc_tag_value(annotation, "occluded"), - "difficult": __get_pascalvoc_tag_value(annotation, "difficult"), - "bndbox": { - "xmin": math.floor(x), - "ymin": math.floor(y), - "xmax": math.floor(x + w), - "ymax": math.floor(y + h), + "truncated": __get_pascalvoc_tag_value(annotation, "truncated"), + "occluded": __get_pascalvoc_tag_value(annotation, "occluded"), + "difficult": __get_pascalvoc_tag_value(annotation, "difficult"), + "bndbox": { + "xmin": math.floor(x), + "ymin": math.floor(y), + "xmax": math.floor(x + w), + "ymax": math.floor(y + h), }, } @@ -471,7 +503,13 @@ def __get_pascalvoc_tag_value(annotation: dict, target_tag_name: str) -> int: if not attributes: return 0 related_attr = next( - (attribute for attribute in attributes if attribute["type"] == "switch" and attribute["key"] == target_tag_name), None) + ( + attribute + for attribute in attributes + if attribute["type"] == "switch" and attribute["key"] == target_tag_name + ), + None, + ) return int(related_attr["value"]) if related_attr else 0 @@ -494,7 +532,8 @@ def to_labelme(tasks: list) -> list: if annotation["type"] == "segmentation": for i in range(int(len(points[0][0]) / 2)): shape_points.append( - [points[0][0][i * 2], points[0][0][(i * 2) + 1]]) + [points[0][0][i * 2], points[0][0][(i * 2) + 1]] + ) else: for i in range(int(len(points) / 2)): shape_points.append([points[i * 2], points[(i * 2) + 1]]) @@ -504,18 +543,20 @@ def to_labelme(tasks: list) -> list: "points": shape_points, "group_id": None, "shape_type": shape_type, - "flags": {} + "flags": {}, } shapes.append(shape) - labelmes.append({ - "version": "4.5.9", - "flags": {}, - "shapes": shapes, - "imagePath": task["name"], - "imageData": None, - "imageHeight": task["height"], - "imageWidth": task["width"], - }) + labelmes.append( + { + "version": "4.5.9", + "flags": {}, + "shapes": shapes, + "imagePath": task["name"], + "imageData": None, + "imageHeight": task["height"], + "imageWidth": task["width"], + } + ) return labelmes @@ -559,10 +600,14 @@ def to_pixel_coordinates(tasks: list) -> list: xmax = max([points[0], points[2]]) ymax = max([points[1], points[3]]) annotation["points"] = [ - xmin, ymin, - xmax, ymin, - xmax, ymax, - xmin, ymax, + xmin, + ymin, + xmax, + ymin, + xmax, + ymax, + xmin, + ymax, ] else: continue @@ -580,8 +625,7 @@ def to_pixel_coordinates(tasks: list) -> list: new_regions.append(new_region) annotation["points"] = new_regions elif annotation["type"] == AnnotationType.polygon.value: - new_points = __remove_duplicated_coordinates( - annotation["points"]) + new_points = __remove_duplicated_coordinates(annotation["points"]) annotation["points"] = new_points return tasks @@ -596,29 +640,31 @@ def __remove_duplicated_coordinates(points: List[int]) -> List[int]: new_points = [] for i in range(int(len(points) / 2)): if i == 0: - new_points.append(points[i*2]) - new_points.append(points[i*2 + 1]) + new_points.append(points[i * 2]) + new_points.append(points[i * 2 + 1]) - if new_points[-2] == points[i*2] and new_points[-1] == points[i*2 + 1]: + if new_points[-2] == points[i * 2] and new_points[-1] == points[i * 2 + 1]: continue if len(new_points) <= 2: - new_points.append(points[i*2]) - new_points.append(points[i*2 + 1]) + new_points.append(points[i * 2]) + new_points.append(points[i * 2 + 1]) else: - if new_points[-4] == new_points[-2] and new_points[-2] == points[i*2]: + if new_points[-4] == new_points[-2] and new_points[-2] == points[i * 2]: new_points.pop() new_points.pop() - new_points.append(points[i*2]) - new_points.append(points[i*2 + 1]) - elif new_points[-3] == new_points[-1] and new_points[-1] == points[i*2 + 1]: + new_points.append(points[i * 2]) + new_points.append(points[i * 2 + 1]) + elif ( + new_points[-3] == new_points[-1] and new_points[-1] == points[i * 2 + 1] + ): new_points.pop() new_points.pop() - new_points.append(points[i*2]) - new_points.append(points[i*2 + 1]) + new_points.append(points[i * 2]) + new_points.append(points[i * 2 + 1]) else: - new_points.append(points[i*2]) - new_points.append(points[i*2 + 1]) + new_points.append(points[i * 2]) + new_points.append(points[i * 2 + 1]) return new_points @@ -636,8 +682,8 @@ def __get_pixel_coordinates(points: List[int or float]) -> List[int]: if i == 0: continue - prev_x = int(points[(i-1) * 2]) - prev_y = int(points[(i-1) * 2 + 1]) + prev_x = int(points[(i - 1) * 2]) + prev_y = int(points[(i - 1) * 2 + 1]) x = int(points[i * 2]) y = int(points[i * 2 + 1]) @@ -658,13 +704,13 @@ def __get_pixel_coordinates(points: List[int or float]) -> List[int]: return new_points -def execute_coco_to_fastlabel(coco: dict ,annotation_type:str) -> dict: +def execute_coco_to_fastlabel(coco: dict, annotation_type: str) -> dict: coco_images = {} for c in coco["images"]: coco_images[c["id"]] = c["file_name"] coco_categories = {} - coco_categories_keypoints={} + coco_categories_keypoints = {} for c in coco["categories"]: coco_categories[c["id"]] = c["supercategory"] coco_categories_keypoints[c["id"]] = c["keypoints"] @@ -686,7 +732,10 @@ def execute_coco_to_fastlabel(coco: dict ,annotation_type:str) -> dict: if not category_name: return - if annotation_type in [AnnotationType.bbox.value, AnnotationType.polygon.value]: + if annotation_type in [ + AnnotationType.bbox.value, + AnnotationType.polygon.value, + ]: segmentation = target_coco_annotation["segmentation"][0] annotation_type = "" if len(segmentation) == 4: @@ -703,32 +752,37 @@ def execute_coco_to_fastlabel(coco: dict ,annotation_type:str) -> dict: elif annotation_type == AnnotationType.pose_estimation.value: keypoints = [] target_coco_annotation_keypoints = target_coco_annotation["keypoints"] - keypoint_keys = coco_categories_keypoints[target_coco_annotation["category_id"]] + keypoint_keys = coco_categories_keypoints[ + target_coco_annotation["category_id"] + ] # coco keypoint style [100,200,1,300,400,1,500,600,2] convert to [[100,200,1],[300,400,1],[500,600,2]] - keypoint_values = [target_coco_annotation_keypoints[i:i + 3] for i in range(0, len(target_coco_annotation_keypoints), 3)] + keypoint_values = [ + target_coco_annotation_keypoints[i : i + 3] + for i in range(0, len(target_coco_annotation_keypoints), 3) + ] for index, keypoint_key in enumerate(keypoint_keys): keypoint_value = keypoint_values[index] if keypoint_value[2] == 0: continue - if not keypoint_value[2] in [1, 2]: - raise FastLabelInvalidException(f"Visibility flag must be 0 or 1, 2 . annotation_id: {target_coco_annotation['id']}", 422) - # fastlabel occulusion is 0 or 1 . coco occulusion is 1 or 2. + if not keypoint_value[2] in [1, 2]: + raise FastLabelInvalidException( + f"Visibility flag must be 0 or 1, 2 . annotation_id: {target_coco_annotation['id']}", + 422, + ) + # fastlabel occulusion is 0 or 1 . coco occulusion is 1 or 2. keypoint_value[2] = keypoint_value[2] - 1 - keypoints.append({ - "key": keypoint_key, - "value": keypoint_value - }) + keypoints.append({"key": keypoint_key, "value": keypoint_value}) annotations.append( { "value": category_name, "type": annotation_type, - "keypoints": keypoints + "keypoints": keypoints, } ) else: raise FastLabelInvalidException( - "Annotation type must be bbox or polygon ,pose_estimation.", 422 + "Annotation type must be bbox or polygon ,pose_estimation.", 422 ) results[coco_images[coco_image_key]] = annotations @@ -817,24 +871,16 @@ def execute_yolo_to_fastlabel( classs_name = classes[str(yolo_class_id)] - yolo_center_x_point = float( - image_width) * float(yolo_center_x_ratio) - yolo_center_y_point = float( - image_height) * float(yolo_center_y_ratio) - yolo_anno_width_size = float( - image_width) * float(yolo_anno_width_ratio) - yolo_anno_height_size = float( - image_height) * float(yolo_anno_height_ratio) + yolo_center_x_point = float(image_width) * float(yolo_center_x_ratio) + yolo_center_y_point = float(image_height) * float(yolo_center_y_ratio) + yolo_anno_width_size = float(image_width) * float(yolo_anno_width_ratio) + yolo_anno_height_size = float(image_height) * float(yolo_anno_height_ratio) points = [] - points.append(yolo_center_x_point - - (yolo_anno_width_size / 2)) # x1 - points.append(yolo_center_y_point - - (yolo_anno_height_size / 2)) # y1 - points.append(yolo_center_x_point + - (yolo_anno_width_size / 2)) # x2 - points.append(yolo_center_y_point + - (yolo_anno_height_size / 2)) # y2 + points.append(yolo_center_x_point - (yolo_anno_width_size / 2)) # x1 + points.append(yolo_center_y_point - (yolo_anno_height_size / 2)) # y1 + points.append(yolo_center_x_point + (yolo_anno_width_size / 2)) # x2 + points.append(yolo_center_y_point + (yolo_anno_height_size / 2)) # y2 annotations.append( { "value": classs_name, diff --git a/setup.py b/setup.py index 2677dd3..c2ba4b5 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,5 @@ python_requires=">=3.7", include_package_data=True, use_scm_version=True, - setup_requires=[ - "setuptools_scm" - ], + setup_requires=["setuptools_scm"], )