123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- # Copyright (c) OpenMMLab. All rights reserved.
- import json
- import os
- import re
- import time
- import warnings
- import cv2
- import numpy as np
- import xmltodict
- from xtcocotools.coco import COCO
- np.random.seed(0)
- def list_all_files(root_dir, ext='.xml'):
- """List all files in the root directory and all its sub directories.
- :param root_dir: root directory
- :param ext: filename extension
- :return: list of files
- """
- files = []
- file_list = os.listdir(root_dir)
- for i in range(0, len(file_list)):
- path = os.path.join(root_dir, file_list[i])
- if os.path.isdir(path):
- files.extend(list_all_files(path))
- if os.path.isfile(path):
- if path.lower().endswith(ext):
- files.append(path)
- return files
- def get_anno_info():
- keypoints_info = [
- 'L_Eye',
- 'R_Eye',
- 'L_EarBase',
- 'R_EarBase',
- 'Nose',
- 'Throat',
- 'TailBase',
- 'Withers',
- 'L_F_Elbow',
- 'R_F_Elbow',
- 'L_B_Elbow',
- 'R_B_Elbow',
- 'L_F_Knee',
- 'R_F_Knee',
- 'L_B_Knee',
- 'R_B_Knee',
- 'L_F_Paw',
- 'R_F_Paw',
- 'L_B_Paw',
- 'R_B_Paw',
- ]
- skeleton_info = [[1, 2], [1, 3], [2, 4], [1, 5], [2, 5], [5, 6], [6, 8],
- [7, 8], [6, 9], [9, 13], [13, 17], [6, 10], [10, 14],
- [14, 18], [7, 11], [11, 15], [15, 19], [7, 12], [12, 16],
- [16, 20]]
- category_info = [{
- 'supercategory': 'animal',
- 'id': 1,
- 'name': 'animal',
- 'keypoints': keypoints_info,
- 'skeleton': skeleton_info
- }]
- return keypoints_info, skeleton_info, category_info
- def xml2coco_trainval(file_list, img_root, save_path, start_ann_id=0):
- """Save annotations in coco-format.
- :param file_list: list of data annotation files.
- :param img_root: the root dir to load images.
- :param save_path: the path to save transformed annotation file.
- :param start_ann_id: the starting point to count the annotation id.
- :param val_num: the number of annotated objects for validation.
- """
- images = []
- annotations = []
- img_ids = []
- ann_ids = []
- ann_id = start_ann_id
- name2id = {
- 'L_Eye': 0,
- 'R_Eye': 1,
- 'L_EarBase': 2,
- 'R_EarBase': 3,
- 'Nose': 4,
- 'Throat': 5,
- 'TailBase': 6,
- 'Withers': 7,
- 'L_F_Elbow': 8,
- 'R_F_Elbow': 9,
- 'L_B_Elbow': 10,
- 'R_B_Elbow': 11,
- 'L_F_Knee': 12,
- 'R_F_Knee': 13,
- 'L_B_Knee': 14,
- 'R_B_Knee': 15,
- 'L_F_Paw': 16,
- 'R_F_Paw': 17,
- 'L_B_Paw': 18,
- 'R_B_Paw': 19
- }
- for file in file_list:
- data_anno = xmltodict.parse(open(file).read())['annotation']
- img_id = int(data_anno['image'].split('_')[0] +
- data_anno['image'].split('_')[1])
- if img_id not in img_ids:
- image_name = 'VOC2012/JPEGImages/' + data_anno['image'] + '.jpg'
- img = cv2.imread(os.path.join(img_root, image_name))
- image = {}
- image['id'] = img_id
- image['file_name'] = image_name
- image['height'] = img.shape[0]
- image['width'] = img.shape[1]
- images.append(image)
- img_ids.append(img_id)
- else:
- pass
- keypoint_anno = data_anno['keypoints']['keypoint']
- assert len(keypoint_anno) == 20
- keypoints = np.zeros([20, 3], dtype=np.float32)
- for kpt_anno in keypoint_anno:
- keypoint_name = kpt_anno['@name']
- keypoint_id = name2id[keypoint_name]
- visibility = int(kpt_anno['@visible'])
- if visibility == 0:
- continue
- else:
- keypoints[keypoint_id, 0] = float(kpt_anno['@x'])
- keypoints[keypoint_id, 1] = float(kpt_anno['@y'])
- keypoints[keypoint_id, 2] = 2
- anno = {}
- anno['keypoints'] = keypoints.reshape(-1).tolist()
- anno['image_id'] = img_id
- anno['id'] = ann_id
- anno['num_keypoints'] = int(sum(keypoints[:, 2] > 0))
- visible_bounds = data_anno['visible_bounds']
- anno['bbox'] = [
- float(visible_bounds['@xmin']),
- float(visible_bounds['@ymin']),
- float(visible_bounds['@width']),
- float(visible_bounds['@height'])
- ]
- anno['iscrowd'] = 0
- anno['area'] = float(anno['bbox'][2] * anno['bbox'][3])
- anno['category_id'] = 1
- annotations.append(anno)
- ann_ids.append(ann_id)
- ann_id += 1
- cocotype = {}
- cocotype['info'] = {}
- cocotype['info'][
- 'description'] = 'AnimalPose dataset Generated by MMPose Team'
- cocotype['info']['version'] = '1.0'
- cocotype['info']['year'] = time.strftime('%Y', time.localtime())
- cocotype['info']['date_created'] = time.strftime('%Y/%m/%d',
- time.localtime())
- cocotype['images'] = images
- cocotype['annotations'] = annotations
- keypoints_info, skeleton_info, category_info = get_anno_info()
- cocotype['categories'] = category_info
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
- json.dump(cocotype, open(save_path, 'w'), indent=4)
- print('number of images:', len(img_ids))
- print('number of annotations:', len(ann_ids))
- print(f'done {save_path}')
- def xml2coco_test(file_list, img_root, save_path, start_ann_id=0):
- """Save annotations in coco-format.
- :param file_list: list of data annotation files.
- :param img_root: the root dir to load images.
- :param save_path: the path to save transformed annotation file.
- :param start_ann_id: the starting point to count the annotation id.
- """
- images = []
- annotations = []
- img_ids = []
- ann_ids = []
- ann_id = start_ann_id
- name2id = {
- 'L_eye': 0,
- 'R_eye': 1,
- 'L_ear': 2,
- 'R_ear': 3,
- 'Nose': 4,
- 'Throat': 5,
- 'Tail': 6,
- 'withers': 7,
- 'L_F_elbow': 8,
- 'R_F_elbow': 9,
- 'L_B_elbow': 10,
- 'R_B_elbow': 11,
- 'L_F_knee': 12,
- 'R_F_knee': 13,
- 'L_B_knee': 14,
- 'R_B_knee': 15,
- 'L_F_paw': 16,
- 'R_F_paw': 17,
- 'L_B_paw': 18,
- 'R_B_paw': 19
- }
- cat2id = {'cat': 1, 'cow': 2, 'dog': 3, 'horse': 4, 'sheep': 5}
- for file in file_list:
- data_anno = xmltodict.parse(open(file).read())['annotation']
- category_id = cat2id[data_anno['category']]
- img_id = category_id * 1000 + int(
- re.findall(r'\d+', data_anno['image'])[0])
- assert img_id not in img_ids
- # prepare images
- image_name = os.path.join('animalpose_image_part2',
- data_anno['category'], data_anno['image'])
- img = cv2.imread(os.path.join(img_root, image_name))
- image = {}
- image['id'] = img_id
- image['file_name'] = image_name
- image['height'] = img.shape[0]
- image['width'] = img.shape[1]
- images.append(image)
- img_ids.append(img_id)
- # prepare annotations
- keypoint_anno = data_anno['keypoints']['keypoint']
- keypoints = np.zeros([20, 3], dtype=np.float32)
- for kpt_anno in keypoint_anno:
- keypoint_name = kpt_anno['@name']
- keypoint_id = name2id[keypoint_name]
- visibility = int(kpt_anno['@visible'])
- if visibility == 0:
- continue
- else:
- keypoints[keypoint_id, 0] = float(kpt_anno['@x'])
- keypoints[keypoint_id, 1] = float(kpt_anno['@y'])
- keypoints[keypoint_id, 2] = 2
- anno = {}
- anno['keypoints'] = keypoints.reshape(-1).tolist()
- anno['image_id'] = img_id
- anno['id'] = ann_id
- anno['num_keypoints'] = int(sum(keypoints[:, 2] > 0))
- visible_bounds = data_anno['visible_bounds']
- anno['bbox'] = [
- float(visible_bounds['@xmin']),
- float(visible_bounds['@xmax']
- ), # typo in original xml: should be 'ymin'
- float(visible_bounds['@width']),
- float(visible_bounds['@height'])
- ]
- anno['iscrowd'] = 0
- anno['area'] = float(anno['bbox'][2] * anno['bbox'][3])
- anno['category_id'] = 1
- annotations.append(anno)
- ann_ids.append(ann_id)
- ann_id += 1
- cocotype = {}
- cocotype['info'] = {}
- cocotype['info'][
- 'description'] = 'AnimalPose dataset Generated by MMPose Team'
- cocotype['info']['version'] = '1.0'
- cocotype['info']['year'] = time.strftime('%Y', time.localtime())
- cocotype['info']['date_created'] = time.strftime('%Y/%m/%d',
- time.localtime())
- cocotype['images'] = images
- cocotype['annotations'] = annotations
- keypoints_info, skeleton_info, category_info = get_anno_info()
- cocotype['categories'] = category_info
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
- json.dump(cocotype, open(save_path, 'w'), indent=4)
- print('=========================================================')
- print('number of images:', len(img_ids))
- print('number of annotations:', len(ann_ids))
- print(f'done {save_path}')
- def split_train_val(work_dir, trainval_file, train_file, val_file,
- val_ann_num):
- """Split train-val json file into training and validation files.
- :param work_dir: path to load train-val json file, and save split files.
- :param trainval_file: The input json file combining both train and val.
- :param trainval_file: The output json file for training.
- :param trainval_file: The output json file for validation.
- :param val_ann_num: the number of validation annotations.
- """
- coco = COCO(os.path.join(work_dir, trainval_file))
- img_list = list(coco.imgs.keys())
- np.random.shuffle(img_list)
- count = 0
- images_train = []
- images_val = []
- annotations_train = []
- annotations_val = []
- for img_id in img_list:
- ann_ids = coco.getAnnIds(img_id)
- if count + len(ann_ids) <= val_ann_num:
- # for validation
- count += len(ann_ids)
- images_val.append(coco.imgs[img_id])
- for ann_id in ann_ids:
- annotations_val.append(coco.anns[ann_id])
- else:
- images_train.append(coco.imgs[img_id])
- for ann_id in ann_ids:
- annotations_train.append(coco.anns[ann_id])
- if count == val_ann_num:
- print(f'We have found {count} annotations for validation.')
- else:
- warnings.warn(
- f'We only found {count} annotations, instead of {val_ann_num}.')
- cocotype_train = {}
- cocotype_val = {}
- keypoints_info, skeleton_info, category_info = get_anno_info()
- cocotype_train['info'] = {}
- cocotype_train['info'][
- 'description'] = 'AnimalPose dataset Generated by MMPose Team'
- cocotype_train['info']['version'] = '1.0'
- cocotype_train['info']['year'] = time.strftime('%Y', time.localtime())
- cocotype_train['info']['date_created'] = time.strftime(
- '%Y/%m/%d', time.localtime())
- cocotype_train['images'] = images_train
- cocotype_train['annotations'] = annotations_train
- cocotype_train['categories'] = category_info
- json.dump(
- cocotype_train,
- open(os.path.join(work_dir, train_file), 'w'),
- indent=4)
- print('=========================================================')
- print('number of images:', len(images_train))
- print('number of annotations:', len(annotations_train))
- print(f'done {train_file}')
- cocotype_val['info'] = {}
- cocotype_val['info'][
- 'description'] = 'AnimalPose dataset Generated by MMPose Team'
- cocotype_val['info']['version'] = '1.0'
- cocotype_val['info']['year'] = time.strftime('%Y', time.localtime())
- cocotype_val['info']['date_created'] = time.strftime(
- '%Y/%m/%d', time.localtime())
- cocotype_val['images'] = images_val
- cocotype_val['annotations'] = annotations_val
- cocotype_val['categories'] = category_info
- json.dump(
- cocotype_val, open(os.path.join(work_dir, val_file), 'w'), indent=4)
- print('=========================================================')
- print('number of images:', len(images_val))
- print('number of annotations:', len(annotations_val))
- print(f'done {val_file}')
- dataset_dir = 'data/animalpose/'
- # We choose the images from PascalVOC for train + val
- # In total, train+val: 3608 images, 5117 annotations
- xml2coco_trainval(
- list_all_files(os.path.join(dataset_dir, 'PASCAL2011_animal_annotation')),
- dataset_dir,
- os.path.join(dataset_dir, 'annotations', 'animalpose_trainval.json'),
- start_ann_id=1000000)
- # train: 2798 images, 4000 annotations
- # val: 810 images, 1117 annotations
- split_train_val(
- os.path.join(dataset_dir, 'annotations'),
- 'animalpose_trainval.json',
- 'animalpose_train.json',
- 'animalpose_val.json',
- val_ann_num=1117)
- # We choose the remaining 1000 images for test
- # 1000 images, 1000 annotations
- xml2coco_test(
- list_all_files(os.path.join(dataset_dir, 'animalpose_anno2')),
- dataset_dir,
- os.path.join(dataset_dir, 'annotations', 'animalpose_test.json'),
- start_ann_id=0)
|