ocsort_matching.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. This code is based on https://github.com/noahcao/OC_SORT/blob/master/trackers/ocsort_tracker/association.py
  16. """
  17. import os
  18. import numpy as np
  19. def iou_batch(bboxes1, bboxes2):
  20. """
  21. From SORT: Computes IOU between two bboxes in the form [x1,y1,x2,y2]
  22. """
  23. bboxes2 = np.expand_dims(bboxes2, 0)
  24. bboxes1 = np.expand_dims(bboxes1, 1)
  25. xx1 = np.maximum(bboxes1[..., 0], bboxes2[..., 0])
  26. yy1 = np.maximum(bboxes1[..., 1], bboxes2[..., 1])
  27. xx2 = np.minimum(bboxes1[..., 2], bboxes2[..., 2])
  28. yy2 = np.minimum(bboxes1[..., 3], bboxes2[..., 3])
  29. w = np.maximum(0., xx2 - xx1)
  30. h = np.maximum(0., yy2 - yy1)
  31. wh = w * h
  32. o = wh / ((bboxes1[..., 2] - bboxes1[..., 0]) *
  33. (bboxes1[..., 3] - bboxes1[..., 1]) +
  34. (bboxes2[..., 2] - bboxes2[..., 0]) *
  35. (bboxes2[..., 3] - bboxes2[..., 1]) - wh)
  36. return (o)
  37. def speed_direction_batch(dets, tracks):
  38. tracks = tracks[..., np.newaxis]
  39. CX1, CY1 = (dets[:, 0] + dets[:, 2]) / 2.0, (dets[:, 1] + dets[:, 3]) / 2.0
  40. CX2, CY2 = (tracks[:, 0] + tracks[:, 2]) / 2.0, (
  41. tracks[:, 1] + tracks[:, 3]) / 2.0
  42. dx = CX1 - CX2
  43. dy = CY1 - CY2
  44. norm = np.sqrt(dx**2 + dy**2) + 1e-6
  45. dx = dx / norm
  46. dy = dy / norm
  47. return dy, dx # size: num_track x num_det
  48. def linear_assignment(cost_matrix):
  49. try:
  50. import lap
  51. _, x, y = lap.lapjv(cost_matrix, extend_cost=True)
  52. match = np.array([[y[i], i] for i in x if i >= 0])
  53. return match
  54. except ImportError:
  55. from scipy.optimize import linear_sum_assignment
  56. x, y = linear_sum_assignment(cost_matrix)
  57. return np.array(list(zip(x, y)))
  58. def associate(detections, trackers, iou_threshold, velocities, previous_obs,
  59. vdc_weight):
  60. if (len(trackers) == 0):
  61. return np.empty(
  62. (0, 2), dtype=int), np.arange(len(detections)), np.empty(
  63. (0, 5), dtype=int)
  64. Y, X = speed_direction_batch(detections, previous_obs)
  65. inertia_Y, inertia_X = velocities[:, 0], velocities[:, 1]
  66. inertia_Y = np.repeat(inertia_Y[:, np.newaxis], Y.shape[1], axis=1)
  67. inertia_X = np.repeat(inertia_X[:, np.newaxis], X.shape[1], axis=1)
  68. diff_angle_cos = inertia_X * X + inertia_Y * Y
  69. diff_angle_cos = np.clip(diff_angle_cos, a_min=-1, a_max=1)
  70. diff_angle = np.arccos(diff_angle_cos)
  71. diff_angle = (np.pi / 2.0 - np.abs(diff_angle)) / np.pi
  72. valid_mask = np.ones(previous_obs.shape[0])
  73. valid_mask[np.where(previous_obs[:, 4] < 0)] = 0
  74. iou_matrix = iou_batch(detections, trackers)
  75. scores = np.repeat(
  76. detections[:, -1][:, np.newaxis], trackers.shape[0], axis=1)
  77. # iou_matrix = iou_matrix * scores # a trick sometiems works, we don't encourage this
  78. valid_mask = np.repeat(valid_mask[:, np.newaxis], X.shape[1], axis=1)
  79. angle_diff_cost = (valid_mask * diff_angle) * vdc_weight
  80. angle_diff_cost = angle_diff_cost.T
  81. angle_diff_cost = angle_diff_cost * scores
  82. if min(iou_matrix.shape) > 0:
  83. a = (iou_matrix > iou_threshold).astype(np.int32)
  84. if a.sum(1).max() == 1 and a.sum(0).max() == 1:
  85. matched_indices = np.stack(np.where(a), axis=1)
  86. else:
  87. matched_indices = linear_assignment(-(iou_matrix + angle_diff_cost))
  88. else:
  89. matched_indices = np.empty(shape=(0, 2))
  90. unmatched_detections = []
  91. for d, det in enumerate(detections):
  92. if (d not in matched_indices[:, 0]):
  93. unmatched_detections.append(d)
  94. unmatched_trackers = []
  95. for t, trk in enumerate(trackers):
  96. if (t not in matched_indices[:, 1]):
  97. unmatched_trackers.append(t)
  98. # filter out matched with low IOU
  99. matches = []
  100. for m in matched_indices:
  101. if (iou_matrix[m[0], m[1]] < iou_threshold):
  102. unmatched_detections.append(m[0])
  103. unmatched_trackers.append(m[1])
  104. else:
  105. matches.append(m.reshape(1, 2))
  106. if (len(matches) == 0):
  107. matches = np.empty((0, 2), dtype=int)
  108. else:
  109. matches = np.concatenate(matches, axis=0)
  110. return matches, np.array(unmatched_detections), np.array(unmatched_trackers)
  111. def associate_only_iou(detections, trackers, iou_threshold):
  112. if (len(trackers) == 0):
  113. return np.empty(
  114. (0, 2), dtype=int), np.arange(len(detections)), np.empty(
  115. (0, 5), dtype=int)
  116. iou_matrix = iou_batch(detections, trackers)
  117. if min(iou_matrix.shape) > 0:
  118. a = (iou_matrix > iou_threshold).astype(np.int32)
  119. if a.sum(1).max() == 1 and a.sum(0).max() == 1:
  120. matched_indices = np.stack(np.where(a), axis=1)
  121. else:
  122. matched_indices = linear_assignment(-iou_matrix)
  123. else:
  124. matched_indices = np.empty(shape=(0, 2))
  125. unmatched_detections = []
  126. for d, det in enumerate(detections):
  127. if (d not in matched_indices[:, 0]):
  128. unmatched_detections.append(d)
  129. unmatched_trackers = []
  130. for t, trk in enumerate(trackers):
  131. if (t not in matched_indices[:, 1]):
  132. unmatched_trackers.append(t)
  133. # filter out matched with low IOU
  134. matches = []
  135. for m in matched_indices:
  136. if (iou_matrix[m[0], m[1]] < iou_threshold):
  137. unmatched_detections.append(m[0])
  138. unmatched_trackers.append(m[1])
  139. else:
  140. matches.append(m.reshape(1, 2))
  141. if (len(matches) == 0):
  142. matches = np.empty((0, 2), dtype=int)
  143. else:
  144. matches = np.concatenate(matches, axis=0)
  145. return matches, np.array(unmatched_detections), np.array(unmatched_trackers)