metric.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import numpy as np
  2. import numpy.linalg as LA
  3. import matplotlib.pyplot as plt
  4. from lcnn.utils import argsort2d
  5. DX = [0, 0, 1, -1, 1, 1, -1, -1]
  6. DY = [1, -1, 0, 0, 1, -1, 1, -1]
  7. def ap(tp, fp):
  8. recall = tp
  9. precision = tp / np.maximum(tp + fp, 1e-9)
  10. recall = np.concatenate(([0.0], recall, [1.0]))
  11. precision = np.concatenate(([0.0], precision, [0.0]))
  12. for i in range(precision.size - 1, 0, -1):
  13. precision[i - 1] = max(precision[i - 1], precision[i])
  14. i = np.where(recall[1:] != recall[:-1])[0]
  15. return np.sum((recall[i + 1] - recall[i]) * precision[i + 1])
  16. def APJ(vert_pred, vert_gt, max_distance, im_ids):
  17. if len(vert_pred) == 0:
  18. return 0
  19. vert_pred = np.array(vert_pred)
  20. vert_gt = np.array(vert_gt)
  21. confidence = vert_pred[:, -1]
  22. idx = np.argsort(-confidence)
  23. vert_pred = vert_pred[idx, :]
  24. im_ids = im_ids[idx]
  25. n_gt = sum(len(gt) for gt in vert_gt)
  26. nd = len(im_ids)
  27. tp, fp = np.zeros(nd, dtype=np.float), np.zeros(nd, dtype=np.float)
  28. hit = [[False for _ in j] for j in vert_gt]
  29. for i in range(nd):
  30. gt_juns = vert_gt[im_ids[i]]
  31. pred_juns = vert_pred[i][:-1]
  32. if len(gt_juns) == 0:
  33. continue
  34. dists = np.linalg.norm((pred_juns[None, :] - gt_juns), axis=1)
  35. choice = np.argmin(dists)
  36. dist = np.min(dists)
  37. if dist < max_distance and not hit[im_ids[i]][choice]:
  38. tp[i] = 1
  39. hit[im_ids[i]][choice] = True
  40. else:
  41. fp[i] = 1
  42. tp = np.cumsum(tp) / n_gt
  43. fp = np.cumsum(fp) / n_gt
  44. return ap(tp, fp)
  45. def nms_j(heatmap, delta=1):
  46. heatmap = heatmap.copy()
  47. disable = np.zeros_like(heatmap, dtype=np.bool)
  48. for x, y in argsort2d(heatmap):
  49. for dx, dy in zip(DX, DY):
  50. xp, yp = x + dx, y + dy
  51. if not (0 <= xp < heatmap.shape[0] and 0 <= yp < heatmap.shape[1]):
  52. continue
  53. if heatmap[x, y] >= heatmap[xp, yp]:
  54. disable[xp, yp] = True
  55. heatmap[disable] *= 0.6
  56. return heatmap
  57. def mAPJ(pred, truth, distances, im_ids):
  58. return sum(APJ(pred, truth, d, im_ids) for d in distances) / len(distances) * 100
  59. def post_jheatmap(heatmap, offset=None, delta=1):
  60. heatmap = nms_j(heatmap, delta=delta)
  61. # only select the best 1000 junctions for efficiency
  62. v0 = argsort2d(-heatmap)[:1000]
  63. confidence = -np.sort(-heatmap.ravel())[:1000]
  64. keep_id = np.where(confidence >= 1e-2)[0]
  65. if len(keep_id) == 0:
  66. return np.zeros((0, 3))
  67. confidence = confidence[keep_id]
  68. if offset is not None:
  69. v0 = np.array([v + offset[:, v[0], v[1]] for v in v0])
  70. v0 = v0[keep_id] + 0.5
  71. v0 = np.hstack((v0, confidence[:, np.newaxis]))
  72. return v0
  73. def vectorized_wireframe_2d_metric(
  74. vert_pred, dpth_pred, edge_pred, vert_gt, dpth_gt, edge_gt, threshold
  75. ):
  76. # staging 1: matching
  77. nd = len(vert_pred)
  78. sorted_confidence = np.argsort(-vert_pred[:, -1])
  79. vert_pred = vert_pred[sorted_confidence, :-1]
  80. dpth_pred = dpth_pred[sorted_confidence]
  81. d = np.sqrt(
  82. np.sum(vert_pred ** 2, 1)[:, None]
  83. + np.sum(vert_gt ** 2, 1)[None, :]
  84. - 2 * vert_pred @ vert_gt.T
  85. )
  86. choice = np.argmin(d, 1)
  87. dist = np.min(d, 1)
  88. # staging 2: compute depth metric: SIL/L2
  89. loss_L1 = loss_L2 = 0
  90. hit = np.zeros_like(dpth_gt, np.bool)
  91. SIL = np.zeros(dpth_pred)
  92. for i in range(nd):
  93. if dist[i] < threshold and not hit[choice[i]]:
  94. hit[choice[i]] = True
  95. loss_L1 += abs(dpth_gt[choice[i]] - dpth_pred[i])
  96. loss_L2 += (dpth_gt[choice[i]] - dpth_pred[i]) ** 2
  97. a = np.maximum(-dpth_pred[i], 1e-10)
  98. b = -dpth_gt[choice[i]]
  99. SIL[i] = np.log(a) - np.log(b)
  100. else:
  101. choice[i] = -1
  102. n = max(np.sum(hit), 1)
  103. loss_L1 /= n
  104. loss_L2 /= n
  105. loss_SIL = np.sum(SIL ** 2) / n - np.sum(SIL) ** 2 / (n * n)
  106. # staging 3: compute mAP for edge matching
  107. edgeset = set([frozenset(e) for e in edge_gt])
  108. tp = np.zeros(len(edge_pred), dtype=np.float)
  109. fp = np.zeros(len(edge_pred), dtype=np.float)
  110. for i, (v0, v1, score) in enumerate(sorted(edge_pred, key=-edge_pred[2])):
  111. length = LA.norm(vert_gt[v0] - vert_gt[v1], axis=1)
  112. if frozenset([choice[v0], choice[v1]]) in edgeset:
  113. tp[i] = length
  114. else:
  115. fp[i] = length
  116. total_length = LA.norm(
  117. vert_gt[edge_gt[:, 0]] - vert_gt[edge_gt[:, 1]], axis=1
  118. ).sum()
  119. return ap(tp / total_length, fp / total_length), (loss_SIL, loss_L1, loss_L2)
  120. def vectorized_wireframe_3d_metric(
  121. vert_pred, dpth_pred, edge_pred, vert_gt, dpth_gt, edge_gt, threshold
  122. ):
  123. # staging 1: matching
  124. nd = len(vert_pred)
  125. sorted_confidence = np.argsort(-vert_pred[:, -1])
  126. vert_pred = np.hstack([vert_pred[:, :-1], dpth_pred[:, None]])[sorted_confidence]
  127. vert_gt = np.hstack([vert_gt[:, :-1], dpth_gt[:, None]])
  128. d = np.sqrt(
  129. np.sum(vert_pred ** 2, 1)[:, None]
  130. + np.sum(vert_gt ** 2, 1)[None, :]
  131. - 2 * vert_pred @ vert_gt.T
  132. )
  133. choice = np.argmin(d, 1)
  134. dist = np.min(d, 1)
  135. hit = np.zeros_like(dpth_gt, np.bool)
  136. for i in range(nd):
  137. if dist[i] < threshold and not hit[choice[i]]:
  138. hit[choice[i]] = True
  139. else:
  140. choice[i] = -1
  141. # staging 2: compute mAP for edge matching
  142. edgeset = set([frozenset(e) for e in edge_gt])
  143. tp = np.zeros(len(edge_pred), dtype=np.float)
  144. fp = np.zeros(len(edge_pred), dtype=np.float)
  145. for i, (v0, v1, score) in enumerate(sorted(edge_pred, key=-edge_pred[2])):
  146. length = LA.norm(vert_gt[v0] - vert_gt[v1], axis=1)
  147. if frozenset([choice[v0], choice[v1]]) in edgeset:
  148. tp[i] = length
  149. else:
  150. fp[i] = length
  151. total_length = LA.norm(
  152. vert_gt[edge_gt[:, 0]] - vert_gt[edge_gt[:, 1]], axis=1
  153. ).sum()
  154. return ap(tp / total_length, fp / total_length)
  155. def msTPFP(line_pred, line_gt, threshold):
  156. diff = ((line_pred[:, None, :, None] - line_gt[:, None]) ** 2).sum(-1)
  157. diff = np.minimum(
  158. diff[:, :, 0, 0] + diff[:, :, 1, 1], diff[:, :, 0, 1] + diff[:, :, 1, 0]
  159. )
  160. choice = np.argmin(diff, 1)
  161. dist = np.min(diff, 1)
  162. hit = np.zeros(len(line_gt), np.bool)
  163. tp = np.zeros(len(line_pred), np.float)
  164. fp = np.zeros(len(line_pred), np.float)
  165. for i in range(len(line_pred)):
  166. if dist[i] < threshold and not hit[choice[i]]:
  167. hit[choice[i]] = True
  168. tp[i] = 1
  169. else:
  170. fp[i] = 1
  171. return tp, fp
  172. def msAP(line_pred, line_gt, threshold):
  173. tp, fp = msTPFP(line_pred, line_gt, threshold)
  174. tp = np.cumsum(tp) / len(line_gt)
  175. fp = np.cumsum(fp) / len(line_gt)
  176. return ap(tp, fp)