@@ -601,9 +601,14 @@ def __init__(self, observations: list[list[Detection]]):
601601
602602 # Metadata
603603 self ._num_frames = len (observations )
604- self ._median_observation = int (np .median ([len (x ) for x in observations ]))
605- # Add 0.5 to do proper rounding with int cast
606- self ._avg_observation = int (np .mean ([len (x ) for x in observations ]) + 0.5 )
604+ # Handle empty observation list edge case
605+ if len (observations ) == 0 :
606+ self ._median_observation = 0
607+ self ._avg_observation = 0
608+ else :
609+ self ._median_observation = int (np .median ([len (x ) for x in observations ]))
610+ # Add 0.5 to do proper rounding with int cast
611+ self ._avg_observation = int (np .mean ([len (x ) for x in observations ]) + 0.5 )
607612 self ._tracklet_gen_method = None
608613 self ._tracklet_stitch_method = None
609614
@@ -786,6 +791,10 @@ def get_embed_centers(self):
786791 longterm_ids = np .asarray (list (set (self ._stitch_translation .values ())))
787792 longterm_ids = longterm_ids [longterm_ids != 0 ]
788793
794+ # Handle edge case where all longterm IDs are 0 (filtered out)
795+ if len (longterm_ids ) == 0 :
796+ return np .zeros ([0 , embedding_shape [0 ]])
797+
789798 # To calculate an average for merged tracklets, we weight by number of frames
790799 longterm_data = {}
791800 for cur_tracklet in self ._tracklets :
@@ -841,8 +850,12 @@ def _make_tracklets(self, include_unassigned: bool = True):
841850 for tracklet_id , observation_list in tracklet_dict .items ():
842851 tracklet_list .append (Tracklet (tracklet_id , observation_list ))
843852
844- if include_unassigned :
845- cur_tracklet_id = np .max (np .asarray (list (tracklet_dict .keys ())))
853+ if include_unassigned and len (unmatched_observations ) > 0 :
854+ # Handle edge case where tracklet_dict is empty
855+ if len (tracklet_dict ) > 0 :
856+ cur_tracklet_id = np .max (np .asarray (list (tracklet_dict .keys ())))
857+ else :
858+ cur_tracklet_id = 0
846859 for cur_observation in unmatched_observations :
847860 tracklet_list .append (Tracklet (int (cur_tracklet_id ), [cur_observation ]))
848861 cur_tracklet_id += 1
@@ -1079,35 +1092,42 @@ def generate_greedy_tracklets(
10791092 if num_threads > 1 :
10801093 self ._start_pool (num_threads )
10811094
1082- # Main loop to cycle over greedy matching.
1083- # Each match problem is posed as a bipartite graph between sequential frames
1084- for frame in np .arange (len (self ._observations ) - 1 ) + 1 :
1085- # Cache the segmentation and rotation data
1086- for obs in self ._observations [frame - 1 ]:
1087- obs .cache ()
1088- for obs in self ._observations [frame ]:
1089- obs .cache ()
1090- # Calculate cost and greedily match
1091- match_costs = self ._calculate_costs (frame - 1 , frame , rotate_pose )
1092- match_costs = np .ma .array (match_costs , fill_value = max_cost , mask = False )
1093- matches = {}
1094- while np .any (~ match_costs .mask ) and np .any (match_costs .filled () < max_cost ):
1095- next_best = np .unravel_index (np .argmin (match_costs ), match_costs .shape )
1096- matches [next_best [1 ]] = prev_matches [next_best [0 ]]
1097- match_costs .mask [next_best [0 ], :] = True
1098- match_costs .mask [:, next_best [1 ]] = True
1099- # Fill any unmatched observations
1100- for j in range (len (self ._observations [frame ])):
1101- if j not in matches :
1102- matches [j ] = cur_tracklet_id
1103- cur_tracklet_id += 1
1104- frame_dict [frame ] = matches
1105- # Cleanup for next loop iteration
1106- for cur_obs in self ._observations [frame - 1 ]:
1107- cur_obs .clear_cache ()
1108- prev_matches = matches
1109- if self ._pool is not None :
1110- self ._kill_pool ()
1095+ try :
1096+ # Main loop to cycle over greedy matching.
1097+ # Each match problem is posed as a bipartite graph between sequential frames
1098+ for frame in np .arange (len (self ._observations ) - 1 ) + 1 :
1099+ # Cache the segmentation and rotation data
1100+ for obs in self ._observations [frame - 1 ]:
1101+ obs .cache ()
1102+ for obs in self ._observations [frame ]:
1103+ obs .cache ()
1104+ # Calculate cost and greedily match
1105+ match_costs = self ._calculate_costs (frame - 1 , frame , rotate_pose )
1106+ match_costs = np .ma .array (match_costs , fill_value = max_cost , mask = False )
1107+ matches = {}
1108+ while np .any (~ match_costs .mask ) and np .any (
1109+ match_costs .filled () < max_cost
1110+ ):
1111+ next_best = np .unravel_index (
1112+ np .argmin (match_costs ), match_costs .shape
1113+ )
1114+ matches [next_best [1 ]] = prev_matches [next_best [0 ]]
1115+ match_costs .mask [next_best [0 ], :] = True
1116+ match_costs .mask [:, next_best [1 ]] = True
1117+ # Fill any unmatched observations
1118+ for j in range (len (self ._observations [frame ])):
1119+ if j not in matches :
1120+ matches [j ] = cur_tracklet_id
1121+ cur_tracklet_id += 1
1122+ frame_dict [frame ] = matches
1123+ # Cleanup for next loop iteration
1124+ for cur_obs in self ._observations [frame - 1 ]:
1125+ cur_obs .clear_cache ()
1126+ prev_matches = matches
1127+ finally :
1128+ # Ensure pool is always cleaned up, even if an exception occurs
1129+ if self ._pool is not None :
1130+ self ._kill_pool ()
11111131 # Final modification of internal state
11121132 self ._observation_id_dict = frame_dict
11131133 self ._tracklet_gen_method = "greedy"
0 commit comments