From 60bcb9aef823b1dd1b77845699bd3a1de631a76a Mon Sep 17 00:00:00 2001 From: daichi Date: Mon, 4 Oct 2021 16:59:36 +0900 Subject: [PATCH 1/9] convert to fastlabel json logic --- fastlabel/__init__.py | 102 +++++++++++++++++++++++++++++++++++ fastlabel/api.py | 3 +- fastlabel/converters.py | 116 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 1 deletion(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 3d2a8d3..51e99e0 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -13,6 +13,9 @@ from .api import Api from fastlabel import converters, utils, const from fastlabel.const import AnnotationType +import pprint +import re + logger = getLogger(__name__) @@ -636,6 +639,105 @@ def delete_task(self, task_id: str) -> None: endpoint = "tasks/" + task_id self.api.delete_request(endpoint) + def convert_coco_to_fastlabel(self, file_path: str) -> dict: + with open(file_path, 'r') as f: + file = f.read() + return converters.execute_coco_to_fastlabel(eval(file)) + + def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: + results = {} + for file_path in glob.iglob(os.path.join(folder_path, "**/**.json"), recursive=True): + with open(file_path, 'r') as f: + c = converters.execute_labelme_to_fastlabel(json.load(f), file_path.replace(os.path.join(*[folder_path, ""]), "")) + results[c[0]] = c[1] + return results + + def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: + results = {} + for file_path in glob.iglob(os.path.join(folder_path, "**/**.xml"), recursive=True): + with open(file_path, 'r') as f: + file = f.read() + c = converters.execute_pascalvoc_to_fastlabel(xmltodict.parse(file), file_path.replace(os.path.join(*[folder_path, ""]), "")) + results[c[0]] = c[1] + return results + + def convert_yolo_to_fastlabel(self, classes_file_path: str, dataset_folder_path: str) -> dict: + classes = self._get_yolo_format_classes(classes_file_path) + image_sizes = self._get_yolo_image_sizes(dataset_folder_path) + yolo_annotations = self._get_yolo_format_annotations(dataset_folder_path) + + return converters.execute_yolo_to_fastlabel(classes, image_sizes, yolo_annotations, os.path.join(*[dataset_folder_path, ""])) + + def _get_yolo_format_classes(self, classes_file_path: str) -> dict: + """ + return data format + { + id: classs_name + ... + } + """ + classes = {} + with open(classes_file_path, 'r') as f: + lines = f.readlines() + line_index = 0 + for line in lines: + classes[str(line_index)] = line.strip() + line_index += 1 + return classes + + def _get_yolo_image_sizes(self, dataset_folder_path: str) -> dict: + """ + return data format + { + image_file_path_without_ext: { + "image_file_path": image file full path + "size": [whdth, height] + ... + } + """ + image_types = ["jpg", "jpeg", "png"] + image_paths = [p for p in glob.glob(os.path.join(dataset_folder_path, "**/*"), recursive=True) if re.search('/*\.({})'.format("|".join(image_types)), str(p))] + image_sizes = {} + for image_path in image_paths: + image = Image.open(image_path) + width, height = image.size + image_sizes[image_path.replace(os.path.splitext(image_path)[1], "")] = {"image_file_path":image_path, "size": [width, height]} + + return image_sizes + + def _get_yolo_format_annotations(self, dataset_folder_path: str) -> dict: + """ + return data format + { + annotaion_file_path_without_ext: + [ + yolo_class_id, + yolo_center_x_ratio, + yolo_center_y_ratio, + yolo_anno_width_ratio, + yolo_anno_height_ratio + ], + ... + } + """ + yolo_annotations = {} + annotaion_file_paths = [p for p in glob.glob(os.path.join(dataset_folder_path, "**/*.txt"), recursive=True) if re.search(('/*\.txt'), str(p))] + for annotaion_file_path in annotaion_file_paths: + with open(annotaion_file_path, 'r') as f: + anno_lines = f.readlines() + annotaion_key = annotaion_file_path.replace(".txt", "") + yolo_annotations[annotaion_key] = [] + for anno_line in anno_lines: + yolo_annotations[annotaion_key].append(anno_line.strip().split(' ')) + return yolo_annotations + + def get_image_path(self, image_folder_path) -> dict: + image_types = ["jpg", "jpeg", "png"] + image_paths = [p for p in glob.glob(os.path.join(image_folder_path, "**/*"), recursive=True) if re.search('/*\.({})'.format("|".join(image_types)), str(p))] + results = {image_path.replace(os.path.join(*[image_folder_path, ""]), ""): image_path for image_path in image_paths} + + return results + # Task Convert def export_coco(self, tasks: list, output_dir: str = os.path.join("output", "coco")) -> None: diff --git a/fastlabel/api.py b/fastlabel/api.py index fafca89..a4d1f3e 100644 --- a/fastlabel/api.py +++ b/fastlabel/api.py @@ -3,7 +3,8 @@ from .exceptions import FastLabelException, FastLabelInvalidException -FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" +# FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" +FASTLABEL_ENDPOINT = "http://localhost:4000/v1/" class Api: diff --git a/fastlabel/converters.py b/fastlabel/converters.py index 47b9409..a1b6c25 100644 --- a/fastlabel/converters.py +++ b/fastlabel/converters.py @@ -6,6 +6,8 @@ import numpy as np import math from fastlabel.const import AnnotationType +import pprint +import os # COCO @@ -519,3 +521,117 @@ def __get_pixel_coordinates(points: List[int or float]) -> List[int]: new_points.append(int(prev_x + int(xdiff / mindiff * (i + 1)))) new_points.append(int(prev_y + int(ydiff / mindiff * (i + 1)))) return new_points + + +def execute_coco_to_fastlabel(coco: dict) -> dict: + coco_images = {} + for c in coco["images"]: + coco_images[c["id"]] = c["file_name"] + + coco_categories = {} + for c in coco["categories"]: + coco_categories[c["id"]] = c["name"] + + coco_annotations = coco["annotations"] + + results = {} + for coco_image_key in coco_images: + target_coco_annotations = filter(lambda annotation: annotation["image_id"] == coco_image_key, coco_annotations) + if not target_coco_annotations: + return + + annotations = [] + for target_coco_annotation in target_coco_annotations: + category_name = coco_categories[target_coco_annotation["category_id"]] + if not category_name: + return + + segmentation = target_coco_annotation["segmentation"][0] + annotation_type = "" + if len(segmentation) == 4: + annotation_type = AnnotationType.bbox.value + if len(segmentation) > 4: + annotation_type = AnnotationType.polygon.value + annotations.append({"value": category_name, "points": segmentation, "type": annotation_type}) + results[coco_images[coco_image_key]] = annotations + return results + +def execute_labelme_to_fastlabel(labelme: dict, file_path: str = None) -> tuple: + file_name = "" + if file_path: + file_name = file_path.replace(".json", os.path.splitext(labelme["imagePath"])[1]) + else: + file_name = labelme["imagePath"] + labelme_annotations = labelme["shapes"] + + annotations = [] + for labelme_annotation in labelme_annotations: + label = labelme_annotation["label"] + if not label: + return + + points = np.ravel(labelme_annotation["points"]) + annotation_type = __get_annotation_type_by_labelme(labelme_annotation["shape_type"]) + annotations.append({"value": label, "points": points.tolist(), "type": annotation_type}) + + return (file_name, annotations) + +def execute_pascalvoc_to_fastlabel(pascalvoc: dict, file_path: str = None) -> tuple: + target_pascalvoc = pascalvoc["annotation"] + file_name = ""#file_path if file_path else target_pascalvoc["filename"] + if file_path: + file_name = file_path.replace(".xml", os.path.splitext(target_pascalvoc["filename"])[1]) + else: + file_name = target_pascalvoc["filename"] + pascalvoc_annotations = target_pascalvoc["object"] + if not isinstance(pascalvoc_annotations, list): + pascalvoc_annotations = [pascalvoc_annotations] + + annotations = [] + for pascalvoc_annotation in pascalvoc_annotations: + category_name = pascalvoc_annotation["name"] + if not category_name: + return + + points = [int(pascalvoc_annotation["bndbox"][item]) for item in pascalvoc_annotation["bndbox"]] + annotations.append({"value": category_name, "points": points, "type": AnnotationType.bbox.value}) + + return (file_name, annotations) + +def execute_yolo_to_fastlabel(classes: dict, image_sizes: dict, yolo_annotations: dict, dataset_folder_path: str = None) -> dict: + results = {} + for yolo_anno_key in yolo_annotations: + annotations = [] + for each_image_annotation in yolo_annotations[yolo_anno_key]: + yolo_class_id, yolo_center_x_ratio, yolo_center_y_ratio, yolo_anno_width_ratio, yolo_anno_height_ratio = each_image_annotation + image_width, image_height = image_sizes[yolo_anno_key]["size"] + + 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) + + 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 + annotations.append({"value": classs_name, "points": points, "type": AnnotationType.bbox.value}) + + file_path = image_sizes[yolo_anno_key]["image_file_path"].replace(os.path.join(*[dataset_folder_path, ""]), "") if dataset_folder_path else image_sizes[yolo_anno_key]["image_file_path"] + results[file_path] = annotations + + return results + +def __get_annotation_type_by_labelme(shape_type: str) -> str: + if shape_type == "rectangle": + return "bbox" + if shape_type == "polygon": + return "polygon" + if shape_type == "point": + return "keypoint" + if shape_type == "line": + return "line" + return None From 73b1001bf0be8d116b912019333a7ba92bc0c815 Mon Sep 17 00:00:00 2001 From: daichi Date: Tue, 5 Oct 2021 21:19:46 +0900 Subject: [PATCH 2/9] fix to code style shaping and Added usage to README --- README.md | 138 ++++++++++++++++++++++++++++++++++++++++ fastlabel/__init__.py | 105 +++++++++++++++++++----------- fastlabel/converters.py | 101 +++++++++++++++++++++-------- 3 files changed, 282 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 3ce13ff..3d998ea 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ _If you are using FastLabel prototype, please install version 0.2.2._ - [Pascal VOC](#pascal-voc) - [labelme](#labelme) - [Segmentation](#segmentation) +- [Converter (to FastLabel format)](#Converter-to-FastLabel-format) + ## Installation @@ -940,6 +942,142 @@ tasks = client.get_image_tasks(project="YOUR_PROJECT_SLUG") client.export_semantic_segmentation(tasks) ``` + +## Converter to FastLabel format + +#### Response +Example of a converted annotations +```python +{ + 'sample.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ], + 'sample2.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ] +} +``` + +### COCO + +Supported bbox or polygon annotation type. + +Convert annotation file of [COCO format](https://cocodataset.org/#format-data) as a Fastlabel format and create task. + +file_path: COCO annotation json file path + +```python +annotations_map = client.convert_coco_to_fastlabel(file_path="./sample.json") +task_id = client.create_image_task( + project="YOUR_PROJECT_SLUG", + name="sample.jpg", + file_path="./sample.jpg", + annotations=annotations_map["sample.jpg"] +) +``` + +Example of converting annotations to create multiple tasks. +```python +annotations_map = client.convert_yolo_to_fastlabel(file_path="./sample.json") +image_paths = client.get_image_path(image_folder_path="./dataset/") +for image_path in image_paths: + time.sleep(1) + + project = "YOUR_PROJECT_SLUG" + name = image_path + file_path = image_paths.get(image_path) + annotations = annotations_map.get(image_path) if annotations_map.get(image_path) is not None else [] + task_id = client.create_image_task( + project=project, + name=name, + file_path=file_path, + annotations=annotations + ) +``` + +### YOLO + +Supported bbox annotation type. + +Convert annotation file of YOLO format as a Fastlabel format and create task. + +classes_file_path: YOLO classes text file path +dataset_folder_path: Folder path containing YOLO Images and annotation + +```python +annotations_map = client.convert_yolo_to_fastlabel( + classes_file_path="./classese.txt", + dataset_folder_path="./dataset/" +) +task_id = client.create_image_task( + project="YOUR_PROJECT_SLUG", + name="sample.jpg", + file_path="./sample.jpg", + annotations=annotations_map["sample.jpg"] +) +``` + +### Pascal VOC + +Supported bbox annotation type. + +Convert annotation file of Pascal VOC format as a Fastlabel format and create task. + +folder_path: Folder path including pascal VOC format annotation files + +```python +annotations_map = client.convert_pascalvoc_to_fastlabel(folder_path="./dataset/") +task_id = client.create_image_task( + project="YOUR_PROJECT_SLUG", + name="sample.jpg", + file_path="./sample.jpg", + annotations=annotations_map["sample.jpg"] +) +``` + +### labelme + +support the following annotation types. +- bbox +- polygon +- points +- line + +Convert annotation file of labelme format as a Fastlabel format and create task. + +folder_path: Folder path including labelme format annotation files + +```python +annotations_map = client.convert_labelme_to_fastlabel(folder_path="./dataset/") +task_id = client.create_image_task( + project="YOUR_PROJECT_SLUG", + name="sample.jpg", + file_path="./sample.jpg", + annotations=annotations_map["sample.jpg"] +) +``` + + + + > Please check const.COLOR_PALLETE for index colors. ## API Docs diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 51e99e0..7103b37 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -1,20 +1,20 @@ -import os import glob import json -from typing import List +import os +import re from logging import getLogger -from PIL import Image +from typing import List + import cv2 import numpy as np - import xmltodict +from PIL import Image -from .exceptions import FastLabelInvalidException -from .api import Api -from fastlabel import converters, utils, const +from fastlabel import const, converters, utils from fastlabel.const import AnnotationType -import pprint -import re + +from .api import Api +from .exceptions import FastLabelInvalidException logger = getLogger(__name__) @@ -639,34 +639,53 @@ def delete_task(self, task_id: str) -> None: endpoint = "tasks/" + task_id self.api.delete_request(endpoint) + # Convert to Fastlabel + def convert_coco_to_fastlabel(self, file_path: str) -> dict: - with open(file_path, 'r') as f: + with open(file_path, "r") as f: file = f.read() return converters.execute_coco_to_fastlabel(eval(file)) def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: results = {} - for file_path in glob.iglob(os.path.join(folder_path, "**/**.json"), recursive=True): - with open(file_path, 'r') as f: - c = converters.execute_labelme_to_fastlabel(json.load(f), file_path.replace(os.path.join(*[folder_path, ""]), "")) + for file_path in glob.iglob( + os.path.join(folder_path, "**/**.json"), recursive=True + ): + with open(file_path, "r") as f: + c = converters.execute_labelme_to_fastlabel( + json.load(f), + file_path.replace(os.path.join(*[folder_path, ""]), ""), + ) results[c[0]] = c[1] return results - + def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: results = {} - for file_path in glob.iglob(os.path.join(folder_path, "**/**.xml"), recursive=True): - with open(file_path, 'r') as f: + for file_path in glob.iglob( + os.path.join(folder_path, "**/**.xml"), recursive=True + ): + with open(file_path, "r") as f: file = f.read() - c = converters.execute_pascalvoc_to_fastlabel(xmltodict.parse(file), file_path.replace(os.path.join(*[folder_path, ""]), "")) + c = converters.execute_pascalvoc_to_fastlabel( + xmltodict.parse(file), + file_path.replace(os.path.join(*[folder_path, ""]), ""), + ) results[c[0]] = c[1] return results - - def convert_yolo_to_fastlabel(self, classes_file_path: str, dataset_folder_path: str) -> dict: + + def convert_yolo_to_fastlabel( + self, classes_file_path: str, dataset_folder_path: str + ) -> dict: classes = self._get_yolo_format_classes(classes_file_path) image_sizes = self._get_yolo_image_sizes(dataset_folder_path) yolo_annotations = self._get_yolo_format_annotations(dataset_folder_path) - - return converters.execute_yolo_to_fastlabel(classes, image_sizes, yolo_annotations, os.path.join(*[dataset_folder_path, ""])) + + return converters.execute_yolo_to_fastlabel( + classes, + image_sizes, + yolo_annotations, + os.path.join(*[dataset_folder_path, ""]), + ) def _get_yolo_format_classes(self, classes_file_path: str) -> dict: """ @@ -677,7 +696,7 @@ def _get_yolo_format_classes(self, classes_file_path: str) -> dict: } """ classes = {} - with open(classes_file_path, 'r') as f: + with open(classes_file_path, "r") as f: lines = f.readlines() line_index = 0 for line in lines: @@ -696,45 +715,59 @@ def _get_yolo_image_sizes(self, dataset_folder_path: str) -> dict: } """ image_types = ["jpg", "jpeg", "png"] - image_paths = [p for p in glob.glob(os.path.join(dataset_folder_path, "**/*"), recursive=True) if re.search('/*\.({})'.format("|".join(image_types)), str(p))] + image_paths = [ + p for p in glob.glob(os.path.join(dataset_folder_path, "**/*"), recursive=True) + if re.search("/*\.({})".format("|".join(image_types)), str(p)) + ] image_sizes = {} for image_path in image_paths: image = Image.open(image_path) width, height = image.size - image_sizes[image_path.replace(os.path.splitext(image_path)[1], "")] = {"image_file_path":image_path, "size": [width, height]} - + image_sizes[image_path.replace(os.path.splitext(image_path)[1], "")] = { + "image_file_path": image_path, + "size": [width, height], + } + return image_sizes def _get_yolo_format_annotations(self, dataset_folder_path: str) -> dict: """ return data format { - annotaion_file_path_without_ext: + annotaion_file_path_without_ext: [ - yolo_class_id, - yolo_center_x_ratio, - yolo_center_y_ratio, - yolo_anno_width_ratio, - yolo_anno_height_ratio + yolo_class_id, + yolo_center_x_ratio, + yolo_center_y_ratio, + yolo_anno_width_ratio, + yolo_anno_height_ratio ], ... } """ yolo_annotations = {} - annotaion_file_paths = [p for p in glob.glob(os.path.join(dataset_folder_path, "**/*.txt"), recursive=True) if re.search(('/*\.txt'), str(p))] + annotaion_file_paths = [ + p for p in glob.glob(os.path.join(dataset_folder_path, "**/*.txt"), recursive=True) + if re.search(("/*\.txt"), str(p)) + ] for annotaion_file_path in annotaion_file_paths: - with open(annotaion_file_path, 'r') as f: + with open(annotaion_file_path, "r") as f: anno_lines = f.readlines() annotaion_key = annotaion_file_path.replace(".txt", "") yolo_annotations[annotaion_key] = [] for anno_line in anno_lines: - yolo_annotations[annotaion_key].append(anno_line.strip().split(' ')) + yolo_annotations[annotaion_key].append(anno_line.strip().split(" ")) return yolo_annotations def get_image_path(self, image_folder_path) -> dict: image_types = ["jpg", "jpeg", "png"] - image_paths = [p for p in glob.glob(os.path.join(image_folder_path, "**/*"), recursive=True) if re.search('/*\.({})'.format("|".join(image_types)), str(p))] - results = {image_path.replace(os.path.join(*[image_folder_path, ""]), ""): image_path for image_path in image_paths} + image_paths = [ + p for p in glob.glob(os.path.join(image_folder_path, "**/*"), recursive=True) + if re.search("/*\.({})".format("|".join(image_types)), str(p)) + ] + results = { + image_path.replace(os.path.join(*[image_folder_path, ""]), ""): image_path for image_path in image_paths + } return results diff --git a/fastlabel/converters.py b/fastlabel/converters.py index a1b6c25..7ee887a 100644 --- a/fastlabel/converters.py +++ b/fastlabel/converters.py @@ -6,7 +6,6 @@ import numpy as np import math from fastlabel.const import AnnotationType -import pprint import os # COCO @@ -527,7 +526,7 @@ def execute_coco_to_fastlabel(coco: dict) -> dict: coco_images = {} for c in coco["images"]: coco_images[c["id"]] = c["file_name"] - + coco_categories = {} for c in coco["categories"]: coco_categories[c["id"]] = c["name"] @@ -536,30 +535,41 @@ def execute_coco_to_fastlabel(coco: dict) -> dict: results = {} for coco_image_key in coco_images: - target_coco_annotations = filter(lambda annotation: annotation["image_id"] == coco_image_key, coco_annotations) + target_coco_annotations = filter( + lambda annotation: annotation["image_id"] == coco_image_key, + coco_annotations, + ) if not target_coco_annotations: return - + annotations = [] for target_coco_annotation in target_coco_annotations: category_name = coco_categories[target_coco_annotation["category_id"]] if not category_name: return - + segmentation = target_coco_annotation["segmentation"][0] annotation_type = "" if len(segmentation) == 4: annotation_type = AnnotationType.bbox.value if len(segmentation) > 4: - annotation_type = AnnotationType.polygon.value - annotations.append({"value": category_name, "points": segmentation, "type": annotation_type}) + annotation_type = AnnotationType.polygon.value + annotations.append( + { + "value": category_name, + "points": segmentation, + "type": annotation_type, + } + ) results[coco_images[coco_image_key]] = annotations return results def execute_labelme_to_fastlabel(labelme: dict, file_path: str = None) -> tuple: file_name = "" if file_path: - file_name = file_path.replace(".json", os.path.splitext(labelme["imagePath"])[1]) + file_name = file_path.replace( + ".json", os.path.splitext(labelme["imagePath"])[1] + ) else: file_name = labelme["imagePath"] labelme_annotations = labelme["shapes"] @@ -569,45 +579,72 @@ def execute_labelme_to_fastlabel(labelme: dict, file_path: str = None) -> tuple: label = labelme_annotation["label"] if not label: return - + points = np.ravel(labelme_annotation["points"]) - annotation_type = __get_annotation_type_by_labelme(labelme_annotation["shape_type"]) - annotations.append({"value": label, "points": points.tolist(), "type": annotation_type}) + annotation_type = __get_annotation_type_by_labelme( + labelme_annotation["shape_type"] + ) + annotations.append( + {"value": label, "points": points.tolist(), "type": annotation_type} + ) return (file_name, annotations) def execute_pascalvoc_to_fastlabel(pascalvoc: dict, file_path: str = None) -> tuple: target_pascalvoc = pascalvoc["annotation"] - file_name = ""#file_path if file_path else target_pascalvoc["filename"] + file_name = "" # file_path if file_path else target_pascalvoc["filename"] if file_path: - file_name = file_path.replace(".xml", os.path.splitext(target_pascalvoc["filename"])[1]) + file_name = file_path.replace( + ".xml", os.path.splitext(target_pascalvoc["filename"])[1] + ) else: file_name = target_pascalvoc["filename"] pascalvoc_annotations = target_pascalvoc["object"] if not isinstance(pascalvoc_annotations, list): pascalvoc_annotations = [pascalvoc_annotations] - + annotations = [] for pascalvoc_annotation in pascalvoc_annotations: category_name = pascalvoc_annotation["name"] if not category_name: return - - points = [int(pascalvoc_annotation["bndbox"][item]) for item in pascalvoc_annotation["bndbox"]] - annotations.append({"value": category_name, "points": points, "type": AnnotationType.bbox.value}) + + points = [ + int(pascalvoc_annotation["bndbox"][item]) + for item in pascalvoc_annotation["bndbox"] + ] + annotations.append( + { + "value": category_name, + "points": points, + "type": AnnotationType.bbox.value, + } + ) return (file_name, annotations) -def execute_yolo_to_fastlabel(classes: dict, image_sizes: dict, yolo_annotations: dict, dataset_folder_path: str = None) -> dict: + +def execute_yolo_to_fastlabel( + classes: dict, + image_sizes: dict, + yolo_annotations: dict, + dataset_folder_path: str = None, +) -> dict: results = {} for yolo_anno_key in yolo_annotations: annotations = [] for each_image_annotation in yolo_annotations[yolo_anno_key]: - yolo_class_id, yolo_center_x_ratio, yolo_center_y_ratio, yolo_anno_width_ratio, yolo_anno_height_ratio = each_image_annotation + ( + yolo_class_id, + yolo_center_x_ratio, + yolo_center_y_ratio, + yolo_anno_width_ratio, + yolo_anno_height_ratio, + ) = each_image_annotation image_width, image_height = image_sizes[yolo_anno_key]["size"] - + 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) @@ -615,12 +652,24 @@ def execute_yolo_to_fastlabel(classes: dict, image_sizes: dict, yolo_annotations 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_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, "points": points, "type": AnnotationType.bbox.value}) - - file_path = image_sizes[yolo_anno_key]["image_file_path"].replace(os.path.join(*[dataset_folder_path, ""]), "") if dataset_folder_path else image_sizes[yolo_anno_key]["image_file_path"] + points.append(yolo_center_y_point + (yolo_anno_height_size / 2)) # y2 + annotations.append( + { + "value": classs_name, + "points": points, + "type": AnnotationType.bbox.value, + } + ) + + file_path = ( + image_sizes[yolo_anno_key]["image_file_path"].replace( + os.path.join(*[dataset_folder_path, ""]), "" + ) + if dataset_folder_path + else image_sizes[yolo_anno_key]["image_file_path"] + ) results[file_path] = annotations return results From dd091fb48777eabdcf38a7f200abd2424aa21f95 Mon Sep 17 00:00:00 2001 From: daichi Date: Tue, 5 Oct 2021 21:22:01 +0900 Subject: [PATCH 3/9] Remove unnecessary changes --- fastlabel/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fastlabel/api.py b/fastlabel/api.py index a4d1f3e..fafca89 100644 --- a/fastlabel/api.py +++ b/fastlabel/api.py @@ -3,8 +3,7 @@ from .exceptions import FastLabelException, FastLabelInvalidException -# FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" -FASTLABEL_ENDPOINT = "http://localhost:4000/v1/" +FASTLABEL_ENDPOINT = "https://api.fastlabel.ai/v1/" class Api: From 3364954c229616b87a270967f7baa9c006594715 Mon Sep 17 00:00:00 2001 From: daichi Date: Sat, 16 Oct 2021 16:06:34 +0900 Subject: [PATCH 4/9] fix to README and minor correction --- README.md | 200 ++++++++++++++++++++++++++++++++++++++---- fastlabel/__init__.py | 46 ++++++---- fastlabel/utils.py | 3 + 3 files changed, 213 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 3d998ea..0474870 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ _If you are using FastLabel prototype, please install version 0.2.2._ - [Pascal VOC](#pascal-voc) - [labelme](#labelme) - [Segmentation](#segmentation) -- [Converter (to FastLabel format)](#Converter-to-FastLabel-format) +- [Converter (to FastLabel format)](#converter-to-fastLabel-format) ## Installation @@ -945,11 +945,11 @@ client.export_semantic_segmentation(tasks) ## Converter to FastLabel format -#### Response +### Response Example of a converted annotations ```python { - 'sample.jpg': [ + 'sample1.jpg': [ { 'points': [ 100, @@ -976,6 +976,46 @@ Example of a converted annotations } ``` +In the case of YOLO, Pascal VOC, and labelme, the key is the tree structure if the tree structure is multi-level. + +``` +dataset +├── sample1.jpg +├── sample1.txt +└── sample_dir + ├── sample2.jpg + └── sample2.txt +``` + +```python +{ + 'sample1.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ], + 'sample_dir/sample2.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ] +} +``` + ### COCO Supported bbox or polygon annotation type. @@ -990,21 +1030,36 @@ task_id = client.create_image_task( project="YOUR_PROJECT_SLUG", name="sample.jpg", file_path="./sample.jpg", - annotations=annotations_map["sample.jpg"] + annotations=annotations_map.get("sample.jpg") ) ``` Example of converting annotations to create multiple tasks. + +In the case of the following tree structure. + +``` +dataset +├── annotation.json +├── sample1.jpg +└── sample2.jpg +``` + +Example source code. + ```python -annotations_map = client.convert_yolo_to_fastlabel(file_path="./sample.json") -image_paths = client.get_image_path(image_folder_path="./dataset/") -for image_path in image_paths: - time.sleep(1) +import fastlabel - project = "YOUR_PROJECT_SLUG" - name = image_path - file_path = image_paths.get(image_path) - annotations = annotations_map.get(image_path) if annotations_map.get(image_path) is not None else [] +project = "YOUR_PROJECT_SLUG" +input_file_path = "./dataset/annotation.json" +input_dataset_path = "./dataset/" + +annotations_map = client.convert_coco_to_fastlabel(file_path=input_file_path) +for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), recursive=True): + time.sleep(1) + name = image_file_path.replace(os.path.join(*[input_dataset_path, ""]), "") + file_path = image_file_path + annotations = annotations_map.get(name) if annotations_map.get(name) is not None else [] task_id = client.create_image_task( project=project, name=name, @@ -1024,17 +1079,57 @@ dataset_folder_path: Folder path containing YOLO Images and annotation ```python annotations_map = client.convert_yolo_to_fastlabel( - classes_file_path="./classese.txt", + classes_file_path="./classes.txt", dataset_folder_path="./dataset/" ) task_id = client.create_image_task( project="YOUR_PROJECT_SLUG", name="sample.jpg", - file_path="./sample.jpg", - annotations=annotations_map["sample.jpg"] + file_path="./dataset/sample.jpg", + annotations=annotations_map.get("sample.jpg") +) +``` + +Example of converting annotations to create multiple tasks. + +In the case of the following tree structure. + +``` +yolo +├── classes.txt +└── dataset + ├── sample1.jpg + ├── sample1.txt + ├── sample2.jpg + └── sample2.txt +``` + +Example source code. + +```python +import fastlabel + +project = "YOUR_PROJECT_SLUG" +input_file_path = "./classes.txt" +input_dataset_path = "./dataset/" +annotations_map = client.convert_yolo_to_fastlabel( + classes_file_path=input_file_path, + dataset_folder_path=input_dataset_path ) +for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), recursive=True): + time.sleep(1) + name = image_file_path.replace(os.path.join(*[input_dataset_path, ""]), "") + file_path = image_file_path + annotations = annotations_map.get(name) if annotations_map.get(name) is not None else [] + task_id = client.create_image_task( + project=project, + name=name, + file_path=file_path, + annotations=annotations + ) ``` + ### Pascal VOC Supported bbox annotation type. @@ -1048,11 +1143,46 @@ annotations_map = client.convert_pascalvoc_to_fastlabel(folder_path="./dataset/" task_id = client.create_image_task( project="YOUR_PROJECT_SLUG", name="sample.jpg", - file_path="./sample.jpg", - annotations=annotations_map["sample.jpg"] + file_path="./dataset/sample.jpg", + annotations=annotations_map.get("sample.jpg") ) ``` +Example of converting annotations to create multiple tasks. + +In the case of the following tree structure. + +``` +dataset +├── sample1.jpg +├── sample1.xml +├── sample2.jpg +└── sample2.xml +``` + +Example source code. + +```python +import fastlabel + +project = "YOUR_PROJECT_SLUG" +input_dataset_path = "./dataset/" + +annotations_map = client.convert_pascalvoc_to_fastlabel(folder_path=input_dataset_path) +for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), recursive=True): + time.sleep(1) + name = image_file_path.replace(os.path.join(*[input_dataset_path, ""]), "") + file_path = image_file_path + annotations = annotations_map.get(name) if annotations_map.get(name) is not None else [] + task_id = client.create_image_task( + project=project, + name=name, + file_path=file_path, + annotations=annotations + ) +``` + + ### labelme support the following annotation types. @@ -1071,10 +1201,44 @@ task_id = client.create_image_task( project="YOUR_PROJECT_SLUG", name="sample.jpg", file_path="./sample.jpg", - annotations=annotations_map["sample.jpg"] + annotations=annotations_map.get("sample.jpg") ) ``` +Example of converting annotations to create multiple tasks. + +In the case of the following tree structure. + +``` +dataset +├── sample1.jpg +├── sample1.json +├── sample2.jpg +└── sample2.json +``` + +Example source code. + +```python +import fastlabel + +project = "YOUR_PROJECT_SLUG" +input_dataset_path = "./dataset/" + +annotations_map = client.convert_labelme_to_fastlabel(folder_path=input_dataset_path) +for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), recursive=True): + time.sleep(1) + name = image_file_path.replace(os.path.join(*[input_dataset_path, ""]), "") + file_path = image_file_path + annotations = annotations_map.get(name) if annotations_map.get(name) is not None else [] + task_id = client.create_image_task( + project=project, + name=name, + file_path=file_path, + annotations=annotations + ) +``` + diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 7103b37..8f9c7b4 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -642,11 +642,21 @@ def delete_task(self, task_id: str) -> None: # Convert to Fastlabel def convert_coco_to_fastlabel(self, file_path: str) -> dict: + """ + Convert COCO format to FastLabel format as annotation file. + + file_path is a COCO format annotation file. (Required) + """ with open(file_path, "r") as f: file = f.read() return converters.execute_coco_to_fastlabel(eval(file)) def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: + """ + Convert labelme format to FastLabel format as annotation files. + + folder_path is the folder that contains the labelme format files with the json extension. (Required) + """ results = {} for file_path in glob.iglob( os.path.join(folder_path, "**/**.json"), recursive=True @@ -660,6 +670,11 @@ def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: return results def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: + """ + Convert PascalVOC format to FastLabel format as annotation files. + + folder_path is the folder that contains the PascalVOC format files with the xml extension. (Required) + """ results = {} for file_path in glob.iglob( os.path.join(folder_path, "**/**.xml"), recursive=True @@ -676,9 +691,15 @@ def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: def convert_yolo_to_fastlabel( self, classes_file_path: str, dataset_folder_path: str ) -> dict: - classes = self._get_yolo_format_classes(classes_file_path) - image_sizes = self._get_yolo_image_sizes(dataset_folder_path) - yolo_annotations = self._get_yolo_format_annotations(dataset_folder_path) + """ + Convert YOLO format to FastLabel format as annotation files. + + classes_file_path is YOLO format class file. (Required) + dataset_folder_path is the folder that contains the image file and YOLO format files with the txt extension. (Required) + """ + classes = self.__get_yolo_format_classes(classes_file_path) + image_sizes = self.__get_yolo_image_sizes(dataset_folder_path) + yolo_annotations = self.__get_yolo_format_annotations(dataset_folder_path) return converters.execute_yolo_to_fastlabel( classes, @@ -687,7 +708,7 @@ def convert_yolo_to_fastlabel( os.path.join(*[dataset_folder_path, ""]), ) - def _get_yolo_format_classes(self, classes_file_path: str) -> dict: + def __get_yolo_format_classes(self, classes_file_path: str) -> dict: """ return data format { @@ -704,7 +725,7 @@ def _get_yolo_format_classes(self, classes_file_path: str) -> dict: line_index += 1 return classes - def _get_yolo_image_sizes(self, dataset_folder_path: str) -> dict: + def __get_yolo_image_sizes(self, dataset_folder_path: str) -> dict: """ return data format { @@ -714,7 +735,7 @@ def _get_yolo_image_sizes(self, dataset_folder_path: str) -> dict: ... } """ - image_types = ["jpg", "jpeg", "png"] + image_types = utils.get_supported_image_ext() image_paths = [ p for p in glob.glob(os.path.join(dataset_folder_path, "**/*"), recursive=True) if re.search("/*\.({})".format("|".join(image_types)), str(p)) @@ -730,7 +751,7 @@ def _get_yolo_image_sizes(self, dataset_folder_path: str) -> dict: return image_sizes - def _get_yolo_format_annotations(self, dataset_folder_path: str) -> dict: + def __get_yolo_format_annotations(self, dataset_folder_path: str) -> dict: """ return data format { @@ -759,17 +780,6 @@ def _get_yolo_format_annotations(self, dataset_folder_path: str) -> dict: yolo_annotations[annotaion_key].append(anno_line.strip().split(" ")) return yolo_annotations - def get_image_path(self, image_folder_path) -> dict: - image_types = ["jpg", "jpeg", "png"] - image_paths = [ - p for p in glob.glob(os.path.join(image_folder_path, "**/*"), recursive=True) - if re.search("/*\.({})".format("|".join(image_types)), str(p)) - ] - results = { - image_path.replace(os.path.join(*[image_folder_path, ""]), ""): image_path for image_path in image_paths - } - - return results # Task Convert diff --git a/fastlabel/utils.py b/fastlabel/utils.py index 7984222..c4a643d 100644 --- a/fastlabel/utils.py +++ b/fastlabel/utils.py @@ -21,3 +21,6 @@ def get_basename(file_path: str) -> str: path/to/file.jpg -> path/to/file """ return os.path.splitext(file_path)[0] + +def get_supported_image_ext() -> list: + return ["jpg", "jpeg", "png"] \ No newline at end of file From 99ce5b79d7bc87824ac984f553c962357696da96 Mon Sep 17 00:00:00 2001 From: daichi Date: Sat, 16 Oct 2021 16:27:47 +0900 Subject: [PATCH 5/9] add output format example --- fastlabel/__init__.py | 145 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 8f9c7b4..8c055ac 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -646,6 +646,34 @@ def convert_coco_to_fastlabel(self, file_path: str) -> dict: Convert COCO format to FastLabel format as annotation file. file_path is a COCO format annotation file. (Required) + + output format example. + { + 'sample1.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ], + 'sample2.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ] + } """ with open(file_path, "r") as f: file = f.read() @@ -656,6 +684,45 @@ def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: Convert labelme format to FastLabel format as annotation files. folder_path is the folder that contains the labelme format files with the json extension. (Required) + + output format example. + In the case of labelme, the key is the tree structure if the tree structure is multi-level. + + [tree structure] + dataset + ├── sample1.jpg + ├── sample1.json + └── sample_dir + ├── sample2.jpg + └── sample2.json + + [output] + { + 'sample1.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ], + 'sample_dir/sample2.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ] + } """ results = {} for file_path in glob.iglob( @@ -674,6 +741,45 @@ def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: Convert PascalVOC format to FastLabel format as annotation files. folder_path is the folder that contains the PascalVOC format files with the xml extension. (Required) + + output format example. + In the case of PascalVOC, the key is the tree structure if the tree structure is multi-level. + + [tree structure] + dataset + ├── sample1.jpg + ├── sample1.xml + └── sample_dir + ├── sample2.jpg + └── sample2.xml + + [output] + { + 'sample1.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ], + 'sample_dir/sample2.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ] + } """ results = {} for file_path in glob.iglob( @@ -696,6 +802,45 @@ def convert_yolo_to_fastlabel( classes_file_path is YOLO format class file. (Required) dataset_folder_path is the folder that contains the image file and YOLO format files with the txt extension. (Required) + + output format example. + In the case of YOLO, the key is the tree structure if the tree structure is multi-level. + + [tree structure] + dataset + ├── sample1.jpg + ├── sample1.txt + └── sample_dir + ├── sample2.jpg + └── sample2.txt + + [output] + { + 'sample1.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ], + 'sample_dir/sample2.jpg': [ + { + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } + ] + } """ classes = self.__get_yolo_format_classes(classes_file_path) image_sizes = self.__get_yolo_image_sizes(dataset_folder_path) From da5f871cccaee333e72bbd675d4abdcd9c82e64a Mon Sep 17 00:00:00 2001 From: daichi Date: Sat, 16 Oct 2021 16:37:15 +0900 Subject: [PATCH 6/9] add explanation of output format --- fastlabel/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 8c055ac..3bff838 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -647,6 +647,8 @@ def convert_coco_to_fastlabel(self, file_path: str) -> dict: file_path is a COCO format annotation file. (Required) + In the output file, the key is the image file name and the value is a list of annotations in FastLabel format, which is returned in dict format. + output format example. { 'sample1.jpg': [ @@ -685,6 +687,9 @@ def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: folder_path is the folder that contains the labelme format files with the json extension. (Required) + In the output file, the key is the image file name and the value is a list of annotations in FastLabel format, which is returned in dict format. + If the tree has multiple hierarchies, the key is the relative path rooted at the specified folder name. + output format example. In the case of labelme, the key is the tree structure if the tree structure is multi-level. @@ -742,6 +747,9 @@ def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: folder_path is the folder that contains the PascalVOC format files with the xml extension. (Required) + In the output file, the key is the image file name and the value is a list of annotations in FastLabel format, which is returned in dict format. + If the tree has multiple hierarchies, the key is the relative path rooted at the specified folder name. + output format example. In the case of PascalVOC, the key is the tree structure if the tree structure is multi-level. @@ -802,6 +810,9 @@ def convert_yolo_to_fastlabel( classes_file_path is YOLO format class file. (Required) dataset_folder_path is the folder that contains the image file and YOLO format files with the txt extension. (Required) + + In the output file, the key is the image file name and the value is a list of annotations in FastLabel format, which is returned in dict format. + If the tree has multiple hierarchies, the key is the relative path rooted at the specified folder name. output format example. In the case of YOLO, the key is the tree structure if the tree structure is multi-level. From 6c5cd5c5123c6d9958f55a3af515d57b6608932a Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 2 Nov 2021 15:34:27 +0900 Subject: [PATCH 7/9] update readme --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0474870..2c75bbe 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ _If you are using FastLabel prototype, please install version 0.2.2._ - [Pascal VOC](#pascal-voc) - [labelme](#labelme) - [Segmentation](#segmentation) -- [Converter (to FastLabel format)](#converter-to-fastLabel-format) - +- [Converter to FastLabel format](#converter-to-fastlabel-format) ## Installation @@ -942,11 +941,12 @@ tasks = client.get_image_tasks(project="YOUR_PROJECT_SLUG") client.export_semantic_segmentation(tasks) ``` - ## Converter to FastLabel format ### Response + Example of a converted annotations + ```python { 'sample1.jpg': [ @@ -1020,7 +1020,7 @@ dataset Supported bbox or polygon annotation type. -Convert annotation file of [COCO format](https://cocodataset.org/#format-data) as a Fastlabel format and create task. +Convert annotation file of [COCO format](https://cocodataset.org/#format-data) as a Fastlabel format and create task. file_path: COCO annotation json file path @@ -1075,7 +1075,7 @@ Supported bbox annotation type. Convert annotation file of YOLO format as a Fastlabel format and create task. classes_file_path: YOLO classes text file path -dataset_folder_path: Folder path containing YOLO Images and annotation +dataset_folder_path: Folder path containing YOLO Images and annotation ```python annotations_map = client.convert_yolo_to_fastlabel( @@ -1129,7 +1129,6 @@ for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), ) ``` - ### Pascal VOC Supported bbox annotation type. @@ -1182,10 +1181,10 @@ for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), ) ``` - ### labelme support the following annotation types. + - bbox - polygon - points @@ -1239,9 +1238,6 @@ for image_file_path in glob.iglob(os.path.join(input_dataset_path, "**/**.jpg"), ) ``` - - - > Please check const.COLOR_PALLETE for index colors. ## API Docs From 161c2b4214c97153ce63a243d6197f5a9e1dcc7c Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 2 Nov 2021 15:37:26 +0900 Subject: [PATCH 8/9] fix format --- fastlabel/__init__.py | 181 ++++++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 85 deletions(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 3bff838..252793b 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -532,7 +532,8 @@ def create_video_classification_task( """ endpoint = "tasks/video/classification" if not utils.is_video_supported_ext(file_path): - raise FastLabelInvalidException("Supported extensions are mp4.", 422) + raise FastLabelInvalidException( + "Supported extensions are mp4.", 422) file = utils.base64_encode(file_path) payload = {"project": project, "name": name, "file": file} if status: @@ -648,31 +649,31 @@ def convert_coco_to_fastlabel(self, file_path: str) -> dict: file_path is a COCO format annotation file. (Required) In the output file, the key is the image file name and the value is a list of annotations in FastLabel format, which is returned in dict format. - + output format example. { 'sample1.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ], 'sample2.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ] } @@ -692,7 +693,7 @@ def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: output format example. In the case of labelme, the key is the tree structure if the tree structure is multi-level. - + [tree structure] dataset ├── sample1.jpg @@ -705,27 +706,27 @@ def convert_labelme_to_fastlabel(self, folder_path: str) -> dict: { 'sample1.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ], 'sample_dir/sample2.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' - } + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' + } ] } """ @@ -765,26 +766,26 @@ def convert_pascalvoc_to_fastlabel(self, folder_path: str) -> dict: { 'sample1.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ], 'sample_dir/sample2.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ] } @@ -810,7 +811,7 @@ def convert_yolo_to_fastlabel( classes_file_path is YOLO format class file. (Required) dataset_folder_path is the folder that contains the image file and YOLO format files with the txt extension. (Required) - + In the output file, the key is the image file name and the value is a list of annotations in FastLabel format, which is returned in dict format. If the tree has multiple hierarchies, the key is the relative path rooted at the specified folder name. @@ -829,33 +830,34 @@ def convert_yolo_to_fastlabel( { 'sample1.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ], 'sample_dir/sample2.jpg': [ { - 'points': [ - 100, - 100, - 200, - 200 - ], - 'type': 'bbox', - 'value': 'cat' + 'points': [ + 100, + 100, + 200, + 200 + ], + 'type': 'bbox', + 'value': 'cat' } ] } """ classes = self.__get_yolo_format_classes(classes_file_path) image_sizes = self.__get_yolo_image_sizes(dataset_folder_path) - yolo_annotations = self.__get_yolo_format_annotations(dataset_folder_path) + yolo_annotations = self.__get_yolo_format_annotations( + dataset_folder_path) return converters.execute_yolo_to_fastlabel( classes, @@ -933,10 +935,10 @@ def __get_yolo_format_annotations(self, dataset_folder_path: str) -> dict: annotaion_key = annotaion_file_path.replace(".txt", "") yolo_annotations[annotaion_key] = [] for anno_line in anno_lines: - yolo_annotations[annotaion_key].append(anno_line.strip().split(" ")) + yolo_annotations[annotaion_key].append( + anno_line.strip().split(" ")) return yolo_annotations - # Task Convert def export_coco(self, tasks: list, output_dir: str = os.path.join("output", "coco")) -> None: @@ -1013,8 +1015,8 @@ def export_labelme(self, tasks: list, output_dir: str = os.path.join("output", " with open(file_path, 'w') as f: json.dump(labelme, f, indent=4, ensure_ascii=False) - # Instance / Semantic Segmetation + def export_instance_segmentation(self, tasks: list, output_dir: str = os.path.join("output", "instance_segmentation"), pallete: List[int] = const.COLOR_PALETTE) -> None: """ Convert tasks to index color instance segmentation (PNG files). @@ -1027,8 +1029,9 @@ def export_instance_segmentation(self, tasks: list, output_dir: str = os.path.jo """ tasks = converters.to_pixel_coordinates(tasks) for task in tasks: - self.__export_index_color_image(task=task, output_dir=output_dir, pallete=pallete, is_instance_segmentation=True) - + self.__export_index_color_image( + task=task, output_dir=output_dir, pallete=pallete, is_instance_segmentation=True) + def export_semantic_segmentation(self, tasks: list, output_dir: str = os.path.join("output", "semantic_segmentation"), pallete: List[int] = const.COLOR_PALETTE) -> None: """ Convert tasks to index color semantic segmentation (PNG files). @@ -1048,7 +1051,8 @@ def export_semantic_segmentation(self, tasks: list, output_dir: str = os.path.jo tasks = converters.to_pixel_coordinates(tasks) for task in tasks: - self.__export_index_color_image(task=task, output_dir=output_dir, pallete=pallete, is_instance_segmentation=False, classes=classes) + self.__export_index_color_image( + task=task, output_dir=output_dir, pallete=pallete, is_instance_segmentation=False, classes=classes) def __export_index_color_image(self, task: list, output_dir: str, pallete: List[int], is_instance_segmentation: bool = True, classes: list = []) -> None: image = Image.new("RGB", (task["width"], task["height"]), 0) @@ -1057,28 +1061,36 @@ def __export_index_color_image(self, task: list, output_dir: str, pallete: List[ index = 1 for annotation in task["annotations"]: - color = index if is_instance_segmentation else classes.index(annotation["value"]) + 1 + color = index if is_instance_segmentation else classes.index( + annotation["value"]) + 1 if annotation["type"] == AnnotationType.segmentation.value: for region in annotation["points"]: count = 0 for points in region: cv_draw_points = self.__get_cv_draw_points(points) if count == 0: - cv2.fillPoly(image, [cv_draw_points], color, lineType=cv2.LINE_8, shift=0) + cv2.fillPoly( + image, [cv_draw_points], color, lineType=cv2.LINE_8, shift=0) else: - cv2.fillPoly(image, [cv_draw_points], 0, lineType=cv2.LINE_8, shift=0) + cv2.fillPoly( + image, [cv_draw_points], 0, lineType=cv2.LINE_8, shift=0) count += 1 elif annotation["type"] == AnnotationType.polygon.value: - cv_draw_points = self.__get_cv_draw_points(annotation["points"]) - cv2.fillPoly(image, [cv_draw_points], color, lineType=cv2.LINE_8, shift=0) + cv_draw_points = self.__get_cv_draw_points( + annotation["points"]) + cv2.fillPoly(image, [cv_draw_points], color, + lineType=cv2.LINE_8, shift=0) elif annotation["type"] == AnnotationType.bbox.value: - cv_draw_points = self.__get_cv_draw_points(annotation["points"]) - cv2.fillPoly(image, [cv_draw_points], color, lineType=cv2.LINE_8, shift=0) + cv_draw_points = self.__get_cv_draw_points( + annotation["points"]) + cv2.fillPoly(image, [cv_draw_points], color, + lineType=cv2.LINE_8, shift=0) else: continue index += 1 - image_path = os.path.join(output_dir, utils.get_basename(task["name"]) + ".png") + image_path = os.path.join( + output_dir, utils.get_basename(task["name"]) + ".png") os.makedirs(os.path.dirname(image_path), exist_ok=True) image = Image.fromarray(image) image = image.convert('P') @@ -1127,7 +1139,6 @@ def __get_cv_draw_points(self, points: List[int]) -> List[int]: cv_points.append((new_points[i * 2], new_points[i * 2 + 1])) return np.array(cv_points) - # Annotation def find_annotation(self, annotation_id: str) -> dict: From 9d8035f45bc90921e175889745cf44f3af648e8e Mon Sep 17 00:00:00 2001 From: faycute Date: Tue, 2 Nov 2021 15:43:03 +0900 Subject: [PATCH 9/9] upgrade version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f77f019..3bd7ebc 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name="fastlabel", - version="0.11.5", + version="0.11.6", author="eisuke-ueta", author_email="eisuke.ueta@fastlabel.ai", description="The official Python SDK for FastLabel API, the Data Platform for AI",