77from pathlib import Path
88from typing import Any , Dict , List , Tuple , Union
99
10+ import cv2
1011import numpy as np
1112from tqdm import tqdm
1213
@@ -65,21 +66,32 @@ def convert(self, info_list: List[Path]) -> None:
6566 id_img_dict = {v ["id" ]: v for v in data .get ("images" )}
6667 all_annotaions = data .get ("annotations" )
6768 for one_anno in tqdm (all_annotaions ):
68- image_info = id_img_dict .get (one_anno ["image_id" ])
69- img_name = image_info .get ("file_name" )
70- img_height = image_info .get ("height" )
71- img_width = image_info .get ("width" )
72-
73- seg_info = one_anno .get ("segmentation" )
74- if seg_info :
75- bbox = self .get_bbox (seg_info )
69+ img_info = id_img_dict .get (one_anno ["image_id" ])
70+ img_name = img_info .get ("file_name" )
71+ img_height = img_info .get ("height" )
72+ img_width = img_info .get ("width" )
73+ category_id = int (one_anno .get ("category_id" )) - 1
74+
75+ bbox_info = one_anno .get ("bbox" , None )
76+ seg_info = one_anno .get ("segmentation" , None )
77+
78+ if bbox_info :
79+ x0 , y0 , w , h = bbox_info
80+ xyxy_bbox = [x0 , y0 , x0 + w , y0 + h ]
81+ xywh = self .xyxy_to_xywh (xyxy_bbox , img_width , img_height )
82+ elif seg_info :
83+ points = np .array (seg_info ).reshape (4 , 2 )
84+ bbox = self .get_bbox_from_poly (img_height , img_width , points )
7685 xywh = self .xyxy_to_xywh (bbox , img_width , img_height )
77- category_id = int ( one_anno . get ( "category_id" )) - 1
78- xywh_str = " " . join ([ str ( v ) for v in xywh ] )
79- label_str = f" { category_id } { xywh_str } "
86+ else :
87+ print ( "The bbox and segmentation are all None, skip current anno." )
88+ continue
8089
81- txt_full_path = save_dir / f"{ Path (img_name ).stem } .txt"
82- self .write_txt (txt_full_path , label_str , mode = "a" )
90+ xywh_str = " " .join ([str (v ) for v in xywh ])
91+ label_str = f"{ category_id } { xywh_str } "
92+
93+ txt_full_path = save_dir / f"{ Path (img_name ).stem } .txt"
94+ self .write_txt (txt_full_path , label_str , mode = "a" )
8395
8496 img_full_path = img_dir / img_name
8597 shutil .copy2 (img_full_path , save_dir )
@@ -94,18 +106,42 @@ def gen_classes_txt(self, save_dir: Path, categories_dict: List[Dict[str, str]])
94106 class_info = [value ["name" ] for value in categories_dict ]
95107 self .write_txt (save_dir / "classes.txt" , class_info )
96108
97- def get_bbox (self , seg_info : List [List [float ]]) -> List [float ]:
98- seg_info = np .array (seg_info [0 ]).reshape (4 , 2 )
99- x0 , y0 = np .min (seg_info , axis = 0 )
100- x1 , y1 = np .max (seg_info , axis = 0 )
101- bbox = [x0 , y0 , x1 , y1 ]
109+ def get_bbox_from_poly (
110+ self , img_h : int , img_w : int , points : np .ndarray
111+ ) -> List [float ]:
112+ mask = np .zeros ((img_h , img_w ), dtype = "uint8" )
113+ img_mask = cv2 .fillPoly (mask , np .int32 ([points ]), 255 )
114+ contours , _ = cv2 .findContours (img_mask , cv2 .RETR_LIST , cv2 .CHAIN_APPROX_SIMPLE )
115+ contour = contours [0 ]
116+ bbox = self .get_mini_boxes (contour )
102117 return bbox
103118
104119 @staticmethod
105- def write_txt (save_path : str , content : List [str ], mode = "w" ) -> None :
106- if not isinstance (save_path , str ):
107- save_path = str (save_path )
120+ def get_mini_boxes (contour ) -> List [int ]:
121+ bounding_box = cv2 .minAreaRect (contour )
122+ points = sorted (list (cv2 .boxPoints (bounding_box )), key = lambda x : x [0 ])
123+
124+ index_1 , index_2 , index_3 , index_4 = 0 , 1 , 2 , 3
125+ if points [1 ][1 ] > points [0 ][1 ]:
126+ index_1 = 0
127+ index_4 = 1
128+ else :
129+ index_1 = 1
130+ index_4 = 0
131+ if points [3 ][1 ] > points [2 ][1 ]:
132+ index_2 = 2
133+ index_3 = 3
134+ else :
135+ index_2 = 3
136+ index_3 = 2
137+
138+ box = [points [index_1 ], points [index_2 ], points [index_3 ], points [index_4 ]]
139+ box = np .round (box ).astype (np .int32 ).tolist ()
140+ left_top , right_bottom = box [0 ], box [2 ]
141+ return left_top + right_bottom
108142
143+ @staticmethod
144+ def write_txt (save_path : str , content : List [str ], mode = "w" ) -> None :
109145 if isinstance (content , str ):
110146 content = [content ]
111147
0 commit comments