ade20k2coco.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. import argparse
  2. import json
  3. import os
  4. from pathlib import Path
  5. import numpy as np
  6. import pycocotools.mask as mask_util
  7. from mmengine.utils import ProgressBar, mkdir_or_exist
  8. from panopticapi.utils import IdGenerator, save_json
  9. from PIL import Image
  10. from mmdet.datasets.ade20k import ADE20KPanopticDataset
  11. ORIGINAL_CATEGORIES = [
  12. 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road, route',
  13. 'bed', 'window', 'grass', 'cabinet', 'sidewalk, pavement', 'person',
  14. 'earth, ground', 'door', 'table', 'mountain, mount', 'plant', 'curtain',
  15. 'chair', 'car', 'water', 'painting, picture', 'sofa', 'shelf', 'house',
  16. 'sea', 'mirror', 'rug', 'field', 'armchair', 'seat', 'fence', 'desk',
  17. 'rock, stone', 'wardrobe, closet, press', 'lamp', 'tub', 'rail', 'cushion',
  18. 'base, pedestal, stand', 'box', 'column, pillar', 'signboard, sign',
  19. 'chest of drawers, chest, bureau, dresser', 'counter', 'sand', 'sink',
  20. 'skyscraper', 'fireplace', 'refrigerator, icebox',
  21. 'grandstand, covered stand', 'path', 'stairs', 'runway',
  22. 'case, display case, showcase, vitrine',
  23. 'pool table, billiard table, snooker table', 'pillow',
  24. 'screen door, screen', 'stairway, staircase', 'river', 'bridge, span',
  25. 'bookcase', 'blind, screen', 'coffee table',
  26. 'toilet, can, commode, crapper, pot, potty, stool, throne', 'flower',
  27. 'book', 'hill', 'bench', 'countertop', 'stove', 'palm, palm tree',
  28. 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar',
  29. 'arcade machine', 'hovel, hut, hutch, shack, shanty', 'bus', 'towel',
  30. 'light', 'truck', 'tower', 'chandelier', 'awning, sunshade, sunblind',
  31. 'street lamp', 'booth', 'tv', 'airplane', 'dirt track', 'clothes', 'pole',
  32. 'land, ground, soil',
  33. 'bannister, banister, balustrade, balusters, handrail',
  34. 'escalator, moving staircase, moving stairway',
  35. 'ottoman, pouf, pouffe, puff, hassock', 'bottle',
  36. 'buffet, counter, sideboard',
  37. 'poster, posting, placard, notice, bill, card', 'stage', 'van', 'ship',
  38. 'fountain',
  39. 'conveyer belt, conveyor belt, conveyer, conveyor, transporter', 'canopy',
  40. 'washer, automatic washer, washing machine', 'plaything, toy', 'pool',
  41. 'stool', 'barrel, cask', 'basket, handbasket', 'falls', 'tent', 'bag',
  42. 'minibike, motorbike', 'cradle', 'oven', 'ball', 'food, solid food',
  43. 'step, stair', 'tank, storage tank', 'trade name', 'microwave', 'pot',
  44. 'animal', 'bicycle', 'lake', 'dishwasher', 'screen', 'blanket, cover',
  45. 'sculpture', 'hood, exhaust hood', 'sconce', 'vase', 'traffic light',
  46. 'tray', 'trash can', 'fan', 'pier', 'crt screen', 'plate', 'monitor',
  47. 'bulletin board', 'shower', 'radiator', 'glass, drinking glass', 'clock',
  48. 'flag'
  49. ]
  50. def parse_args():
  51. parser = argparse.ArgumentParser(
  52. description='Convert ADE20K annotations to COCO format')
  53. parser.add_argument('src', help='ade20k data path')
  54. parser.add_argument('--task', help='task name', default='panoptic')
  55. args = parser.parse_args()
  56. return args
  57. def prepare_instance_annotations(dataset_dir: str):
  58. dataset_dir = Path(dataset_dir)
  59. for name, dirname in [('train', 'training'), ('val', 'validation')]:
  60. image_dir = dataset_dir / 'images' / dirname
  61. instance_dir = dataset_dir / 'annotations_instance' / dirname
  62. ann_id = 0
  63. # json
  64. out_file = dataset_dir / f'ade20k_instance_{name}.json'
  65. # json config
  66. instance_config_file = dataset_dir / 'imgCatIds.json'
  67. with open(instance_config_file, 'r') as f:
  68. category_dict = json.load(f)['categories']
  69. # catid mapping
  70. mapping_file = dataset_dir / 'categoryMapping.txt'
  71. with open(mapping_file, 'r') as f:
  72. map_id = {}
  73. for i, line in enumerate(f.readlines()):
  74. if i == 0:
  75. continue
  76. ins_id, sem_id, _ = line.strip().split()
  77. map_id[int(ins_id)] = int(sem_id) - 1
  78. for cat in category_dict:
  79. cat['id'] = map_id[cat['id']]
  80. filenames = sorted(list(image_dir.iterdir()))
  81. ann_dict = {}
  82. images = []
  83. annotations = []
  84. progressbar = ProgressBar(len(filenames))
  85. for filename in filenames:
  86. image = {}
  87. image_id = filename.stem
  88. image['id'] = image_id
  89. image['file_name'] = filename.name
  90. original_format = np.array(Image.open(filename))
  91. image['height'] = original_format.shape[0]
  92. image['width'] = original_format.shape[1]
  93. images.append(image)
  94. instance_file = instance_dir / f'{image_id}.png'
  95. ins_seg = np.array(Image.open(instance_file))
  96. assert ins_seg.dtype == np.uint8
  97. instance_cat_ids = ins_seg[..., 0]
  98. instance_ins_ids = ins_seg[..., 1]
  99. for thing_id in np.unique(instance_ins_ids):
  100. if thing_id == 0:
  101. continue
  102. mask = instance_ins_ids == thing_id
  103. instance_cat_id = np.unique(instance_cat_ids[mask])
  104. assert len(instance_cat_id) == 1
  105. anno = {}
  106. anno['id'] = ann_id
  107. ann_id += 1
  108. anno['image_id'] = image['id']
  109. anno['iscrowd'] = int(0)
  110. anno['category_id'] = int(map_id[instance_cat_id[0]])
  111. inds = np.nonzero(mask)
  112. ymin, ymax = inds[0].min(), inds[0].max()
  113. xmin, xmax = inds[1].min(), inds[1].max()
  114. anno['bbox'] = [
  115. int(xmin),
  116. int(ymin),
  117. int(xmax - xmin + 1),
  118. int(ymax - ymin + 1)
  119. ]
  120. rle = mask_util.encode(
  121. np.array(mask[:, :, np.newaxis], order='F',
  122. dtype='uint8'))[0]
  123. rle['counts'] = rle['counts'].decode('utf-8')
  124. anno['segmentation'] = rle
  125. anno['area'] = int(mask_util.area(rle))
  126. annotations.append(anno)
  127. progressbar.update()
  128. ann_dict['images'] = images
  129. ann_dict['categories'] = category_dict
  130. ann_dict['annotations'] = annotations
  131. save_json(ann_dict, out_file)
  132. def prepare_panoptic_annotations(dataset_dir: str):
  133. dataset_dir = Path(dataset_dir)
  134. for name, dirname in [('train', 'training'), ('val', 'validation')]:
  135. image_dir = dataset_dir / 'images' / dirname
  136. semantic_dir = dataset_dir / 'annotations' / dirname
  137. instance_dir = dataset_dir / 'annotations_instance' / dirname
  138. # folder to store panoptic PNGs
  139. out_folder = dataset_dir / f'ade20k_panoptic_{name}'
  140. # json with segmentations information
  141. out_file = dataset_dir / f'ade20k_panoptic_{name}.json'
  142. mkdir_or_exist(out_folder)
  143. # catid mapping
  144. neworder_categories = []
  145. all_classes = ORIGINAL_CATEGORIES
  146. thing_classes = ADE20KPanopticDataset.METAINFO['thing_classes']
  147. stuff_classes = ADE20KPanopticDataset.METAINFO['stuff_classes']
  148. palette = ADE20KPanopticDataset.METAINFO['palette']
  149. old_2_new_mapping = {}
  150. new_2_old_mapping = {}
  151. for i, t in enumerate(thing_classes):
  152. j = list(all_classes).index(t)
  153. old_2_new_mapping[j] = i
  154. new_2_old_mapping[i] = j
  155. for i, t in enumerate(stuff_classes):
  156. j = list(all_classes).index(t)
  157. old_2_new_mapping[j] = i + len(thing_classes)
  158. new_2_old_mapping[i + len(thing_classes)] = j
  159. for old, new in old_2_new_mapping.items():
  160. neworder_categories.append({
  161. 'id': new,
  162. 'name': all_classes[old],
  163. 'isthing': int(new < len(thing_classes)),
  164. 'color': palette[new]
  165. })
  166. categories_dict = {cat['id']: cat for cat in neworder_categories}
  167. panoptic_json_categories = neworder_categories[:]
  168. panoptic_json_images = []
  169. panoptic_json_annotations = []
  170. filenames = sorted(list(image_dir.iterdir()))
  171. progressbar = ProgressBar(len(filenames))
  172. for filename in filenames:
  173. panoptic_json_image = {}
  174. image_id = filename.stem
  175. panoptic_json_image['id'] = image_id
  176. panoptic_json_image['file_name'] = filename.name
  177. original_format = np.array(Image.open(filename))
  178. panoptic_json_image['height'] = original_format.shape[0]
  179. panoptic_json_image['width'] = original_format.shape[1]
  180. pan_seg = np.zeros(
  181. (original_format.shape[0], original_format.shape[1], 3),
  182. dtype=np.uint8)
  183. id_generator = IdGenerator(categories_dict)
  184. filename_semantic = semantic_dir / f'{image_id}.png'
  185. filename_instance = instance_dir / f'{image_id}.png'
  186. sem_seg = np.array(Image.open(filename_semantic))
  187. ins_seg = np.array(Image.open(filename_instance))
  188. assert sem_seg.dtype == np.uint8
  189. assert ins_seg.dtype == np.uint8
  190. semantic_cat_ids = sem_seg - 1
  191. instance_cat_ids = ins_seg[..., 0] - 1
  192. # instance id starts from 1!
  193. # because 0 is reserved as VOID label
  194. instance_ins_ids = ins_seg[..., 1]
  195. segm_info = []
  196. # process stuffs
  197. for semantic_cat_id in np.unique(semantic_cat_ids):
  198. if semantic_cat_id == 255:
  199. continue
  200. if categories_dict[old_2_new_mapping[int(
  201. semantic_cat_id)]]['isthing'] == 1:
  202. continue
  203. mask = semantic_cat_ids == semantic_cat_id
  204. # should not have any overlap
  205. assert pan_seg[mask].sum() == 0
  206. segment_id, color = id_generator.get_id_and_color(
  207. old_2_new_mapping[int(semantic_cat_id)])
  208. pan_seg[mask] = color
  209. area = np.sum(mask)
  210. # bbox computation for a segment
  211. hor = np.sum(mask, axis=0)
  212. hor_idx = np.nonzero(hor)[0]
  213. x = hor_idx[0]
  214. width = hor_idx[-1] - x + 1
  215. vert = np.sum(mask, axis=1)
  216. vert_idx = np.nonzero(vert)[0]
  217. y = vert_idx[0]
  218. height = vert_idx[-1] - y + 1
  219. bbox = [int(x), int(y), int(width), int(height)]
  220. segm_info.append({
  221. 'id':
  222. int(segment_id),
  223. 'category_id':
  224. old_2_new_mapping[int(semantic_cat_id)],
  225. 'area':
  226. int(area),
  227. 'bbox':
  228. bbox,
  229. 'iscrowd':
  230. 0
  231. })
  232. # process things
  233. for thing_id in np.unique(instance_ins_ids):
  234. if thing_id == 0:
  235. continue
  236. mask = instance_ins_ids == thing_id
  237. instance_cat_id = np.unique(instance_cat_ids[mask])
  238. assert len(instance_cat_id) == 1
  239. segment_id, color = id_generator.get_id_and_color(
  240. instance_cat_id[0])
  241. pan_seg[mask] = color
  242. area = np.sum(mask)
  243. # bbox computation for a segment
  244. hor = np.sum(mask, axis=0)
  245. hor_idx = np.nonzero(hor)[0]
  246. x = hor_idx[-1] - x + 1
  247. width = hor_idx[-1] - x + 1
  248. vert = np.sum(mask, axis=1)
  249. vert_idx = np.nonzero(vert)[0]
  250. y = vert_idx[0]
  251. height = vert_idx[-1] - y + 1
  252. bbox = [int(x), int(y), int(width), int(height)]
  253. segm_info.append({
  254. 'id': int(segment_id),
  255. 'category_id': int(instance_cat_id[0]),
  256. 'area': int(area),
  257. 'bbox': bbox,
  258. 'iscrowd': 0
  259. })
  260. panoptic_json_annotation = {
  261. 'image_id': image_id,
  262. 'file_name': image_id + '.png',
  263. 'segments_info': segm_info
  264. }
  265. Image.fromarray(pan_seg).save(out_folder / f'{image_id}.png')
  266. panoptic_json_images.append(panoptic_json_image)
  267. panoptic_json_annotations.append(panoptic_json_annotation)
  268. progressbar.update()
  269. panoptic_json = {
  270. 'images': panoptic_json_images,
  271. 'annotations': panoptic_json_annotations,
  272. 'categories': panoptic_json_categories
  273. }
  274. save_json(panoptic_json, out_file)
  275. def main():
  276. args = parse_args()
  277. assert args.task in ['panoptic', 'instance']
  278. src = args.src
  279. if args.task == 'panoptic':
  280. annotation_train_path = f'{src}/ade20k_panoptic_train'
  281. annotation_val_path = f'{src}/ade20k_panoptic_val'
  282. print('Preparing ADE20K panoptic annotations ...')
  283. print(
  284. f'Creating panoptic annotations to {annotation_train_path} and {annotation_val_path} ...' # noqa
  285. )
  286. if os.path.exists(annotation_train_path) or os.path.exists(
  287. annotation_val_path):
  288. raise RuntimeError('Panoptic annotations already exist.')
  289. prepare_panoptic_annotations(src)
  290. print('Done.')
  291. else:
  292. annotation_train_path = f'{src}/ade20k_instance_train'
  293. annotation_val_path = f'{src}/ade20k_instance_val'
  294. print('Preparing ADE20K instance annotations ...')
  295. print(
  296. f'Creating instance annotations to {annotation_train_path} and {annotation_val_path} ...' # noqa
  297. )
  298. if os.path.exists(annotation_train_path) or os.path.exists(
  299. annotation_val_path):
  300. raise RuntimeError('Instance annotations already exist.')
  301. prepare_instance_annotations(src)
  302. print('Done.')
  303. if __name__ == '__main__':
  304. main()