diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 3d2a8d3..36c6412 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -529,7 +529,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: @@ -712,12 +713,12 @@ 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). - Supports only bbox, polygon and segmentation annotation types. Hollowed points are not supported. + Supports only bbox, polygon and segmentation annotation types. Supports up to 57 instances in default colors palette. Check const.COLOR_PALETTE for more details. tasks is a list of tasks. (Required) @@ -726,12 +727,13 @@ 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). - Supports only bbox, polygon and segmentation annotation types. Hollowed points are not supported. + Supports only bbox, polygon and segmentation annotation types. Check const.COLOR_PALETTE for color pallete. tasks is a list of tasks. (Required) @@ -747,7 +749,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) @@ -756,28 +759,39 @@ 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) + cv_draw_points = self.__get_cv_draw_points(points) + 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) + # Reverse hollow points for opencv because this points are counter clockwise + cv_draw_points = self.__get_cv_draw_points( + utils.reverse_points(points)) + 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') @@ -826,7 +840,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: diff --git a/fastlabel/utils.py b/fastlabel/utils.py index 7984222..58d9254 100644 --- a/fastlabel/utils.py +++ b/fastlabel/utils.py @@ -21,3 +21,18 @@ def get_basename(file_path: str) -> str: path/to/file.jpg -> path/to/file """ return os.path.splitext(file_path)[0] + + +def reverse_points(points: list[int]) -> list[int]: + """ + e.g.) + [4, 5, 4, 9, 8, 9, 8, 5, 4, 5] => [4, 5, 8, 5, 8, 9, 4, 9, 4, 5] + """ + reversed_points = [] + for index, _ in enumerate(points): + if index % 2 == 0: + reversed_points.insert( + 0, points[index + 1]) + reversed_points.insert( + 0, points[index]) + return reversed_points