| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import argparse |
| import glob |
| import json |
| import os |
| import os.path as osp |
| import shutil |
| import xml.etree.ElementTree as ET |
|
|
| import numpy as np |
| import PIL.ImageDraw |
| from tqdm import tqdm |
| import cv2 |
|
|
| label_to_num = {} |
| categories_list = [] |
| labels_list = [] |
|
|
|
|
| class MyEncoder(json.JSONEncoder): |
| def default(self, obj): |
| if isinstance(obj, np.integer): |
| return int(obj) |
| elif isinstance(obj, np.floating): |
| return float(obj) |
| elif isinstance(obj, np.ndarray): |
| return obj.tolist() |
| else: |
| return super(MyEncoder, self).default(obj) |
|
|
|
|
| def images_labelme(data, num): |
| image = {} |
| image['height'] = data['imageHeight'] |
| image['width'] = data['imageWidth'] |
| image['id'] = num + 1 |
| if '\\' in data['imagePath']: |
| image['file_name'] = data['imagePath'].split('\\')[-1] |
| else: |
| image['file_name'] = data['imagePath'].split('/')[-1] |
| return image |
|
|
|
|
| def images_cityscape(data, num, img_file): |
| image = {} |
| image['height'] = data['imgHeight'] |
| image['width'] = data['imgWidth'] |
| image['id'] = num + 1 |
| image['file_name'] = img_file |
| return image |
|
|
|
|
| def categories(label, labels_list): |
| category = {} |
| category['supercategory'] = 'component' |
| category['id'] = len(labels_list) + 1 |
| category['name'] = label |
| return category |
|
|
|
|
| def annotations_rectangle(points, label, image_num, object_num, label_to_num): |
| annotation = {} |
| seg_points = np.asarray(points).copy() |
| seg_points[1, :] = np.asarray(points)[2, :] |
| seg_points[2, :] = np.asarray(points)[1, :] |
| annotation['segmentation'] = [list(seg_points.flatten())] |
| annotation['iscrowd'] = 0 |
| annotation['image_id'] = image_num + 1 |
| annotation['bbox'] = list( |
| map(float, [ |
| points[0][0], points[0][1], points[1][0] - points[0][0], points[1][ |
| 1] - points[0][1] |
| ])) |
| annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3] |
| annotation['category_id'] = label_to_num[label] |
| annotation['id'] = object_num + 1 |
| return annotation |
|
|
|
|
| def annotations_polygon(height, width, points, label, image_num, object_num, |
| label_to_num): |
| annotation = {} |
| annotation['segmentation'] = [list(np.asarray(points).flatten())] |
| annotation['iscrowd'] = 0 |
| annotation['image_id'] = image_num + 1 |
| annotation['bbox'] = list(map(float, get_bbox(height, width, points))) |
| annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3] |
| annotation['category_id'] = label_to_num[label] |
| annotation['id'] = object_num + 1 |
| return annotation |
|
|
|
|
| def get_bbox(height, width, points): |
| polygons = points |
| mask = np.zeros([height, width], dtype=np.uint8) |
| mask = PIL.Image.fromarray(mask) |
| xy = list(map(tuple, polygons)) |
| PIL.ImageDraw.Draw(mask).polygon(xy=xy, outline=1, fill=1) |
| mask = np.array(mask, dtype=bool) |
| index = np.argwhere(mask == 1) |
| rows = index[:, 0] |
| clos = index[:, 1] |
| left_top_r = np.min(rows) |
| left_top_c = np.min(clos) |
| right_bottom_r = np.max(rows) |
| right_bottom_c = np.max(clos) |
| return [ |
| left_top_c, left_top_r, right_bottom_c - left_top_c, |
| right_bottom_r - left_top_r |
| ] |
|
|
|
|
| def deal_json(ds_type, img_path, json_path): |
| data_coco = {} |
| images_list = [] |
| annotations_list = [] |
| image_num = -1 |
| object_num = -1 |
| for img_file in os.listdir(img_path): |
| img_label = os.path.splitext(img_file)[0] |
| if img_file.split('.')[ |
| -1] not in ['bmp', 'jpg', 'jpeg', 'png', 'JPEG', 'JPG', 'PNG']: |
| continue |
| label_file = osp.join(json_path, img_label + '.json') |
| print('Generating dataset from:', label_file) |
| image_num = image_num + 1 |
| with open(label_file) as f: |
| data = json.load(f) |
| if ds_type == 'labelme': |
| images_list.append(images_labelme(data, image_num)) |
| elif ds_type == 'cityscape': |
| images_list.append(images_cityscape(data, image_num, img_file)) |
| if ds_type == 'labelme': |
| for shapes in data['shapes']: |
| object_num = object_num + 1 |
| label = shapes['label'] |
| if label not in labels_list: |
| categories_list.append(categories(label, labels_list)) |
| labels_list.append(label) |
| label_to_num[label] = len(labels_list) |
| p_type = shapes['shape_type'] |
| if p_type == 'polygon': |
| points = shapes['points'] |
| annotations_list.append( |
| annotations_polygon(data['imageHeight'], data[ |
| 'imageWidth'], points, label, image_num, |
| object_num, label_to_num)) |
|
|
| if p_type == 'rectangle': |
| (x1, y1), (x2, y2) = shapes['points'] |
| x1, x2 = sorted([x1, x2]) |
| y1, y2 = sorted([y1, y2]) |
| points = [[x1, y1], [x2, y2], [x1, y2], [x2, y1]] |
| annotations_list.append( |
| annotations_rectangle(points, label, image_num, |
| object_num, label_to_num)) |
| elif ds_type == 'cityscape': |
| for shapes in data['objects']: |
| object_num = object_num + 1 |
| label = shapes['label'] |
| if label not in labels_list: |
| categories_list.append(categories(label, labels_list)) |
| labels_list.append(label) |
| label_to_num[label] = len(labels_list) |
| points = shapes['polygon'] |
| annotations_list.append( |
| annotations_polygon(data['imgHeight'], data[ |
| 'imgWidth'], points, label, image_num, object_num, |
| label_to_num)) |
| data_coco['images'] = images_list |
| data_coco['categories'] = categories_list |
| data_coco['annotations'] = annotations_list |
| return data_coco |
|
|
|
|
| def voc_get_label_anno(ann_dir_path, ann_ids_path, labels_path): |
| with open(labels_path, 'r') as f: |
| labels_str = f.read().split() |
| labels_ids = list(range(1, len(labels_str) + 1)) |
|
|
| with open(ann_ids_path, 'r') as f: |
| ann_ids = [lin.strip().split(' ')[-1] for lin in f.readlines()] |
|
|
| ann_paths = [] |
| for aid in ann_ids: |
| if aid.endswith('xml'): |
| ann_path = os.path.join(ann_dir_path, aid) |
| else: |
| ann_path = os.path.join(ann_dir_path, aid + '.xml') |
| ann_paths.append(ann_path) |
|
|
| return dict(zip(labels_str, labels_ids)), ann_paths |
|
|
|
|
| def voc_get_image_info(annotation_root, im_id): |
| filename = annotation_root.findtext('filename') |
| assert filename is not None |
| img_name = os.path.basename(filename) |
|
|
| size = annotation_root.find('size') |
| width = float(size.findtext('width')) |
| height = float(size.findtext('height')) |
|
|
| image_info = { |
| 'file_name': filename, |
| 'height': height, |
| 'width': width, |
| 'id': im_id |
| } |
| return image_info |
|
|
|
|
| def voc_get_coco_annotation(obj, label2id): |
| label = obj.findtext('name') |
| assert label in label2id, "label is not in label2id." |
| category_id = label2id[label] |
| bndbox = obj.find('bndbox') |
| xmin = float(bndbox.findtext('xmin')) |
| ymin = float(bndbox.findtext('ymin')) |
| xmax = float(bndbox.findtext('xmax')) |
| ymax = float(bndbox.findtext('ymax')) |
| assert xmax > xmin and ymax > ymin, "Box size error." |
| o_width = xmax - xmin |
| o_height = ymax - ymin |
| anno = { |
| 'area': o_width * o_height, |
| 'iscrowd': 0, |
| 'bbox': [xmin, ymin, o_width, o_height], |
| 'category_id': category_id, |
| 'ignore': 0, |
| } |
| return anno |
|
|
|
|
| def voc_xmls_to_cocojson(annotation_paths, label2id, output_dir, output_file): |
| output_json_dict = { |
| "images": [], |
| "type": "instances", |
| "annotations": [], |
| "categories": [] |
| } |
| bnd_id = 1 |
| im_id = 0 |
| print('Start converting !') |
| for a_path in tqdm(annotation_paths): |
| |
| ann_tree = ET.parse(a_path) |
| ann_root = ann_tree.getroot() |
|
|
| img_info = voc_get_image_info(ann_root, im_id) |
| output_json_dict['images'].append(img_info) |
|
|
| for obj in ann_root.findall('object'): |
| ann = voc_get_coco_annotation(obj=obj, label2id=label2id) |
| ann.update({'image_id': im_id, 'id': bnd_id}) |
| output_json_dict['annotations'].append(ann) |
| bnd_id = bnd_id + 1 |
| im_id += 1 |
|
|
| for label, label_id in label2id.items(): |
| category_info = {'supercategory': 'none', 'id': label_id, 'name': label} |
| output_json_dict['categories'].append(category_info) |
| output_file = os.path.join(output_dir, output_file) |
| with open(output_file, 'w') as f: |
| output_json = json.dumps(output_json_dict) |
| f.write(output_json) |
|
|
|
|
| def widerface_to_cocojson(root_path): |
| train_gt_txt = os.path.join(root_path, "wider_face_split", "wider_face_train_bbx_gt.txt") |
| val_gt_txt = os.path.join(root_path, "wider_face_split", "wider_face_val_bbx_gt.txt") |
| train_img_dir = os.path.join(root_path, "WIDER_train", "images") |
| val_img_dir = os.path.join(root_path, "WIDER_val", "images") |
| assert train_gt_txt |
| assert val_gt_txt |
| assert train_img_dir |
| assert val_img_dir |
| save_path = os.path.join(root_path, "widerface_train.json") |
| widerface_convert(train_gt_txt, train_img_dir, save_path) |
| print("Wider Face train dataset converts sucess, the json path: {}".format(save_path)) |
| save_path = os.path.join(root_path, "widerface_val.json") |
| widerface_convert(val_gt_txt, val_img_dir, save_path) |
| print("Wider Face val dataset converts sucess, the json path: {}".format(save_path)) |
|
|
|
|
| def widerface_convert(gt_txt, img_dir, save_path): |
| output_json_dict = { |
| "images": [], |
| "type": "instances", |
| "annotations": [], |
| "categories": [{'supercategory': 'none', 'id': 0, 'name': "human_face"}] |
| } |
| bnd_id = 1 |
| im_id = 0 |
| print('Start converting !') |
| with open(gt_txt) as fd: |
| lines = fd.readlines() |
|
|
| i = 0 |
| while i < len(lines): |
| image_name = lines[i].strip() |
| bbox_num = int(lines[i + 1].strip()) |
| i += 2 |
| img_info = get_widerface_image_info(img_dir, image_name, im_id) |
| if img_info: |
| output_json_dict["images"].append(img_info) |
| for j in range(i, i + bbox_num): |
| anno = get_widerface_ann_info(lines[j]) |
| anno.update({'image_id': im_id, 'id': bnd_id}) |
| output_json_dict['annotations'].append(anno) |
| bnd_id += 1 |
| else: |
| print("The image dose not exist: {}".format(os.path.join(img_dir, image_name))) |
| bbox_num = 1 if bbox_num == 0 else bbox_num |
| i += bbox_num |
| im_id += 1 |
| with open(save_path, 'w') as f: |
| output_json = json.dumps(output_json_dict) |
| f.write(output_json) |
|
|
|
|
| def get_widerface_image_info(img_root, img_relative_path, img_id): |
| image_info = {} |
| save_path = os.path.join(img_root, img_relative_path) |
| if os.path.exists(save_path): |
| img = cv2.imread(save_path) |
| image_info["file_name"] = os.path.join(os.path.basename( |
| os.path.dirname(img_root)), os.path.basename(img_root), |
| img_relative_path) |
| image_info["height"] = img.shape[0] |
| image_info["width"] = img.shape[1] |
| image_info["id"] = img_id |
| return image_info |
|
|
|
|
| def get_widerface_ann_info(info): |
| info = [int(x) for x in info.strip().split()] |
| anno = { |
| 'area': info[2] * info[3], |
| 'iscrowd': 0, |
| 'bbox': [info[0], info[1], info[2], info[3]], |
| 'category_id': 0, |
| 'ignore': 0, |
| 'blur': info[4], |
| 'expression': info[5], |
| 'illumination': info[6], |
| 'invalid': info[7], |
| 'occlusion': info[8], |
| 'pose': info[9] |
| } |
| return anno |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| parser.add_argument( |
| '--dataset_type', |
| help='the type of dataset, can be `voc`, `widerface`, `labelme` or `cityscape`') |
| parser.add_argument('--json_input_dir', help='input annotated directory') |
| parser.add_argument('--image_input_dir', help='image directory') |
| parser.add_argument( |
| '--output_dir', help='output dataset directory', default='./') |
| parser.add_argument( |
| '--train_proportion', |
| help='the proportion of train dataset', |
| type=float, |
| default=1.0) |
| parser.add_argument( |
| '--val_proportion', |
| help='the proportion of validation dataset', |
| type=float, |
| default=0.0) |
| parser.add_argument( |
| '--test_proportion', |
| help='the proportion of test dataset', |
| type=float, |
| default=0.0) |
| parser.add_argument( |
| '--voc_anno_dir', |
| help='In Voc format dataset, path to annotation files directory.', |
| type=str, |
| default=None) |
| parser.add_argument( |
| '--voc_anno_list', |
| help='In Voc format dataset, path to annotation files ids list.', |
| type=str, |
| default=None) |
| parser.add_argument( |
| '--voc_label_list', |
| help='In Voc format dataset, path to label list. The content of each line is a category.', |
| type=str, |
| default=None) |
| parser.add_argument( |
| '--voc_out_name', |
| type=str, |
| default='voc.json', |
| help='In Voc format dataset, path to output json file') |
| parser.add_argument( |
| '--widerface_root_dir', |
| help='The root_path for wider face dataset, which contains `wider_face_split`, `WIDER_train` and `WIDER_val`.And the json file will save in this path', |
| type=str, |
| default=None) |
| args = parser.parse_args() |
| try: |
| assert args.dataset_type in ['voc', 'labelme', 'cityscape', 'widerface'] |
| except AssertionError as e: |
| print( |
| 'Now only support the voc, cityscape dataset and labelme dataset!!') |
| os._exit(0) |
|
|
| if args.dataset_type == 'voc': |
| assert args.voc_anno_dir and args.voc_anno_list and args.voc_label_list |
| label2id, ann_paths = voc_get_label_anno( |
| args.voc_anno_dir, args.voc_anno_list, args.voc_label_list) |
| voc_xmls_to_cocojson( |
| annotation_paths=ann_paths, |
| label2id=label2id, |
| output_dir=args.output_dir, |
| output_file=args.voc_out_name) |
| elif args.dataset_type == "widerface": |
| assert args.widerface_root_dir |
| widerface_to_cocojson(args.widerface_root_dir) |
| else: |
| try: |
| assert os.path.exists(args.json_input_dir) |
| except AssertionError as e: |
| print('The json folder does not exist!') |
| os._exit(0) |
| try: |
| assert os.path.exists(args.image_input_dir) |
| except AssertionError as e: |
| print('The image folder does not exist!') |
| os._exit(0) |
| try: |
| assert abs(args.train_proportion + args.val_proportion \ |
| + args.test_proportion - 1.0) < 1e-5 |
| except AssertionError as e: |
| print( |
| 'The sum of pqoportion of training, validation and test datase must be 1!' |
| ) |
| os._exit(0) |
|
|
| |
| total_num = len(glob.glob(osp.join(args.json_input_dir, '*.json'))) |
| if args.train_proportion != 0: |
| train_num = int(total_num * args.train_proportion) |
| out_dir = args.output_dir + '/train' |
| if not os.path.exists(out_dir): |
| os.makedirs(out_dir) |
| else: |
| train_num = 0 |
| if args.val_proportion == 0.0: |
| val_num = 0 |
| test_num = total_num - train_num |
| out_dir = args.output_dir + '/test' |
| if args.test_proportion != 0.0 and not os.path.exists(out_dir): |
| os.makedirs(out_dir) |
| else: |
| val_num = int(total_num * args.val_proportion) |
| test_num = total_num - train_num - val_num |
| val_out_dir = args.output_dir + '/val' |
| if not os.path.exists(val_out_dir): |
| os.makedirs(val_out_dir) |
| test_out_dir = args.output_dir + '/test' |
| if args.test_proportion != 0.0 and not os.path.exists(test_out_dir): |
| os.makedirs(test_out_dir) |
| count = 1 |
| for img_name in os.listdir(args.image_input_dir): |
| if count <= train_num: |
| if osp.exists(args.output_dir + '/train/'): |
| shutil.copyfile( |
| osp.join(args.image_input_dir, img_name), |
| osp.join(args.output_dir + '/train/', img_name)) |
| else: |
| if count <= train_num + val_num: |
| if osp.exists(args.output_dir + '/val/'): |
| shutil.copyfile( |
| osp.join(args.image_input_dir, img_name), |
| osp.join(args.output_dir + '/val/', img_name)) |
| else: |
| if osp.exists(args.output_dir + '/test/'): |
| shutil.copyfile( |
| osp.join(args.image_input_dir, img_name), |
| osp.join(args.output_dir + '/test/', img_name)) |
| count = count + 1 |
|
|
| |
| if not os.path.exists(args.output_dir + '/annotations'): |
| os.makedirs(args.output_dir + '/annotations') |
| if args.train_proportion != 0: |
| train_data_coco = deal_json(args.dataset_type, |
| args.output_dir + '/train', |
| args.json_input_dir) |
| train_json_path = osp.join(args.output_dir + '/annotations', |
| 'instance_train.json') |
| json.dump( |
| train_data_coco, |
| open(train_json_path, 'w'), |
| indent=4, |
| cls=MyEncoder) |
| if args.val_proportion != 0: |
| val_data_coco = deal_json(args.dataset_type, |
| args.output_dir + '/val', |
| args.json_input_dir) |
| val_json_path = osp.join(args.output_dir + '/annotations', |
| 'instance_val.json') |
| json.dump( |
| val_data_coco, |
| open(val_json_path, 'w'), |
| indent=4, |
| cls=MyEncoder) |
| if args.test_proportion != 0: |
| test_data_coco = deal_json(args.dataset_type, |
| args.output_dir + '/test', |
| args.json_input_dir) |
| test_json_path = osp.join(args.output_dir + '/annotations', |
| 'instance_test.json') |
| json.dump( |
| test_data_coco, |
| open(test_json_path, 'w'), |
| indent=4, |
| cls=MyEncoder) |
|
|
|
|
| if __name__ == '__main__': |
| main() |
|
|