11from concurrent .futures import ThreadPoolExecutor
2+ from datetime import datetime
3+ from decimal import Decimal
24from typing import List
35
46import copy
@@ -76,7 +78,7 @@ def __get_categories(tasks: list, annotations: list) -> list:
7678 values = []
7779 for task in tasks :
7880 for task_annotation in task ["annotations" ]:
79- if task_annotation ["type" ] != AnnotationType .bbox .value and task_annotation [ "type" ] != AnnotationType .polygon .value and task_annotation [ "type" ] != AnnotationType .pose_estimation .value :
81+ if task_annotation ["type" ] not in [ AnnotationType .bbox .value , AnnotationType .polygon .value , AnnotationType . segmentation . value , AnnotationType .pose_estimation .value ] :
8082 continue
8183 values .append (task_annotation ["value" ])
8284 values = list (set (values ))
@@ -131,7 +133,7 @@ def __to_annotation(data: dict) -> dict:
131133 annotation_type = annotation ["type" ]
132134 annotation_id = 0
133135
134- if annotation_type != AnnotationType .bbox .value and annotation_type != AnnotationType .polygon .value and annotation_type != AnnotationType .pose_estimation .value :
136+ if annotation_type not in [ AnnotationType .bbox .value , AnnotationType .polygon .value , AnnotationType . segmentation . value , AnnotationType .pose_estimation .value ] :
135137 return None
136138 if annotation_type != AnnotationType .pose_estimation .value and (not points or len (points )) == 0 :
137139 return None
@@ -144,7 +146,6 @@ def __to_annotation(data: dict) -> dict:
144146 annotation_id , points , keypoints , category ["id" ], image , annotation_type )
145147
146148
147-
148149def __get_category_by_name (categories : list , name : str ) -> str :
149150 category = [
150151 category for category in categories if category ["name" ] == name ][0 ]
@@ -169,46 +170,104 @@ def __get_annotation(id_: int, points: list, keypoints: list, category_id: int,
169170 annotation ["num_keypoints" ] = len (keypoints ) if keypoints else 0
170171 annotation ["keypoints" ] = __get_coco_annotation_keypoints (
171172 keypoints ) if keypoints else []
172- annotation ["segmentation" ] = [points ] if points else []
173+ annotation ["segmentation" ] = __to_coco_segmentation (
174+ annotation_type , points )
173175 annotation ["iscrowd" ] = 0
174- annotation ["area" ] = __calc_area (annotation_type , points ) if points else 0
176+ annotation ["area" ] = __to_area (annotation_type , points )
175177 annotation ["image_id" ] = image ["id" ]
176- annotation ["bbox" ] = __to_bbox (points ) if points else []
178+ annotation ["bbox" ] = __to_bbox (annotation_type , points )
177179 annotation ["category_id" ] = category_id
178180 annotation ["id" ] = id_
179181 return annotation
180182
181183
182- def __to_bbox (points : list ) -> list :
183- points_splitted = [points [idx :idx + 2 ]
184- for idx in range (0 , len (points ), 2 )]
184+ def __get_without_hollowed_points (points : list ) -> list :
185+ return [region [0 ] for region in points ]
186+
187+
188+ def __to_coco_segmentation (annotation_type : str , points : list ) -> list :
189+ if not points :
190+ return []
191+ if annotation_type == AnnotationType .segmentation .value :
192+ # Remove hollowed points
193+ return __get_without_hollowed_points (points )
194+ return [points ]
195+
196+
197+ def __to_bbox (annotation_type : str , points : list ) -> list :
198+ if not points :
199+ return []
200+ base_points = []
201+ if annotation_type == AnnotationType .segmentation .value :
202+ base_points = sum (__get_without_hollowed_points (points ), [])
203+ else :
204+ base_points = points
205+ points_splitted = [
206+ base_points [idx : idx + 2 ] for idx in range (0 , len (base_points ), 2 )
207+ ]
185208 polygon_geo = geojson .Polygon (points_splitted )
186209 coords = np .array (list (geojson .utils .coords (polygon_geo )))
187210 left_top_x = coords [:, 0 ].min ()
188211 left_top_y = coords [:, 1 ].min ()
189212 right_bottom_x = coords [:, 0 ].max ()
190213 right_bottom_y = coords [:, 1 ].max ()
214+ width = right_bottom_x - left_top_x
215+ height = right_bottom_y - left_top_y
216+ return [__serialize (point ) for point in [left_top_x , left_top_y , width , height ]]
191217
192- return [
193- left_top_x , # x
194- left_top_y , # y
195- right_bottom_x - left_top_x , # width
196- right_bottom_y - left_top_y , # height
197- ]
218+
219+ def __to_area (annotation_type : str , points : list ) -> float :
220+ if not points :
221+ return 0
222+ area = 0
223+ if annotation_type == AnnotationType .segmentation .value :
224+ for region in __get_without_hollowed_points (points ):
225+ area += __calc_area (annotation_type , region )
226+ else :
227+ area = __calc_area (annotation_type , points )
228+ return __serialize (area )
198229
199230
200231def __calc_area (annotation_type : str , points : list ) -> float :
201- area = 0
232+ if not points :
233+ return 0
202234 if annotation_type == AnnotationType .bbox .value :
203235 width = points [0 ] - points [2 ]
204236 height = points [1 ] - points [3 ]
205- area = width * height
206- elif annotation_type == AnnotationType .polygon .value :
237+ return width * height
238+ elif annotation_type in [ AnnotationType .polygon .value , AnnotationType . segmentation . value ] :
207239 x = points [0 ::2 ]
208240 y = points [1 ::2 ]
209- area = 0.5 * np .abs (np .dot (x , np .roll (y , 1 )) -
210- np .dot (y , np .roll (x , 1 )))
211- return area
241+ return 0.5 * np .abs (
242+ np .dot (x , np .roll (y , 1 )) - np .dot (y , np .roll (x , 1 ))
243+ )
244+ else :
245+ raise Exception (f"Unsupported annotation type: { annotation_type } " )
246+
247+
248+ def __serialize (value : any ) -> any :
249+ if isinstance (value , datetime ):
250+ return value .isoformat ()
251+ if isinstance (value , Decimal ):
252+ float_value = float (value )
253+ if float_value .is_integer ():
254+ return int (value )
255+ else :
256+ return float_value
257+ if isinstance (value , float ):
258+ if value .is_integer ():
259+ return int (value )
260+ if isinstance (value , np .integer ):
261+ return int (value )
262+ if isinstance (value , np .floating ):
263+ float_value = float (value )
264+ if float_value .is_integer ():
265+ return int (value )
266+ else :
267+ return float_value
268+ if isinstance (value , np .ndarray ):
269+ return value .tolist ()
270+ return value
212271
213272
214273# YOLO
@@ -373,6 +432,7 @@ def to_pascalvoc(tasks: list) -> list:
373432 pascalvoc .append (voc )
374433 return pascalvoc
375434
435+
376436def __get_pascalvoc_obj (data : dict ) -> dict :
377437 annotation = data ["annotation" ]
378438 points = annotation ["points" ]
@@ -403,6 +463,7 @@ def __get_pascalvoc_obj(data: dict) -> dict:
403463 },
404464 }
405465
466+
406467def __get_pascalvoc_tag_value (annotation : dict , target_tag_name : str ) -> int :
407468 attributes = annotation ["attributes" ]
408469 if not attributes :
@@ -416,44 +477,46 @@ def __get_pascalvoc_tag_value(annotation: dict, target_tag_name: str) -> int:
416477
417478
418479def to_labelme (tasks : list ) -> list :
419- labelmes = []
480+ labelmes = []
420481 for task in tasks :
421482 shapes = []
422483 for annotation in task ["annotations" ]:
423- shape_type = __to_labelme_shape_type (annotation ["type" ])
424- if not shape_type :
425- continue
426- points = annotation ["points" ]
427- if len (points ) == 0 :
428- continue
484+ shape_type = __to_labelme_shape_type (annotation ["type" ])
485+ if not shape_type :
486+ continue
487+ points = annotation ["points" ]
488+ if len (points ) == 0 :
489+ continue
429490
430- shape_points = []
431- if annotation ["type" ] == "segmentation" :
432- for i in range (int (len (points [0 ][0 ]) / 2 )):
433- shape_points .append ([points [0 ][0 ][i * 2 ], points [0 ][0 ][(i * 2 ) + 1 ]])
434- else :
435- for i in range (int (len (points ) / 2 )):
436- shape_points .append ([points [i * 2 ], points [(i * 2 ) + 1 ]])
437-
438- shape = {
439- "label" : annotation ["value" ],
440- "points" : shape_points ,
441- "group_id" : None ,
442- "shape_type" : shape_type ,
443- "flags" : {}
444- }
445- shapes .append (shape )
491+ shape_points = []
492+ if annotation ["type" ] == "segmentation" :
493+ for i in range (int (len (points [0 ][0 ]) / 2 )):
494+ shape_points .append (
495+ [points [0 ][0 ][i * 2 ], points [0 ][0 ][(i * 2 ) + 1 ]])
496+ else :
497+ for i in range (int (len (points ) / 2 )):
498+ shape_points .append ([points [i * 2 ], points [(i * 2 ) + 1 ]])
499+
500+ shape = {
501+ "label" : annotation ["value" ],
502+ "points" : shape_points ,
503+ "group_id" : None ,
504+ "shape_type" : shape_type ,
505+ "flags" : {}
506+ }
507+ shapes .append (shape )
446508 labelmes .append ({
447- "version" : "4.5.9" ,
448- "flags" : {},
449- "shapes" : shapes ,
450- "imagePath" : task ["name" ],
451- "imageData" : None ,
452- "imageHeight" : task ["height" ],
453- "imageWidth" : task ["width" ],
509+ "version" : "4.5.9" ,
510+ "flags" : {},
511+ "shapes" : shapes ,
512+ "imagePath" : task ["name" ],
513+ "imageData" : None ,
514+ "imageHeight" : task ["height" ],
515+ "imageWidth" : task ["width" ],
454516 })
455517 return labelmes
456518
519+
457520def __to_labelme_shape_type (annotation_type : str ) -> str :
458521 if annotation_type == "polygon" or annotation_type == "segmentation" :
459522 return "polygon"
@@ -515,10 +578,12 @@ def to_pixel_coordinates(tasks: list) -> list:
515578 new_regions .append (new_region )
516579 annotation ["points" ] = new_regions
517580 elif annotation ["type" ] == AnnotationType .polygon .value :
518- new_points = __remove_duplicated_coordinates (annotation ["points" ])
581+ new_points = __remove_duplicated_coordinates (
582+ annotation ["points" ])
519583 annotation ["points" ] = new_points
520584 return tasks
521585
586+
522587def __remove_duplicated_coordinates (points : List [int ]) -> List [int ]:
523588 """
524589 Remove duplicated coordinates.
@@ -554,6 +619,7 @@ def __remove_duplicated_coordinates(points: List[int]) -> List[int]:
554619 new_points .append (points [i * 2 + 1 ])
555620 return new_points
556621
622+
557623def __get_pixel_coordinates (points : List [int or float ]) -> List [int ]:
558624 """
559625 Remove diagonal coordinates and return pixel outline coordinates.
@@ -632,6 +698,7 @@ def execute_coco_to_fastlabel(coco: dict) -> dict:
632698 results [coco_images [coco_image_key ]] = annotations
633699 return results
634700
701+
635702def execute_labelme_to_fastlabel (labelme : dict , file_path : str = None ) -> tuple :
636703 file_name = ""
637704 if file_path :
@@ -658,6 +725,7 @@ def execute_labelme_to_fastlabel(labelme: dict, file_path: str = None) -> tuple:
658725
659726 return (file_name , annotations )
660727
728+
661729def execute_pascalvoc_to_fastlabel (pascalvoc : dict , file_path : str = None ) -> tuple :
662730 target_pascalvoc = pascalvoc ["annotation" ]
663731 file_name = "" # file_path if file_path else target_pascalvoc["filename"]
@@ -713,16 +781,24 @@ def execute_yolo_to_fastlabel(
713781
714782 classs_name = classes [str (yolo_class_id )]
715783
716- yolo_center_x_point = float (image_width ) * float (yolo_center_x_ratio )
717- yolo_center_y_point = float (image_height ) * float (yolo_center_y_ratio )
718- yolo_anno_width_size = float (image_width ) * float (yolo_anno_width_ratio )
719- yolo_anno_height_size = float (image_height ) * float (yolo_anno_height_ratio )
784+ yolo_center_x_point = float (
785+ image_width ) * float (yolo_center_x_ratio )
786+ yolo_center_y_point = float (
787+ image_height ) * float (yolo_center_y_ratio )
788+ yolo_anno_width_size = float (
789+ image_width ) * float (yolo_anno_width_ratio )
790+ yolo_anno_height_size = float (
791+ image_height ) * float (yolo_anno_height_ratio )
720792
721793 points = []
722- points .append (yolo_center_x_point - (yolo_anno_width_size / 2 )) # x1
723- points .append (yolo_center_y_point - (yolo_anno_height_size / 2 )) # y1
724- points .append (yolo_center_x_point + (yolo_anno_width_size / 2 )) # x2
725- points .append (yolo_center_y_point + (yolo_anno_height_size / 2 )) # y2
794+ points .append (yolo_center_x_point -
795+ (yolo_anno_width_size / 2 )) # x1
796+ points .append (yolo_center_y_point -
797+ (yolo_anno_height_size / 2 )) # y1
798+ points .append (yolo_center_x_point +
799+ (yolo_anno_width_size / 2 )) # x2
800+ points .append (yolo_center_y_point +
801+ (yolo_anno_height_size / 2 )) # y2
726802 annotations .append (
727803 {
728804 "value" : classs_name ,
@@ -742,6 +818,7 @@ def execute_yolo_to_fastlabel(
742818
743819 return results
744820
821+
745822def __get_annotation_type_by_labelme (shape_type : str ) -> str :
746823 if shape_type == "rectangle" :
747824 return "bbox"
0 commit comments