image.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import numpy as np
  2. class Image(object):
  3. def __init__(self, mode):
  4. self.ID = None
  5. self._width = None
  6. self._height = None
  7. self.dtboxes = None
  8. self.gtboxes = None
  9. self.eval_mode = mode
  10. self._ignNum = None
  11. self._gtNum = None
  12. self._dtNum = None
  13. def load(self, record, body_key, head_key, class_names, gtflag):
  14. """
  15. :meth: read the object from a dict
  16. """
  17. if "ID" in record and self.ID is None:
  18. self.ID = record['ID']
  19. if "width" in record and self._width is None:
  20. self._width = record["width"]
  21. if "height" in record and self._height is None:
  22. self._height = record["height"]
  23. if gtflag:
  24. self._gtNum = len(record["gtboxes"])
  25. body_bbox, head_bbox = self.load_gt_boxes(record, 'gtboxes', class_names)
  26. if self.eval_mode == 0:
  27. self.gtboxes = body_bbox
  28. self._ignNum = (body_bbox[:, -1] == -1).sum()
  29. elif self.eval_mode == 1:
  30. self.gtboxes = head_bbox
  31. self._ignNum = (head_bbox[:, -1] == -1).sum()
  32. elif self.eval_mode == 2:
  33. gt_tag = np.array([body_bbox[i,-1]!=-1 and head_bbox[i,-1]!=-1 for i in range(len(body_bbox))])
  34. self._ignNum = (gt_tag == 0).sum()
  35. self.gtboxes = np.hstack((body_bbox[:, :-1], head_bbox[:, :-1], gt_tag.reshape(-1, 1)))
  36. else:
  37. raise Exception('Unknown evaluation mode!')
  38. if not gtflag:
  39. self._dtNum = len(record["dtboxes"])
  40. if self.eval_mode == 0:
  41. self.dtboxes = self.load_det_boxes(record, 'dtboxes', body_key, 'score')
  42. elif self.eval_mode == 1:
  43. self.dtboxes = self.load_det_boxes(record, 'dtboxes', head_key, 'score')
  44. elif self.eval_mode == 2:
  45. body_dtboxes = self.load_det_boxes(record, 'dtboxes', body_key)
  46. head_dtboxes = self.load_det_boxes(record, 'dtboxes', head_key, 'score')
  47. self.dtboxes = np.hstack((body_dtboxes, head_dtboxes))
  48. else:
  49. raise Exception('Unknown evaluation mode!')
  50. def compare_caltech(self, thres):
  51. """
  52. :meth: match the detection results with the groundtruth by Caltech matching strategy
  53. :param thres: iou threshold
  54. :type thres: float
  55. :return: a list of tuples (dtbox, imageID), in the descending sort of dtbox.score
  56. """
  57. dtboxes = self.dtboxes if self.dtboxes is not None else list()
  58. gtboxes = self.gtboxes if self.gtboxes is not None else list()
  59. dt_matched = np.zeros(len(dtboxes))
  60. gt_matched = np.zeros(len(gtboxes))
  61. dtboxes = np.array(sorted(dtboxes, key=lambda x: x[-1], reverse=True))
  62. gtboxes = np.array(sorted(gtboxes, key=lambda x: x[-1], reverse=True))
  63. if len(dtboxes):
  64. overlap_iou = self.box_overlap_opr(dtboxes, gtboxes, True)
  65. overlap_ioa = self.box_overlap_opr(dtboxes, gtboxes, False)
  66. else:
  67. return list()
  68. scorelist = list()
  69. for i, dt in enumerate(dtboxes):
  70. maxpos = -1
  71. maxiou = thres
  72. for j, gt in enumerate(gtboxes):
  73. if gt_matched[j] == 1:
  74. continue
  75. if gt[-1] > 0:
  76. overlap = overlap_iou[i][j]
  77. if overlap > maxiou:
  78. maxiou = overlap
  79. maxpos = j
  80. else:
  81. if maxpos >= 0:
  82. break
  83. else:
  84. overlap = overlap_ioa[i][j]
  85. if overlap > thres:
  86. maxiou = overlap
  87. maxpos = j
  88. if maxpos >= 0:
  89. if gtboxes[maxpos, -1] > 0:
  90. gt_matched[maxpos] = 1
  91. dt_matched[i] = 1
  92. scorelist.append((dt, 1, self.ID))
  93. else:
  94. dt_matched[i] = -1
  95. else:
  96. dt_matched[i] = 0
  97. scorelist.append((dt, 0, self.ID))
  98. return scorelist
  99. def compare_caltech_union(self, thres):
  100. """
  101. :meth: match the detection results with the groundtruth by Caltech matching strategy
  102. :param thres: iou threshold
  103. :type thres: float
  104. :return: a list of tuples (dtbox, imageID), in the descending sort of dtbox.score
  105. """
  106. dtboxes = self.dtboxes if self.dtboxes is not None else list()
  107. gtboxes = self.gtboxes if self.gtboxes is not None else list()
  108. if len(dtboxes) == 0:
  109. return list()
  110. dt_matched = np.zeros(dtboxes.shape[0])
  111. gt_matched = np.zeros(gtboxes.shape[0])
  112. dtboxes = np.array(sorted(dtboxes, key=lambda x: x[-1], reverse=True))
  113. gtboxes = np.array(sorted(gtboxes, key=lambda x: x[-1], reverse=True))
  114. dt_body_boxes = np.hstack((dtboxes[:, :4], dtboxes[:, -1][:,None]))
  115. dt_head_boxes = dtboxes[:, 4:8]
  116. gt_body_boxes = np.hstack((gtboxes[:, :4], gtboxes[:, -1][:,None]))
  117. gt_head_boxes = gtboxes[:, 4:8]
  118. overlap_iou = self.box_overlap_opr(dt_body_boxes, gt_body_boxes, True)
  119. overlap_head = self.box_overlap_opr(dt_head_boxes, gt_head_boxes, True)
  120. overlap_ioa = self.box_overlap_opr(dt_body_boxes, gt_body_boxes, False)
  121. scorelist = list()
  122. for i, dt in enumerate(dtboxes):
  123. maxpos = -1
  124. maxiou = thres
  125. for j, gt in enumerate(gtboxes):
  126. if gt_matched[j] == 1:
  127. continue
  128. if gt[-1] > 0:
  129. o_body = overlap_iou[i][j]
  130. o_head = overlap_head[i][j]
  131. if o_body > maxiou and o_head > maxiou:
  132. maxiou = o_body
  133. maxpos = j
  134. else:
  135. if maxpos >= 0:
  136. break
  137. else:
  138. o_body = overlap_ioa[i][j]
  139. if o_body > thres:
  140. maxiou = o_body
  141. maxpos = j
  142. if maxpos >= 0:
  143. if gtboxes[maxpos, -1] > 0:
  144. gt_matched[maxpos] = 1
  145. dt_matched[i] = 1
  146. scorelist.append((dt, 1, self.ID))
  147. else:
  148. dt_matched[i] = -1
  149. else:
  150. dt_matched[i] = 0
  151. scorelist.append((dt, 0, self.ID))
  152. return scorelist
  153. def box_overlap_opr(self, dboxes:np.ndarray, gboxes:np.ndarray, if_iou):
  154. eps = 1e-6
  155. assert dboxes.shape[-1] >= 4 and gboxes.shape[-1] >= 4
  156. N, K = dboxes.shape[0], gboxes.shape[0]
  157. dtboxes = np.tile(np.expand_dims(dboxes, axis = 1), (1, K, 1))
  158. gtboxes = np.tile(np.expand_dims(gboxes, axis = 0), (N, 1, 1))
  159. iw = np.minimum(dtboxes[:,:,2], gtboxes[:,:,2]) - np.maximum(dtboxes[:,:,0], gtboxes[:,:,0])
  160. ih = np.minimum(dtboxes[:,:,3], gtboxes[:,:,3]) - np.maximum(dtboxes[:,:,1], gtboxes[:,:,1])
  161. inter = np.maximum(0, iw) * np.maximum(0, ih)
  162. dtarea = (dtboxes[:,:,2] - dtboxes[:,:,0]) * (dtboxes[:,:,3] - dtboxes[:,:,1])
  163. if if_iou:
  164. gtarea = (gtboxes[:,:,2] - gtboxes[:,:,0]) * (gtboxes[:,:,3] - gtboxes[:,:,1])
  165. ious = inter / (dtarea + gtarea - inter + eps)
  166. else:
  167. ious = inter / (dtarea + eps)
  168. return ious
  169. def clip_all_boader(self):
  170. def _clip_boundary(boxes,height,width):
  171. assert boxes.shape[-1]>=4
  172. boxes[:,0] = np.minimum(np.maximum(boxes[:,0],0), width - 1)
  173. boxes[:,1] = np.minimum(np.maximum(boxes[:,1],0), height - 1)
  174. boxes[:,2] = np.maximum(np.minimum(boxes[:,2],width), 0)
  175. boxes[:,3] = np.maximum(np.minimum(boxes[:,3],height), 0)
  176. return boxes
  177. assert self.dtboxes.shape[-1]>=4
  178. assert self.gtboxes.shape[-1]>=4
  179. assert self._width is not None and self._height is not None
  180. if self.eval_mode == 2:
  181. self.dtboxes[:, :4] = _clip_boundary(self.dtboxes[:, :4], self._height, self._width)
  182. self.gtboxes[:, :4] = _clip_boundary(self.gtboxes[:, :4], self._height, self._width)
  183. self.dtboxes[:, 4:8] = _clip_boundary(self.dtboxes[:, 4:8], self._height, self._width)
  184. self.gtboxes[:, 4:8] = _clip_boundary(self.gtboxes[:, 4:8], self._height, self._width)
  185. else:
  186. self.dtboxes = _clip_boundary(self.dtboxes, self._height, self._width)
  187. self.gtboxes = _clip_boundary(self.gtboxes, self._height, self._width)
  188. def load_gt_boxes(self, dict_input, key_name, class_names):
  189. assert key_name in dict_input
  190. if len(dict_input[key_name]) < 1:
  191. return np.empty([0, 5])
  192. head_bbox = []
  193. body_bbox = []
  194. for rb in dict_input[key_name]:
  195. if rb['tag'] in class_names:
  196. body_tag = class_names.index(rb['tag'])
  197. head_tag = 1
  198. else:
  199. body_tag = -1
  200. head_tag = -1
  201. if 'extra' in rb:
  202. if 'ignore' in rb['extra']:
  203. if rb['extra']['ignore'] != 0:
  204. body_tag = -1
  205. head_tag = -1
  206. if 'head_attr' in rb:
  207. if 'ignore' in rb['head_attr']:
  208. if rb['head_attr']['ignore'] != 0:
  209. head_tag = -1
  210. head_bbox.append(np.hstack((rb['hbox'], head_tag)))
  211. body_bbox.append(np.hstack((rb['fbox'], body_tag)))
  212. head_bbox = np.array(head_bbox)
  213. head_bbox[:, 2:4] += head_bbox[:, :2]
  214. body_bbox = np.array(body_bbox)
  215. body_bbox[:, 2:4] += body_bbox[:, :2]
  216. return body_bbox, head_bbox
  217. def load_det_boxes(self, dict_input, key_name, key_box, key_score=None, key_tag=None):
  218. assert key_name in dict_input
  219. if len(dict_input[key_name]) < 1:
  220. return np.empty([0, 5])
  221. else:
  222. assert key_box in dict_input[key_name][0]
  223. if key_score:
  224. assert key_score in dict_input[key_name][0]
  225. if key_tag:
  226. assert key_tag in dict_input[key_name][0]
  227. if key_score:
  228. if key_tag:
  229. bboxes = np.vstack([np.hstack((rb[key_box], rb[key_score], rb[key_tag])) for rb in dict_input[key_name]])
  230. else:
  231. bboxes = np.vstack([np.hstack((rb[key_box], rb[key_score])) for rb in dict_input[key_name]])
  232. else:
  233. if key_tag:
  234. bboxes = np.vstack([np.hstack((rb[key_box], rb[key_tag])) for rb in dict_input[key_name]])
  235. else:
  236. bboxes = np.vstack([rb[key_box] for rb in dict_input[key_name]])
  237. bboxes[:, 2:4] += bboxes[:, :2]
  238. return bboxes
  239. def compare_voc(self, thres):
  240. """
  241. :meth: match the detection results with the groundtruth by VOC matching strategy
  242. :param thres: iou threshold
  243. :type thres: float
  244. :return: a list of tuples (dtbox, imageID), in the descending sort of dtbox.score
  245. """
  246. if self.dtboxes is None:
  247. return list()
  248. dtboxes = self.dtboxes
  249. gtboxes = self.gtboxes if self.gtboxes is not None else list()
  250. dtboxes.sort(key=lambda x: x.score, reverse=True)
  251. gtboxes.sort(key=lambda x: x.ign)
  252. scorelist = list()
  253. for i, dt in enumerate(dtboxes):
  254. maxpos = -1
  255. maxiou = thres
  256. for j, gt in enumerate(gtboxes):
  257. overlap = dt.iou(gt)
  258. if overlap > maxiou:
  259. maxiou = overlap
  260. maxpos = j
  261. if maxpos >= 0:
  262. if gtboxes[maxpos].ign == 0:
  263. gtboxes[maxpos].matched = 1
  264. dtboxes[i].matched = 1
  265. scorelist.append((dt, self.ID))
  266. else:
  267. dtboxes[i].matched = -1
  268. else:
  269. dtboxes[i].matched = 0
  270. scorelist.append((dt, self.ID))
  271. return scorelist