@@ -102,6 +102,29 @@ def embedding_distance(tracks, detections, metric='cosine'):
102102 raise NotImplementedError
103103 return cost_matrix
104104
105+ def nearest_embedding_distance (tracks , detections , metric = 'cosine' ):
106+ """
107+ different from embedding distance, this func calculate the
108+ nearest distance among all track history features and detections
109+
110+ tracks: list[STrack]
111+ detections: list[STrack]
112+ metric: str, cosine or euclidean
113+
114+ return:
115+ cost_matrix, np.ndarray, shape(len(tracks), len(detections))
116+ """
117+ cost_matrix = np .zeros ((len (tracks ), len (detections )))
118+ det_features = np .asarray ([det .features [- 1 ] for det in detections ])
119+
120+ for row , track in enumerate (tracks ):
121+ track_history_features = np .asarray (track .features )
122+ dist = 1. - cal_cosine_distance (track_history_features , det_features )
123+ dist = dist .min (axis = 0 )
124+ cost_matrix [row , :] = dist
125+
126+ return cost_matrix
127+
105128def ecu_iou_distance (tracks , detections , img0_shape ):
106129 """
107130 combine eculidian center-point distance and iou distance
@@ -189,90 +212,70 @@ def fuse_motion(kf, cost_matrix, tracks, detections, only_position=False, lambda
189212 return cost_matrix
190213
191214
192- """
193- distance metric that combines multi-frame info
194- used in StrongSORT
195- TODO: use in DeepSORT
196- """
197-
198- class NearestNeighborDistanceMetric (object ):
215+ def matching_cascade (
216+ distance_metric , matching_thresh , cascade_depth , tracks , detections ,
217+ track_indices = None , detection_indices = None ):
199218 """
200- A nearest neighbor distance metric that, for each target, returns
201- the closest distance to any sample that has been observed so far.
202-
203- Parameters
204- ----------
205- metric : str
206- Either "euclidean" or "cosine".
207- matching_threshold: float
208- The matching threshold. Samples with larger distance are considered an
209- invalid match.
210- budget : Optional[int]
211- If not None, fix samples per class to at most this number. Removes
212- the oldest samples when the budget is reached.
213-
214- Attributes
215- ----------
216- samples : Dict[int -> List[ndarray]]
217- A dictionary that maps from target identities to the list of samples
218- that have been observed so far.
219-
219+ Run matching cascade in DeepSORT
220+
221+ distance_metirc: function that calculate the cost matrix
222+ matching_thresh: float, Associations with cost larger than this value are disregarded.
223+ cascade_path: int, equal to max_age of a tracklet
224+ tracks: List[STrack], current tracks
225+ detections: List[STrack], current detections
226+ track_indices: List[int], tracks that will be calculated, Default None
227+ detection_indices: List[int], detections that will be calculated, Default None
228+
229+ return:
230+ matched pair, unmatched tracks, unmatced detections: List[int], List[int], List[int]
220231 """
232+ if track_indices is None :
233+ track_indices = list (range (len (tracks )))
234+ if detection_indices is None :
235+ detection_indices = list (range (len (detections )))
221236
222- def __init__ (self , metric , matching_threshold , budget = None ):
223- if metric == "euclidean" :
224- self ._metric = cal_eculidian_distance
225- elif metric == "cosine" :
226- self ._metric = cal_cosine_distance
227- else :
228- raise ValueError (
229- "Invalid metric; must be either 'euclidean' or 'cosine'" )
230- self .matching_threshold = matching_threshold
231- self .budget = budget
232- self .samples = {}
233-
234- def partial_fit (self , features , targets , active_targets ):
235- """Update the distance metric with new data.
236-
237- Parameters
238- ----------
239- features : ndarray
240- An NxM matrix of N features of dimensionality M.
241- targets : ndarray
242- An integer array of associated target identities.
243- active_targets : List[int]
244- A list of targets that are currently present in the scene.
237+ detections_to_match = detection_indices
238+ matches = []
245239
240+ for level in range (cascade_depth ):
246241 """
247- for feature , target in zip (features , targets ):
248- self .samples .setdefault (target , []).append (feature )
249- if self .budget is not None :
250- self .samples [target ] = self .samples [target ][- self .budget :]
251- self .samples = {k : self .samples [k ] for k in active_targets }
252-
253- def distance (self , features , targets ):
254- """Compute distance between features and targets.
255-
256- Parameters
257- ----------
258- features : ndarray
259- An NxM matrix of N features of dimensionality M.
260- targets : List[int]
261- A list of targets to match the given `features` against.
262-
263- Returns
264- -------
265- ndarray
266- Returns a cost matrix of shape len(targets), len(features), where
267- element (i, j) contains the closest squared distance between
268- `targets[i]` and `features[j]`.
269-
242+ match new track with detection firstly
270243 """
271- cost_matrix = np .zeros ((len (targets ), len (features )))
272- for i , target in enumerate (targets ):
273- cost_matrix [i , :] = self ._metric (self .samples [target ], features )
274- return cost_matrix
244+ if not len (detections_to_match ): # No detections left
245+ break
246+
247+ track_indices_l = [
248+ k for k in track_indices
249+ if tracks [k ].time_since_update == 1 + level
250+ ] # filter tracks whose age is equal to level + 1 (The age of Newest track = 1)
251+
252+ if not len (track_indices_l ): # Nothing to match at this level
253+ continue
254+
255+ # tracks and detections which will be mathcted in current level
256+ track_l = [tracks [idx ] for idx in track_indices_l ] # List[STrack]
257+ det_l = [detections [idx ] for idx in detections_to_match ] # List[STrack]
258+
259+ # calculate the cost matrix
260+ cost_matrix = distance_metric (track_l , det_l )
261+
262+ # solve the linear assignment problem
263+ matched_row_col , umatched_row , umatched_col = \
264+ linear_assignment (cost_matrix , matching_thresh )
265+
266+ for row , col in matched_row_col : # for those who matched
267+ matches .append ((track_indices_l [row ], detections_to_match [col ]))
268+
269+ umatched_detecion_l = [] # current detections not matched
270+ for col in umatched_col : # for detections not matched
271+ umatched_detecion_l .append (detections_to_match [col ])
272+
273+ detections_to_match = umatched_detecion_l # update detections to match for next level
274+ unmatched_tracks = list (set (track_indices ) - set (k for k , _ in matches ))
275+
276+ return matches , unmatched_tracks , detections_to_match
275277
278+
276279
277280"""
278281funcs to cal similarity, copied from UAVMOT
0 commit comments