123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- import argparse
- import json
- import os
- from pathlib import Path
- import numpy as np
- import pycocotools.mask as mask_util
- from mmengine.utils import ProgressBar, mkdir_or_exist
- from panopticapi.utils import IdGenerator, save_json
- from PIL import Image
- from mmdet.datasets.ade20k import ADE20KPanopticDataset
- ORIGINAL_CATEGORIES = [
- 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road, route',
- 'bed', 'window', 'grass', 'cabinet', 'sidewalk, pavement', 'person',
- 'earth, ground', 'door', 'table', 'mountain, mount', 'plant', 'curtain',
- 'chair', 'car', 'water', 'painting, picture', 'sofa', 'shelf', 'house',
- 'sea', 'mirror', 'rug', 'field', 'armchair', 'seat', 'fence', 'desk',
- 'rock, stone', 'wardrobe, closet, press', 'lamp', 'tub', 'rail', 'cushion',
- 'base, pedestal, stand', 'box', 'column, pillar', 'signboard, sign',
- 'chest of drawers, chest, bureau, dresser', 'counter', 'sand', 'sink',
- 'skyscraper', 'fireplace', 'refrigerator, icebox',
- 'grandstand, covered stand', 'path', 'stairs', 'runway',
- 'case, display case, showcase, vitrine',
- 'pool table, billiard table, snooker table', 'pillow',
- 'screen door, screen', 'stairway, staircase', 'river', 'bridge, span',
- 'bookcase', 'blind, screen', 'coffee table',
- 'toilet, can, commode, crapper, pot, potty, stool, throne', 'flower',
- 'book', 'hill', 'bench', 'countertop', 'stove', 'palm, palm tree',
- 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar',
- 'arcade machine', 'hovel, hut, hutch, shack, shanty', 'bus', 'towel',
- 'light', 'truck', 'tower', 'chandelier', 'awning, sunshade, sunblind',
- 'street lamp', 'booth', 'tv', 'airplane', 'dirt track', 'clothes', 'pole',
- 'land, ground, soil',
- 'bannister, banister, balustrade, balusters, handrail',
- 'escalator, moving staircase, moving stairway',
- 'ottoman, pouf, pouffe, puff, hassock', 'bottle',
- 'buffet, counter, sideboard',
- 'poster, posting, placard, notice, bill, card', 'stage', 'van', 'ship',
- 'fountain',
- 'conveyer belt, conveyor belt, conveyer, conveyor, transporter', 'canopy',
- 'washer, automatic washer, washing machine', 'plaything, toy', 'pool',
- 'stool', 'barrel, cask', 'basket, handbasket', 'falls', 'tent', 'bag',
- 'minibike, motorbike', 'cradle', 'oven', 'ball', 'food, solid food',
- 'step, stair', 'tank, storage tank', 'trade name', 'microwave', 'pot',
- 'animal', 'bicycle', 'lake', 'dishwasher', 'screen', 'blanket, cover',
- 'sculpture', 'hood, exhaust hood', 'sconce', 'vase', 'traffic light',
- 'tray', 'trash can', 'fan', 'pier', 'crt screen', 'plate', 'monitor',
- 'bulletin board', 'shower', 'radiator', 'glass, drinking glass', 'clock',
- 'flag'
- ]
- def parse_args():
- parser = argparse.ArgumentParser(
- description='Convert ADE20K annotations to COCO format')
- parser.add_argument('src', help='ade20k data path')
- parser.add_argument('--task', help='task name', default='panoptic')
- args = parser.parse_args()
- return args
- def prepare_instance_annotations(dataset_dir: str):
- dataset_dir = Path(dataset_dir)
- for name, dirname in [('train', 'training'), ('val', 'validation')]:
- image_dir = dataset_dir / 'images' / dirname
- instance_dir = dataset_dir / 'annotations_instance' / dirname
- ann_id = 0
- # json
- out_file = dataset_dir / f'ade20k_instance_{name}.json'
- # json config
- instance_config_file = dataset_dir / 'imgCatIds.json'
- with open(instance_config_file, 'r') as f:
- category_dict = json.load(f)['categories']
- # catid mapping
- mapping_file = dataset_dir / 'categoryMapping.txt'
- with open(mapping_file, 'r') as f:
- map_id = {}
- for i, line in enumerate(f.readlines()):
- if i == 0:
- continue
- ins_id, sem_id, _ = line.strip().split()
- map_id[int(ins_id)] = int(sem_id) - 1
- for cat in category_dict:
- cat['id'] = map_id[cat['id']]
- filenames = sorted(list(image_dir.iterdir()))
- ann_dict = {}
- images = []
- annotations = []
- progressbar = ProgressBar(len(filenames))
- for filename in filenames:
- image = {}
- image_id = filename.stem
- image['id'] = image_id
- image['file_name'] = filename.name
- original_format = np.array(Image.open(filename))
- image['height'] = original_format.shape[0]
- image['width'] = original_format.shape[1]
- images.append(image)
- instance_file = instance_dir / f'{image_id}.png'
- ins_seg = np.array(Image.open(instance_file))
- assert ins_seg.dtype == np.uint8
- instance_cat_ids = ins_seg[..., 0]
- instance_ins_ids = ins_seg[..., 1]
- for thing_id in np.unique(instance_ins_ids):
- if thing_id == 0:
- continue
- mask = instance_ins_ids == thing_id
- instance_cat_id = np.unique(instance_cat_ids[mask])
- assert len(instance_cat_id) == 1
- anno = {}
- anno['id'] = ann_id
- ann_id += 1
- anno['image_id'] = image['id']
- anno['iscrowd'] = int(0)
- anno['category_id'] = int(map_id[instance_cat_id[0]])
- inds = np.nonzero(mask)
- ymin, ymax = inds[0].min(), inds[0].max()
- xmin, xmax = inds[1].min(), inds[1].max()
- anno['bbox'] = [
- int(xmin),
- int(ymin),
- int(xmax - xmin + 1),
- int(ymax - ymin + 1)
- ]
- rle = mask_util.encode(
- np.array(mask[:, :, np.newaxis], order='F',
- dtype='uint8'))[0]
- rle['counts'] = rle['counts'].decode('utf-8')
- anno['segmentation'] = rle
- anno['area'] = int(mask_util.area(rle))
- annotations.append(anno)
- progressbar.update()
- ann_dict['images'] = images
- ann_dict['categories'] = category_dict
- ann_dict['annotations'] = annotations
- save_json(ann_dict, out_file)
- def prepare_panoptic_annotations(dataset_dir: str):
- dataset_dir = Path(dataset_dir)
- for name, dirname in [('train', 'training'), ('val', 'validation')]:
- image_dir = dataset_dir / 'images' / dirname
- semantic_dir = dataset_dir / 'annotations' / dirname
- instance_dir = dataset_dir / 'annotations_instance' / dirname
- # folder to store panoptic PNGs
- out_folder = dataset_dir / f'ade20k_panoptic_{name}'
- # json with segmentations information
- out_file = dataset_dir / f'ade20k_panoptic_{name}.json'
- mkdir_or_exist(out_folder)
- # catid mapping
- neworder_categories = []
- all_classes = ORIGINAL_CATEGORIES
- thing_classes = ADE20KPanopticDataset.METAINFO['thing_classes']
- stuff_classes = ADE20KPanopticDataset.METAINFO['stuff_classes']
- palette = ADE20KPanopticDataset.METAINFO['palette']
- old_2_new_mapping = {}
- new_2_old_mapping = {}
- for i, t in enumerate(thing_classes):
- j = list(all_classes).index(t)
- old_2_new_mapping[j] = i
- new_2_old_mapping[i] = j
- for i, t in enumerate(stuff_classes):
- j = list(all_classes).index(t)
- old_2_new_mapping[j] = i + len(thing_classes)
- new_2_old_mapping[i + len(thing_classes)] = j
- for old, new in old_2_new_mapping.items():
- neworder_categories.append({
- 'id': new,
- 'name': all_classes[old],
- 'isthing': int(new < len(thing_classes)),
- 'color': palette[new]
- })
- categories_dict = {cat['id']: cat for cat in neworder_categories}
- panoptic_json_categories = neworder_categories[:]
- panoptic_json_images = []
- panoptic_json_annotations = []
- filenames = sorted(list(image_dir.iterdir()))
- progressbar = ProgressBar(len(filenames))
- for filename in filenames:
- panoptic_json_image = {}
- image_id = filename.stem
- panoptic_json_image['id'] = image_id
- panoptic_json_image['file_name'] = filename.name
- original_format = np.array(Image.open(filename))
- panoptic_json_image['height'] = original_format.shape[0]
- panoptic_json_image['width'] = original_format.shape[1]
- pan_seg = np.zeros(
- (original_format.shape[0], original_format.shape[1], 3),
- dtype=np.uint8)
- id_generator = IdGenerator(categories_dict)
- filename_semantic = semantic_dir / f'{image_id}.png'
- filename_instance = instance_dir / f'{image_id}.png'
- sem_seg = np.array(Image.open(filename_semantic))
- ins_seg = np.array(Image.open(filename_instance))
- assert sem_seg.dtype == np.uint8
- assert ins_seg.dtype == np.uint8
- semantic_cat_ids = sem_seg - 1
- instance_cat_ids = ins_seg[..., 0] - 1
- # instance id starts from 1!
- # because 0 is reserved as VOID label
- instance_ins_ids = ins_seg[..., 1]
- segm_info = []
- # process stuffs
- for semantic_cat_id in np.unique(semantic_cat_ids):
- if semantic_cat_id == 255:
- continue
- if categories_dict[old_2_new_mapping[int(
- semantic_cat_id)]]['isthing'] == 1:
- continue
- mask = semantic_cat_ids == semantic_cat_id
- # should not have any overlap
- assert pan_seg[mask].sum() == 0
- segment_id, color = id_generator.get_id_and_color(
- old_2_new_mapping[int(semantic_cat_id)])
- pan_seg[mask] = color
- area = np.sum(mask)
- # bbox computation for a segment
- hor = np.sum(mask, axis=0)
- hor_idx = np.nonzero(hor)[0]
- x = hor_idx[0]
- width = hor_idx[-1] - x + 1
- vert = np.sum(mask, axis=1)
- vert_idx = np.nonzero(vert)[0]
- y = vert_idx[0]
- height = vert_idx[-1] - y + 1
- bbox = [int(x), int(y), int(width), int(height)]
- segm_info.append({
- 'id':
- int(segment_id),
- 'category_id':
- old_2_new_mapping[int(semantic_cat_id)],
- 'area':
- int(area),
- 'bbox':
- bbox,
- 'iscrowd':
- 0
- })
- # process things
- for thing_id in np.unique(instance_ins_ids):
- if thing_id == 0:
- continue
- mask = instance_ins_ids == thing_id
- instance_cat_id = np.unique(instance_cat_ids[mask])
- assert len(instance_cat_id) == 1
- segment_id, color = id_generator.get_id_and_color(
- instance_cat_id[0])
- pan_seg[mask] = color
- area = np.sum(mask)
- # bbox computation for a segment
- hor = np.sum(mask, axis=0)
- hor_idx = np.nonzero(hor)[0]
- x = hor_idx[-1] - x + 1
- width = hor_idx[-1] - x + 1
- vert = np.sum(mask, axis=1)
- vert_idx = np.nonzero(vert)[0]
- y = vert_idx[0]
- height = vert_idx[-1] - y + 1
- bbox = [int(x), int(y), int(width), int(height)]
- segm_info.append({
- 'id': int(segment_id),
- 'category_id': int(instance_cat_id[0]),
- 'area': int(area),
- 'bbox': bbox,
- 'iscrowd': 0
- })
- panoptic_json_annotation = {
- 'image_id': image_id,
- 'file_name': image_id + '.png',
- 'segments_info': segm_info
- }
- Image.fromarray(pan_seg).save(out_folder / f'{image_id}.png')
- panoptic_json_images.append(panoptic_json_image)
- panoptic_json_annotations.append(panoptic_json_annotation)
- progressbar.update()
- panoptic_json = {
- 'images': panoptic_json_images,
- 'annotations': panoptic_json_annotations,
- 'categories': panoptic_json_categories
- }
- save_json(panoptic_json, out_file)
- def main():
- args = parse_args()
- assert args.task in ['panoptic', 'instance']
- src = args.src
- if args.task == 'panoptic':
- annotation_train_path = f'{src}/ade20k_panoptic_train'
- annotation_val_path = f'{src}/ade20k_panoptic_val'
- print('Preparing ADE20K panoptic annotations ...')
- print(
- f'Creating panoptic annotations to {annotation_train_path} and {annotation_val_path} ...' # noqa
- )
- if os.path.exists(annotation_train_path) or os.path.exists(
- annotation_val_path):
- raise RuntimeError('Panoptic annotations already exist.')
- prepare_panoptic_annotations(src)
- print('Done.')
- else:
- annotation_train_path = f'{src}/ade20k_instance_train'
- annotation_val_path = f'{src}/ade20k_instance_val'
- print('Preparing ADE20K instance annotations ...')
- print(
- f'Creating instance annotations to {annotation_train_path} and {annotation_val_path} ...' # noqa
- )
- if os.path.exists(annotation_train_path) or os.path.exists(
- annotation_val_path):
- raise RuntimeError('Instance annotations already exist.')
- prepare_instance_annotations(src)
- print('Done.')
- if __name__ == '__main__':
- main()
|