test_coco_video_metric.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. # Copyright (c) OpenMMLab. All rights reserved.
  2. import os.path as osp
  3. import tempfile
  4. from unittest import TestCase
  5. import numpy as np
  6. import pycocotools.mask as mask_util
  7. import torch
  8. from mmengine.fileio import dump
  9. from mmengine.structures import BaseDataElement, InstanceData
  10. from mmdet.evaluation import CocoVideoMetric
  11. from mmdet.structures import DetDataSample, TrackDataSample
  12. class TestCocoVideoMetric(TestCase):
  13. def _create_dummy_coco_json(self, json_name):
  14. dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)
  15. dummy_mask[:5, :5] = 1
  16. rle_mask = mask_util.encode(dummy_mask)
  17. rle_mask['counts'] = rle_mask['counts'].decode('utf-8')
  18. image = {
  19. 'id': 0,
  20. 'width': 640,
  21. 'height': 640,
  22. 'file_name': 'fake_name.jpg',
  23. }
  24. annotation_1 = {
  25. 'id': 1,
  26. 'image_id': 0,
  27. 'category_id': 0,
  28. 'area': 400,
  29. 'bbox': [50, 60, 20, 20],
  30. 'iscrowd': 0,
  31. 'segmentation': rle_mask,
  32. }
  33. annotation_2 = {
  34. 'id': 2,
  35. 'image_id': 0,
  36. 'category_id': 0,
  37. 'area': 900,
  38. 'bbox': [100, 120, 30, 30],
  39. 'iscrowd': 0,
  40. 'segmentation': rle_mask,
  41. }
  42. annotation_3 = {
  43. 'id': 3,
  44. 'image_id': 0,
  45. 'category_id': 1,
  46. 'area': 1600,
  47. 'bbox': [150, 160, 40, 40],
  48. 'iscrowd': 0,
  49. 'segmentation': rle_mask,
  50. }
  51. annotation_4 = {
  52. 'id': 4,
  53. 'image_id': 0,
  54. 'category_id': 0,
  55. 'area': 10000,
  56. 'bbox': [250, 260, 100, 100],
  57. 'iscrowd': 0,
  58. 'segmentation': rle_mask,
  59. }
  60. categories = [
  61. {
  62. 'id': 0,
  63. 'name': 'car',
  64. 'supercategory': 'car',
  65. },
  66. {
  67. 'id': 1,
  68. 'name': 'bicycle',
  69. 'supercategory': 'bicycle',
  70. },
  71. ]
  72. fake_json = {
  73. 'images': [image],
  74. 'annotations':
  75. [annotation_1, annotation_2, annotation_3, annotation_4],
  76. 'categories': categories
  77. }
  78. dump(fake_json, json_name)
  79. def _create_dummy_results(self):
  80. bboxes = np.array([[50, 60, 70, 80], [100, 120, 130, 150],
  81. [150, 160, 190, 200], [250, 260, 350, 360]])
  82. scores = np.array([1.0, 0.98, 0.96, 0.95])
  83. labels = np.array([0, 0, 1, 0])
  84. dummy_mask = np.zeros((4, 10, 10), dtype=np.uint8)
  85. dummy_mask[:, :5, :5] = 1
  86. return dict(
  87. bboxes=torch.from_numpy(bboxes),
  88. scores=torch.from_numpy(scores),
  89. labels=torch.from_numpy(labels),
  90. masks=torch.from_numpy(dummy_mask))
  91. def setUp(self):
  92. self.tmp_dir = tempfile.TemporaryDirectory()
  93. def tearDown(self):
  94. self.tmp_dir.cleanup()
  95. def test_init(self):
  96. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  97. self._create_dummy_coco_json(fake_json_file)
  98. with self.assertRaisesRegex(KeyError, 'metric should be one of'):
  99. CocoVideoMetric(ann_file=fake_json_file, metric='unknown')
  100. def test_evaluate(self):
  101. # create dummy data
  102. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  103. self._create_dummy_coco_json(fake_json_file)
  104. dummy_pred = self._create_dummy_results()
  105. # test single coco dataset evaluation
  106. coco_metric = CocoVideoMetric(
  107. ann_file=fake_json_file,
  108. classwise=False,
  109. outfile_prefix=f'{self.tmp_dir.name}/test')
  110. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  111. pred_det_instances = InstanceData(**dummy_pred)
  112. img_data_sample = DetDataSample()
  113. img_data_sample.pred_instances = pred_det_instances
  114. img_data_sample.set_metainfo(
  115. dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))
  116. track_data_sample = TrackDataSample()
  117. track_data_sample.video_data_samples = [img_data_sample]
  118. predictions = []
  119. if isinstance(track_data_sample, BaseDataElement):
  120. predictions.append(track_data_sample.to_dict())
  121. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  122. eval_results = coco_metric.evaluate()
  123. target = {
  124. 'coco/bbox_mAP': 1.0,
  125. 'coco/bbox_mAP_50': 1.0,
  126. 'coco/bbox_mAP_75': 1.0,
  127. 'coco/bbox_mAP_s': 1.0,
  128. 'coco/bbox_mAP_m': 1.0,
  129. 'coco/bbox_mAP_l': 1.0,
  130. }
  131. self.assertDictEqual(eval_results, target)
  132. self.assertTrue(
  133. osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))
  134. # test box and segm coco dataset evaluation
  135. coco_metric = CocoVideoMetric(
  136. ann_file=fake_json_file,
  137. metric=['bbox', 'segm'],
  138. classwise=False,
  139. outfile_prefix=f'{self.tmp_dir.name}/test')
  140. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  141. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  142. eval_results = coco_metric.evaluate()
  143. target = {
  144. 'coco/bbox_mAP': 1.0,
  145. 'coco/bbox_mAP_50': 1.0,
  146. 'coco/bbox_mAP_75': 1.0,
  147. 'coco/bbox_mAP_s': 1.0,
  148. 'coco/bbox_mAP_m': 1.0,
  149. 'coco/bbox_mAP_l': 1.0,
  150. 'coco/segm_mAP': 1.0,
  151. 'coco/segm_mAP_50': 1.0,
  152. 'coco/segm_mAP_75': 1.0,
  153. 'coco/segm_mAP_s': 1.0,
  154. 'coco/segm_mAP_m': 1.0,
  155. 'coco/segm_mAP_l': 1.0,
  156. }
  157. self.assertDictEqual(eval_results, target)
  158. self.assertTrue(
  159. osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))
  160. self.assertTrue(
  161. osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))
  162. # test invalid custom metric_items
  163. with self.assertRaisesRegex(KeyError,
  164. 'metric item "invalid" is not supported'):
  165. coco_metric = CocoVideoMetric(
  166. ann_file=fake_json_file, metric_items=['invalid'])
  167. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  168. coco_metric.process(
  169. dict(inputs=None, data_samples=None), predictions)
  170. coco_metric.evaluate()
  171. # test custom metric_items
  172. coco_metric = CocoVideoMetric(
  173. ann_file=fake_json_file, metric_items=['mAP_m'])
  174. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  175. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  176. eval_results = coco_metric.evaluate()
  177. target = {
  178. 'coco/bbox_mAP_m': 1.0,
  179. }
  180. self.assertDictEqual(eval_results, target)
  181. def test_classwise_evaluate(self):
  182. # create dummy data
  183. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  184. self._create_dummy_coco_json(fake_json_file)
  185. dummy_pred = self._create_dummy_results()
  186. # test single coco dataset evaluation
  187. coco_metric = CocoVideoMetric(
  188. ann_file=fake_json_file, metric='bbox', classwise=True)
  189. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  190. pred_det_instances = InstanceData(**dummy_pred)
  191. img_data_sample = DetDataSample()
  192. img_data_sample.pred_instances = pred_det_instances
  193. img_data_sample.set_metainfo(
  194. dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))
  195. track_data_sample = TrackDataSample()
  196. track_data_sample.video_data_samples = [img_data_sample]
  197. predictions = []
  198. if isinstance(track_data_sample, BaseDataElement):
  199. predictions.append(track_data_sample.to_dict())
  200. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  201. eval_results = coco_metric.evaluate()
  202. target = {
  203. 'coco/bbox_mAP': 1.0,
  204. 'coco/bbox_mAP_50': 1.0,
  205. 'coco/bbox_mAP_75': 1.0,
  206. 'coco/bbox_mAP_s': 1.0,
  207. 'coco/bbox_mAP_m': 1.0,
  208. 'coco/bbox_mAP_l': 1.0,
  209. 'coco/car_precision': 1.0,
  210. 'coco/bicycle_precision': 1.0,
  211. }
  212. self.assertDictEqual(eval_results, target)
  213. def test_manually_set_iou_thrs(self):
  214. # create dummy data
  215. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  216. self._create_dummy_coco_json(fake_json_file)
  217. # test single coco dataset evaluation
  218. coco_metric = CocoVideoMetric(
  219. ann_file=fake_json_file, metric='bbox', iou_thrs=[0.3, 0.6])
  220. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  221. self.assertEqual(coco_metric.iou_thrs, [0.3, 0.6])
  222. def test_fast_eval_recall(self):
  223. # create dummy data
  224. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  225. self._create_dummy_coco_json(fake_json_file)
  226. dummy_pred = self._create_dummy_results()
  227. # test default proposal nums
  228. coco_metric = CocoVideoMetric(
  229. ann_file=fake_json_file, metric='proposal_fast')
  230. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  231. pred_det_instances = InstanceData(**dummy_pred)
  232. img_data_sample = DetDataSample()
  233. img_data_sample.pred_instances = pred_det_instances
  234. img_data_sample.set_metainfo(
  235. dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))
  236. track_data_sample = TrackDataSample()
  237. track_data_sample.video_data_samples = [img_data_sample]
  238. predictions = []
  239. if isinstance(track_data_sample, BaseDataElement):
  240. predictions.append(track_data_sample.to_dict())
  241. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  242. eval_results = coco_metric.evaluate()
  243. target = {'coco/AR@100': 1.0, 'coco/AR@300': 1.0, 'coco/AR@1000': 1.0}
  244. self.assertDictEqual(eval_results, target)
  245. # test manually set proposal nums
  246. coco_metric = CocoVideoMetric(
  247. ann_file=fake_json_file,
  248. metric='proposal_fast',
  249. proposal_nums=(2, 4))
  250. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  251. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  252. eval_results = coco_metric.evaluate()
  253. target = {'coco/AR@2': 0.5, 'coco/AR@4': 1.0}
  254. self.assertDictEqual(eval_results, target)
  255. def test_evaluate_proposal(self):
  256. # create dummy data
  257. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  258. self._create_dummy_coco_json(fake_json_file)
  259. dummy_pred = self._create_dummy_results()
  260. coco_metric = CocoVideoMetric(
  261. ann_file=fake_json_file, metric='proposal')
  262. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  263. pred_det_instances = InstanceData(**dummy_pred)
  264. img_data_sample = DetDataSample()
  265. img_data_sample.pred_instances = pred_det_instances
  266. img_data_sample.set_metainfo(
  267. dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))
  268. track_data_sample = TrackDataSample()
  269. track_data_sample.video_data_samples = [img_data_sample]
  270. predictions = []
  271. if isinstance(track_data_sample, BaseDataElement):
  272. predictions.append(track_data_sample.to_dict())
  273. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  274. eval_results = coco_metric.evaluate()
  275. print(eval_results)
  276. target = {
  277. 'coco/AR@100': 1,
  278. 'coco/AR@300': 1.0,
  279. 'coco/AR@1000': 1.0,
  280. 'coco/AR_s@1000': 1.0,
  281. 'coco/AR_m@1000': 1.0,
  282. 'coco/AR_l@1000': 1.0
  283. }
  284. self.assertDictEqual(eval_results, target)
  285. def test_empty_results(self):
  286. # create dummy data
  287. fake_json_file = osp.join(self.tmp_dir.name, 'fake_data.json')
  288. self._create_dummy_coco_json(fake_json_file)
  289. coco_metric = CocoVideoMetric(ann_file=fake_json_file, metric='bbox')
  290. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  291. bboxes = np.zeros((0, 4))
  292. labels = np.array([])
  293. scores = np.array([])
  294. dummy_mask = np.zeros((0, 10, 10), dtype=np.uint8)
  295. empty_pred = dict(
  296. bboxes=torch.from_numpy(bboxes),
  297. scores=torch.from_numpy(scores),
  298. labels=torch.from_numpy(labels),
  299. masks=torch.from_numpy(dummy_mask))
  300. pred_det_instances = InstanceData(**empty_pred)
  301. img_data_sample = DetDataSample()
  302. img_data_sample.pred_instances = pred_det_instances
  303. img_data_sample.set_metainfo(
  304. dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))
  305. track_data_sample = TrackDataSample()
  306. track_data_sample.video_data_samples = [img_data_sample]
  307. predictions = []
  308. if isinstance(track_data_sample, BaseDataElement):
  309. predictions.append(track_data_sample.to_dict())
  310. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  311. # coco api Index error will be caught
  312. coco_metric.evaluate()
  313. def test_evaluate_without_json(self):
  314. dummy_pred = self._create_dummy_results()
  315. dummy_mask = np.zeros((10, 10), order='F', dtype=np.uint8)
  316. dummy_mask[:5, :5] = 1
  317. rle_mask = mask_util.encode(dummy_mask)
  318. rle_mask['counts'] = rle_mask['counts'].decode('utf-8')
  319. instances = [{
  320. 'bbox_label': 0,
  321. 'bbox': [50, 60, 70, 80],
  322. 'ignore_flag': 0,
  323. 'mask': rle_mask,
  324. }, {
  325. 'bbox_label': 0,
  326. 'bbox': [100, 120, 130, 150],
  327. 'ignore_flag': 0,
  328. 'mask': rle_mask,
  329. }, {
  330. 'bbox_label': 1,
  331. 'bbox': [150, 160, 190, 200],
  332. 'ignore_flag': 0,
  333. 'mask': rle_mask,
  334. }, {
  335. 'bbox_label': 0,
  336. 'bbox': [250, 260, 350, 360],
  337. 'ignore_flag': 0,
  338. 'mask': rle_mask,
  339. }]
  340. coco_metric = CocoVideoMetric(
  341. ann_file=None,
  342. metric=['bbox', 'segm'],
  343. classwise=False,
  344. outfile_prefix=f'{self.tmp_dir.name}/test')
  345. coco_metric.dataset_meta = dict(classes=['car', 'bicycle'])
  346. pred_det_instances = InstanceData(**dummy_pred)
  347. img_data_sample = DetDataSample()
  348. img_data_sample.pred_instances = pred_det_instances
  349. img_data_sample.instances = instances
  350. img_data_sample.set_metainfo(
  351. dict(img_id=0, ori_shape=(640, 640), ori_video_length=1))
  352. track_data_sample = TrackDataSample()
  353. track_data_sample.video_data_samples = [img_data_sample]
  354. predictions = []
  355. if isinstance(track_data_sample, BaseDataElement):
  356. predictions.append(track_data_sample.to_dict())
  357. coco_metric.process(dict(inputs=None, data_samples=None), predictions)
  358. eval_results = coco_metric.evaluate()
  359. print(eval_results)
  360. target = {
  361. 'coco/bbox_mAP': 1.0,
  362. 'coco/bbox_mAP_50': 1.0,
  363. 'coco/bbox_mAP_75': 1.0,
  364. 'coco/bbox_mAP_s': 1.0,
  365. 'coco/bbox_mAP_m': 1.0,
  366. 'coco/bbox_mAP_l': 1.0,
  367. 'coco/segm_mAP': 1.0,
  368. 'coco/segm_mAP_50': 1.0,
  369. 'coco/segm_mAP_75': 1.0,
  370. 'coco/segm_mAP_s': 1.0,
  371. 'coco/segm_mAP_m': 1.0,
  372. 'coco/segm_mAP_l': 1.0,
  373. }
  374. self.assertDictEqual(eval_results, target)
  375. self.assertTrue(
  376. osp.isfile(osp.join(self.tmp_dir.name, 'test.bbox.json')))
  377. self.assertTrue(
  378. osp.isfile(osp.join(self.tmp_dir.name, 'test.segm.json')))
  379. self.assertTrue(
  380. osp.isfile(osp.join(self.tmp_dir.name, 'test.gt.json')))