coco_error_analysis.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import copy
  2. import os
  3. from argparse import ArgumentParser
  4. from multiprocessing import Pool
  5. import matplotlib.pyplot as plt
  6. import numpy as np
  7. from pycocotools.coco import COCO
  8. from pycocotools.cocoeval import COCOeval
  9. def makeplot(rs, ps, outDir, class_name, iou_type):
  10. cs = np.vstack([
  11. np.ones((2, 3)), np.array([.31, .51, .74]), np.array([.75, .31, .30]),
  12. np.array([.36, .90, .38]), np.array([.50, .39, .64]),
  13. np.array([1, .6, 0])
  14. ])
  15. areaNames = ['allarea', 'small', 'medium', 'large']
  16. types = ['C75', 'C50', 'Loc', 'Sim', 'Oth', 'BG', 'FN']
  17. for i in range(len(areaNames)):
  18. area_ps = ps[..., i, 0]
  19. figure_tile = iou_type + '-' + class_name + '-' + areaNames[i]
  20. aps = [ps_.mean() for ps_ in area_ps]
  21. ps_curve = [
  22. ps_.mean(axis=1) if ps_.ndim > 1 else ps_ for ps_ in area_ps
  23. ]
  24. ps_curve.insert(0, np.zeros(ps_curve[0].shape))
  25. fig = plt.figure()
  26. ax = plt.subplot(111)
  27. for k in range(len(types)):
  28. ax.plot(rs, ps_curve[k + 1], color=[0, 0, 0], linewidth=0.5)
  29. ax.fill_between(
  30. rs,
  31. ps_curve[k],
  32. ps_curve[k + 1],
  33. color=cs[k],
  34. label=str('[{:.3f}'.format(aps[k]) + ']' + types[k]))
  35. plt.xlabel('recall')
  36. plt.ylabel('precision')
  37. plt.xlim(0, 1.)
  38. plt.ylim(0, 1.)
  39. plt.title(figure_tile)
  40. plt.legend()
  41. # plt.show()
  42. fig.savefig(outDir + '/{}.png'.format(figure_tile))
  43. plt.close(fig)
  44. def analyze_individual_category(k, cocoDt, cocoGt, catId, iou_type):
  45. nm = cocoGt.loadCats(catId)[0]
  46. print('--------------analyzing {}-{}---------------'.format(k + 1, nm[
  47. 'name']))
  48. ps_ = {}
  49. dt = copy.deepcopy(cocoDt)
  50. nm = cocoGt.loadCats(catId)[0]
  51. imgIds = cocoGt.getImgIds()
  52. dt_anns = dt.dataset['annotations']
  53. select_dt_anns = []
  54. for ann in dt_anns:
  55. if ann['category_id'] == catId:
  56. select_dt_anns.append(ann)
  57. dt.dataset['annotations'] = select_dt_anns
  58. dt.createIndex()
  59. # compute precision but ignore superclass confusion
  60. gt = copy.deepcopy(cocoGt)
  61. child_catIds = gt.getCatIds(supNms=[nm['supercategory']])
  62. for idx, ann in enumerate(gt.dataset['annotations']):
  63. if (ann['category_id'] in child_catIds and
  64. ann['category_id'] != catId):
  65. gt.dataset['annotations'][idx]['ignore'] = 1
  66. gt.dataset['annotations'][idx]['iscrowd'] = 1
  67. gt.dataset['annotations'][idx]['category_id'] = catId
  68. cocoEval = COCOeval(gt, copy.deepcopy(dt), iou_type)
  69. cocoEval.params.imgIds = imgIds
  70. cocoEval.params.maxDets = [100]
  71. cocoEval.params.iouThrs = [.1]
  72. cocoEval.params.useCats = 1
  73. cocoEval.evaluate()
  74. cocoEval.accumulate()
  75. ps_supercategory = cocoEval.eval['precision'][0, :, k, :, :]
  76. ps_['ps_supercategory'] = ps_supercategory
  77. # compute precision but ignore any class confusion
  78. gt = copy.deepcopy(cocoGt)
  79. for idx, ann in enumerate(gt.dataset['annotations']):
  80. if ann['category_id'] != catId:
  81. gt.dataset['annotations'][idx]['ignore'] = 1
  82. gt.dataset['annotations'][idx]['iscrowd'] = 1
  83. gt.dataset['annotations'][idx]['category_id'] = catId
  84. cocoEval = COCOeval(gt, copy.deepcopy(dt), iou_type)
  85. cocoEval.params.imgIds = imgIds
  86. cocoEval.params.maxDets = [100]
  87. cocoEval.params.iouThrs = [.1]
  88. cocoEval.params.useCats = 1
  89. cocoEval.evaluate()
  90. cocoEval.accumulate()
  91. ps_allcategory = cocoEval.eval['precision'][0, :, k, :, :]
  92. ps_['ps_allcategory'] = ps_allcategory
  93. return k, ps_
  94. def analyze_results(res_file, ann_file, res_types, out_dir):
  95. for res_type in res_types:
  96. assert res_type in ['bbox', 'segm']
  97. directory = os.path.dirname(out_dir + '/')
  98. if not os.path.exists(directory):
  99. print('-------------create {}-----------------'.format(out_dir))
  100. os.makedirs(directory)
  101. cocoGt = COCO(ann_file)
  102. cocoDt = cocoGt.loadRes(res_file)
  103. imgIds = cocoGt.getImgIds()
  104. for res_type in res_types:
  105. res_out_dir = out_dir + '/' + res_type + '/'
  106. res_directory = os.path.dirname(res_out_dir)
  107. if not os.path.exists(res_directory):
  108. print('-------------create {}-----------------'.format(
  109. res_out_dir))
  110. os.makedirs(res_directory)
  111. iou_type = res_type
  112. cocoEval = COCOeval(
  113. copy.deepcopy(cocoGt), copy.deepcopy(cocoDt), iou_type)
  114. cocoEval.params.imgIds = imgIds
  115. cocoEval.params.iouThrs = [.75, .5, .1]
  116. cocoEval.params.maxDets = [100]
  117. cocoEval.evaluate()
  118. cocoEval.accumulate()
  119. ps = cocoEval.eval['precision']
  120. ps = np.vstack([ps, np.zeros((4, *ps.shape[1:]))])
  121. catIds = cocoGt.getCatIds()
  122. recThrs = cocoEval.params.recThrs
  123. with Pool(processes=48) as pool:
  124. args = [(k, cocoDt, cocoGt, catId, iou_type)
  125. for k, catId in enumerate(catIds)]
  126. analyze_results = pool.starmap(analyze_individual_category, args)
  127. for k, catId in enumerate(catIds):
  128. nm = cocoGt.loadCats(catId)[0]
  129. print('--------------saving {}-{}---------------'.format(k + 1, nm[
  130. 'name']))
  131. analyze_result = analyze_results[k]
  132. assert k == analyze_result[0]
  133. ps_supercategory = analyze_result[1]['ps_supercategory']
  134. ps_allcategory = analyze_result[1]['ps_allcategory']
  135. # compute precision but ignore superclass confusion
  136. ps[3, :, k, :, :] = ps_supercategory
  137. # compute precision but ignore any class confusion
  138. ps[4, :, k, :, :] = ps_allcategory
  139. # fill in background and false negative errors and plot
  140. #ps[ps == -1] = 0
  141. T, _, _, A, _ = ps.shape
  142. for t in range(T):
  143. for a in range(A):
  144. if np.sum(ps[t, :, k, a, :] ==
  145. -1) != len(ps[t, :, k, :, :]):
  146. ps[t, :, k, a, :][ps[t, :, k, a, :] == -1] = 0
  147. ps[5, :, k, :, :] = (ps[4, :, k, :, :] > 0)
  148. ps[6, :, k, :, :] = 1.0
  149. makeplot(recThrs, ps[:, :, k], res_out_dir, nm['name'], iou_type)
  150. makeplot(recThrs, ps, res_out_dir, 'allclass', iou_type)
  151. def main():
  152. parser = ArgumentParser(description='COCO Error Analysis Tool')
  153. parser.add_argument('result', help='result file (json format) path')
  154. parser.add_argument('out_dir', help='dir to save analyze result images')
  155. parser.add_argument(
  156. '--ann',
  157. default='data/coco/annotations/instances_val2017.json',
  158. help='annotation file path')
  159. parser.add_argument(
  160. '--types', type=str, nargs='+', default=['bbox'], help='result types')
  161. args = parser.parse_args()
  162. analyze_results(args.result, args.ann, args.types, out_dir=args.out_dir)
  163. if __name__ == '__main__':
  164. main()