3344import argparse
55import json
6+ import math
67import random
78import shutil
89import time
910from pathlib import Path
10- from typing import List , Union
11+ from typing import List , Tuple , Union
1112
1213import cv2
1314import numpy as np
1415from tqdm import tqdm
1516
1617ValueType = Union [str , Path , None ]
17- RECTANGLE = "rectangle"
18- POLYGON = "polygon"
18+ RECTANGLE : str = "rectangle"
19+ POLYGON : str = "polygon"
20+ CIRCLE : str = "circle"
1921
2022
2123class LabelmeToCOCO :
@@ -64,9 +66,7 @@ def __init__(
6466
6567 self .categories = self ._get_category ()
6668
67- def __call__ (
68- self ,
69- ):
69+ def __call__ (self ):
7070 img_list = self .get_img_list ()
7171 if not img_list :
7272 raise ValueError (f"{ self .data_dir } is empty!" )
@@ -146,9 +146,7 @@ def _init_json(self):
146146 }
147147 return annotation_info
148148
149- def _get_category (
150- self ,
151- ):
149+ def _get_category (self ):
152150 json_list = Path (self .data_dir ).glob ("*.json" )
153151 all_categories = []
154152 for json_path in json_list :
@@ -198,9 +196,9 @@ def generate_json(self, img_list, save_dir):
198196 anno_list = []
199197 for shape in shapes :
200198 shape_type = shape .get ("shape_type" )
201- if shape_type not in [RECTANGLE , POLYGON ]:
199+ if shape_type not in [RECTANGLE , POLYGON , CIRCLE ]:
202200 print (
203- f"Current shape type is { shape_type } , not between { RECTANGLE } and { POLYGON } , skip"
201+ f"Current shape type is { shape_type } , not between { RECTANGLE } , { CIRCLE } and { POLYGON } , skip"
204202 )
205203 continue
206204
@@ -209,17 +207,25 @@ def generate_json(self, img_list, save_dir):
209207 points = np .array (shape .get ("points" ))
210208
211209 if shape_type == RECTANGLE :
212- seg_points = [np .ravel (points , order = "C" ).tolist ()]
213-
214210 x0 , y0 = np .min (points , axis = 0 )
215211 x1 , y1 = np .max (points , axis = 0 )
216- w , h = x1 - x1 , y1 - y0
212+ seg_points = [[x0 , y0 , x1 , y0 , x1 , y1 , x0 , y1 ]]
213+
214+ w , h = x1 - x0 , y1 - y0
217215 bbox_points = [x0 , y0 , w , h ]
218216 area = w * h
219-
220217 elif shape_type == POLYGON :
221218 seg_points = points .tolist ()
222219 bbox_points , area = self .cvt_poly_to_rect (img_h , img_w , points )
220+ elif shape_type == CIRCLE :
221+ circle_center , radius_point = points .tolist ()
222+ x0 , y0 , x1 , y1 = self .cvt_circle_to_rect (
223+ circle_center , radius_point
224+ )
225+ seg_points = [[x0 , y0 , x1 , y0 , x1 , y1 , x0 , y1 ]]
226+ w , h = x1 - x0 , y1 - y0
227+ bbox_points = [x0 , y0 , w , h ]
228+ area = w * h
223229 else :
224230 print (f"Current { shape_type } is not supported!" )
225231 continue
@@ -300,13 +306,31 @@ def get_mini_boxes(contour) -> List[int]:
300306 box_h = right_bottom [1 ] - left_top [1 ]
301307 return left_top + [box_w , box_h ]
302308
309+ @staticmethod
310+ def cvt_circle_to_rect (circle_center : float , radius_point : float ) -> Tuple [float ]:
311+ """
312+ 根据圆心和圆上一点计算正方形边界框的左上角和右下角坐标。
313+ modified from https://blog.csdn.net/jacke121/article/details/137387901
314+ """
315+ # 计算半径
316+ radius = math .sqrt (
317+ (circle_center [0 ] - radius_point [0 ]) ** 2
318+ + (circle_center [1 ] - radius_point [1 ]) ** 2
319+ )
320+
321+ # 计算正方形边界框的左上角和右下角坐标
322+ x0 , y0 = circle_center [0 ] - radius , circle_center [1 ] - radius
323+ x1 , y1 = circle_center [0 ] + radius , circle_center [1 ] + radius
324+
325+ return x0 , y0 , x1 , y1
326+
303327
304328def main ():
305329 parser = argparse .ArgumentParser ("Datasets converter from labelme to COCO" )
306330 parser .add_argument (
307331 "--data_dir" ,
308332 type = str ,
309- default = "/Users/joshuawang /projects/_self/ LabelConvert/data " ,
333+ default = "/Users/jiahuawang /projects/LabelConvert/tests/test_files/labelme_dataset " ,
310334 )
311335 parser .add_argument ("--save_dir" , type = str , default = None )
312336 parser .add_argument ("--val_ratio" , type = float , default = 0.2 )
0 commit comments