ocsort_matching.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. bboxes2 = np.expand_dims(bboxes2, 0)
  21. bboxes1 = np.expand_dims(bboxes1, 1)
  22. xx1 = np.maximum(bboxes1[..., 0], bboxes2[..., 0])
  23. yy1 = np.maximum(bboxes1[..., 1], bboxes2[..., 1])
  24. xx2 = np.minimum(bboxes1[..., 2], bboxes2[..., 2])
  25. yy2 = np.minimum(bboxes1[..., 3], bboxes2[..., 3])
  26. w = np.maximum(0., xx2 - xx1)
  27. h = np.maximum(0., yy2 - yy1)
  28. area = w * h
  29. iou_matrix = area / ((bboxes1[..., 2] - bboxes1[..., 0]) *
  30. (bboxes1[..., 3] - bboxes1[..., 1]) +
  31. (bboxes2[..., 2] - bboxes2[..., 0]) *
  32. (bboxes2[..., 3] - bboxes2[..., 1]) - area)
  33. return iou_matrix
  34. def speed_direction_batch(dets, tracks):
  35. tracks = tracks[..., np.newaxis]
  36. CX1, CY1 = (dets[:, 0] + dets[:, 2]) / 2.0, (dets[:, 1] + dets[:, 3]) / 2.0
  37. CX2, CY2 = (tracks[:, 0] + tracks[:, 2]) / 2.0, (
  38. tracks[:, 1] + tracks[:, 3]) / 2.0
  39. dx = CX1 - CX2
  40. dy = CY1 - CY2
  41. norm = np.sqrt(dx**2 + dy**2) + 1e-6
  42. dx = dx / norm
  43. dy = dy / norm
  44. return dy, dx
  45. def linear_assignment(cost_matrix):
  46. try:
  47. import lap
  48. _, x, y = lap.lapjv(cost_matrix, extend_cost=True)
  49. return np.array([[y[i], i] for i in x if i >= 0])
  50. except ImportError:
  51. from scipy.optimize import linear_sum_assignment
  52. x, y = linear_sum_assignment(cost_matrix)
  53. return np.array(list(zip(x, y)))
  54. def associate(detections, trackers, iou_threshold, velocities, previous_obs,
  55. vdc_weight):
  56. if (len(trackers) == 0):
  57. return np.empty(
  58. (0, 2), dtype=int), np.arange(len(detections)), np.empty(
  59. (0, 5), dtype=int)
  60. Y, X = speed_direction_batch(detections, previous_obs)
  61. inertia_Y, inertia_X = velocities[:, 0], velocities[:, 1]
  62. inertia_Y = np.repeat(inertia_Y[:, np.newaxis], Y.shape[1], axis=1)
  63. inertia_X = np.repeat(inertia_X[:, np.newaxis], X.shape[1], axis=1)
  64. diff_angle_cos = inertia_X * X + inertia_Y * Y
  65. diff_angle_cos = np.clip(diff_angle_cos, a_min=-1, a_max=1)
  66. diff_angle = np.arccos(diff_angle_cos)
  67. diff_angle = (np.pi / 2.0 - np.abs(diff_angle)) / np.pi
  68. valid_mask = np.ones(previous_obs.shape[0])
  69. valid_mask[np.where(previous_obs[:, 4] < 0)] = 0
  70. iou_matrix = iou_batch(detections, trackers)
  71. scores = np.repeat(
  72. detections[:, -1][:, np.newaxis], trackers.shape[0], axis=1)
  73. # iou_matrix = iou_matrix * scores # a trick sometiems works, we don't encourage this
  74. valid_mask = np.repeat(valid_mask[:, np.newaxis], X.shape[1], axis=1)
  75. angle_diff_cost = (valid_mask * diff_angle) * vdc_weight
  76. angle_diff_cost = angle_diff_cost.T
  77. angle_diff_cost = angle_diff_cost * scores
  78. if min(iou_matrix.shape) > 0:
  79. a = (iou_matrix > iou_threshold).astype(np.int32)
  80. if a.sum(1).max() == 1 and a.sum(0).max() == 1:
  81. matched_indices = np.stack(np.where(a), axis=1)
  82. else:
  83. matched_indices = linear_assignment(-(iou_matrix + angle_diff_cost))
  84. else:
  85. matched_indices = np.empty(shape=(0, 2))
  86. unmatched_detections = []
  87. for d, det in enumerate(detections):
  88. if (d not in matched_indices[:, 0]):
  89. unmatched_detections.append(d)
  90. unmatched_trackers = []
  91. for t, trk in enumerate(trackers):
  92. if (t not in matched_indices[:, 1]):
  93. unmatched_trackers.append(t)
  94. # filter out matched with low IOU
  95. matches = []
  96. for m in matched_indices:
  97. if (iou_matrix[m[0], m[1]] < iou_threshold):
  98. unmatched_detections.append(m[0])
  99. unmatched_trackers.append(m[1])
  100. else:
  101. matches.append(m.reshape(1, 2))
  102. if (len(matches) == 0):
  103. matches = np.empty((0, 2), dtype=int)
  104. else:
  105. matches = np.concatenate(matches, axis=0)
  106. return matches, np.array(unmatched_detections), np.array(unmatched_trackers)
  107. def associate_only_iou(detections, trackers, iou_threshold):
  108. if (len(trackers) == 0):
  109. return np.empty(
  110. (0, 2), dtype=int), np.arange(len(detections)), np.empty(
  111. (0, 5), dtype=int)
  112. iou_matrix = iou_batch(detections, trackers)
  113. if min(iou_matrix.shape) > 0:
  114. a = (iou_matrix > iou_threshold).astype(np.int32)
  115. if a.sum(1).max() == 1 and a.sum(0).max() == 1:
  116. matched_indices = np.stack(np.where(a), axis=1)
  117. else:
  118. matched_indices = linear_assignment(-iou_matrix)
  119. else:
  120. matched_indices = np.empty(shape=(0, 2))
  121. unmatched_detections = []
  122. for d, det in enumerate(detections):
  123. if (d not in matched_indices[:, 0]):
  124. unmatched_detections.append(d)
  125. unmatched_trackers = []
  126. for t, trk in enumerate(trackers):
  127. if (t not in matched_indices[:, 1]):
  128. unmatched_trackers.append(t)
  129. # filter out matched with low IOU
  130. matches = []
  131. for m in matched_indices:
  132. if (iou_matrix[m[0], m[1]] < iou_threshold):
  133. unmatched_detections.append(m[0])
  134. unmatched_trackers.append(m[1])
  135. else:
  136. matches.append(m.reshape(1, 2))
  137. if (len(matches) == 0):
  138. matches = np.empty((0, 2), dtype=int)
  139. else:
  140. matches = np.concatenate(matches, axis=0)
  141. return matches, np.array(unmatched_detections), np.array(unmatched_trackers)