|
| 1 | +import os |
| 2 | +import json |
| 3 | +import argparse |
| 4 | +from shutil import copyfile |
| 5 | + |
| 6 | + |
| 7 | +# parser = argparse.ArgumentParser(description='Test yolo data.') |
| 8 | +# parser.add_argument('-j', help='JSON file', dest='json', required=True) |
| 9 | +# parser.add_argument('-o', help='path to output folder', dest='out', required=True) |
| 10 | +# |
| 11 | +# args = parser.parse_args() |
| 12 | +# |
| 13 | +# json_file = args.json |
| 14 | +# output = args.out |
| 15 | + |
| 16 | +class COCO2YOLO: |
| 17 | + def __init__(self, json_file, output_path): |
| 18 | + self.json_file = json_file |
| 19 | + self.output_path = output_path |
| 20 | + self.output_image_path = output_path.replace('labels', 'images') |
| 21 | + self.output_folder = os.path.dirname(os.path.dirname(self.output_path)) |
| 22 | + self._check_file_and_dir() |
| 23 | + self.labels = json.load(open(json_file, 'r', encoding='utf-8')) |
| 24 | + self.coco_id_name_map = self._categories() |
| 25 | + self.coco_name_list = list(self.coco_id_name_map.values()) |
| 26 | + print("total images", len(self.labels['images'])) |
| 27 | + print("total categories", len(self.labels['categories'])) |
| 28 | + print("total labels", len(self.labels['annotations'])) |
| 29 | + |
| 30 | + def _check_file_and_dir(self): |
| 31 | + if not os.path.exists(self.json_file): |
| 32 | + raise ValueError("file not found") |
| 33 | + os.makedirs(self.output_path, exist_ok=True) |
| 34 | + os.makedirs(self.output_image_path, exist_ok=True) |
| 35 | + |
| 36 | + def _categories(self): |
| 37 | + categories = {} |
| 38 | + for cls in self.labels['categories']: |
| 39 | + categories[cls['id']] = cls['name'] |
| 40 | + return categories |
| 41 | + |
| 42 | + def _load_images_info(self): |
| 43 | + images_info = {} |
| 44 | + for image in self.labels['images']: |
| 45 | + id = image['id'] |
| 46 | + file_name = image['file_name'] |
| 47 | + if file_name.find('\\') > -1: |
| 48 | + file_name = file_name[file_name.index('\\') + 1:] |
| 49 | + w = image['width'] |
| 50 | + h = image['height'] |
| 51 | + images_info[id] = (file_name, w, h) |
| 52 | + |
| 53 | + return images_info |
| 54 | + |
| 55 | + def _bbox_2_yolo(self, bbox, img_w, img_h): |
| 56 | + x, y, w, h = bbox[0], bbox[1], bbox[2], bbox[3] |
| 57 | + centerx = bbox[0] + w / 2 |
| 58 | + centery = bbox[1] + h / 2 |
| 59 | + dw = 1 / img_w |
| 60 | + dh = 1 / img_h |
| 61 | + centerx *= dw |
| 62 | + w *= dw |
| 63 | + centery *= dh |
| 64 | + h *= dh |
| 65 | + return centerx, centery, w, h |
| 66 | + |
| 67 | + def _convert_anno(self, images_info): |
| 68 | + anno_dict = dict() |
| 69 | + for anno in self.labels['annotations']: |
| 70 | + bbox = anno['bbox'] |
| 71 | + image_id = anno['image_id'] |
| 72 | + category_id = anno['category_id'] |
| 73 | + |
| 74 | + image_info = images_info.get(image_id) |
| 75 | + image_name = image_info[0] |
| 76 | + img_w = image_info[1] |
| 77 | + img_h = image_info[2] |
| 78 | + yolo_box = self._bbox_2_yolo(bbox, img_w, img_h) |
| 79 | + |
| 80 | + anno_info = (image_name, category_id, yolo_box) |
| 81 | + anno_infos = anno_dict.get(image_id) |
| 82 | + if not anno_infos: |
| 83 | + anno_dict[image_id] = [anno_info] |
| 84 | + else: |
| 85 | + anno_infos.append(anno_info) |
| 86 | + anno_dict[image_id] = anno_infos |
| 87 | + return anno_dict |
| 88 | + |
| 89 | + def save_classes(self): |
| 90 | + sorted_classes = list(map(lambda x: x['name'], sorted(self.labels['categories'], key=lambda x: x['id']))) |
| 91 | + print('coco names', sorted_classes) |
| 92 | + with open(f'{self.output_folder}/classes.txt', 'w', encoding='utf-8') as f: |
| 93 | + for cls in sorted_classes: |
| 94 | + f.write(cls + '\n') |
| 95 | + f.close() |
| 96 | + |
| 97 | + def coco2yolo(self): |
| 98 | + print("loading image info...") |
| 99 | + images_info = self._load_images_info() |
| 100 | + print("loading done, total images", len(images_info)) |
| 101 | + |
| 102 | + print("start converting...") |
| 103 | + anno_dict = self._convert_anno(images_info) |
| 104 | + print("converting done, total labels", len(anno_dict)) |
| 105 | + |
| 106 | + self.save_classes() |
| 107 | + |
| 108 | + print("saving txt file...") |
| 109 | + self._save_txt(anno_dict) |
| 110 | + print("saving done") |
| 111 | + |
| 112 | + def _save_txt(self, anno_dict): |
| 113 | + raw_images_path = os.path.join(os.path.dirname(self.json_file), 'images') |
| 114 | + for k, v in anno_dict.items(): |
| 115 | + file_name = os.path.splitext(v[0][0])[0] + ".txt" |
| 116 | + image_name = os.path.splitext(v[0][0])[0] + ".png" |
| 117 | + copyfile(f'{raw_images_path}/{image_name}', f'{self.output_image_path}/{image_name}') |
| 118 | + with open(os.path.join(self.output_path, file_name), 'w', encoding='utf-8') as f: |
| 119 | + # print(k, v) |
| 120 | + for obj in v: |
| 121 | + cat_name = self.coco_id_name_map.get(obj[1]) |
| 122 | + category_id = self.coco_name_list.index(cat_name) |
| 123 | + box = ['{:.6f}'.format(x) for x in obj[2]] |
| 124 | + box = ' '.join(box) |
| 125 | + line = str(category_id) + ' ' + box |
| 126 | + f.write(line + '\n') |
| 127 | + |
| 128 | + |
| 129 | +if __name__ == '__main__': |
| 130 | + mode = 'val' |
| 131 | + json_file = f'COCO_datasets/Multi-modal_COCO_dataset_2023-12-14-13_52_07/{mode}.json' |
| 132 | + output = f'YOLO_datasets/Multi-modal_COCO_dataset_2023-12-14-13_52_07/labels/{mode}' |
| 133 | + c2y = COCO2YOLO(json_file, output) |
| 134 | + c2y.coco2yolo() |
0 commit comments