group by label before tracking objects

This commit is contained in:
Blake Blackshear 2020-01-09 06:52:28 -06:00
parent 32b212c7b6
commit be5a114f6a

View File

@ -251,6 +251,7 @@ class ObjectTracker(threading.Thread):
def register(self, index, obj): def register(self, index, obj):
id = f"{str(obj['frame_time'])}-{index}" id = f"{str(obj['frame_time'])}-{index}"
obj['id'] = id
self.tracked_objects[id] = obj self.tracked_objects[id] = obj
self.disappeared[id] = 0 self.disappeared[id] = 0
@ -281,95 +282,105 @@ class ObjectTracker(threading.Thread):
# to update # to update
return return
# compute centroids # group by name
new_object_groups = defaultdict(lambda: [])
for obj in new_objects: for obj in new_objects:
centroid_x = int((obj['box']['xmin']+obj['box']['xmax']) / 2.0) new_object_groups[obj['name']].append(obj)
centroid_y = int((obj['box']['ymin']+obj['box']['ymax']) / 2.0)
obj['centroid'] = (centroid_x, centroid_y)
if len(self.tracked_objects) == 0: # track objects for each label type
for index, obj in enumerate(new_objects): # TODO: this is going to miss deregistering objects that are not in the new groups
self.register(index, obj) for label, group in new_object_groups.items():
return current_objects = [o for o in self.tracked_objects.values() if o['name'] == label]
current_ids = [o['id'] for o in current_objects]
current_centroids = np.array([o['centroid'] for o in current_objects])
new_centroids = np.array([o['centroid'] for o in new_objects]) # compute centroids
current_ids = list(self.tracked_objects.keys()) for obj in group:
current_centroids = np.array([o['centroid'] for o in self.tracked_objects.values()]) centroid_x = int((obj['box']['xmin']+obj['box']['xmax']) / 2.0)
centroid_y = int((obj['box']['ymin']+obj['box']['ymax']) / 2.0)
obj['centroid'] = (centroid_x, centroid_y)
# compute the distance between each pair of tracked if len(current_objects) == 0:
# centroids and new centroids, respectively -- our for index, obj in enumerate(group):
# goal will be to match each new centroid to an existing self.register(index, obj)
# object centroid return
D = dist.cdist(current_centroids, new_centroids)
# in order to perform this matching we must (1) find the new_centroids = np.array([o['centroid'] for o in group])
# smallest value in each row and then (2) sort the row
# indexes based on their minimum values so that the row
# with the smallest value is at the *front* of the index
# list
rows = D.min(axis=1).argsort()
# next, we perform a similar process on the columns by # compute the distance between each pair of tracked
# finding the smallest value in each column and then # centroids and new centroids, respectively -- our
# sorting using the previously computed row index list # goal will be to match each new centroid to an existing
cols = D.argmin(axis=1)[rows] # object centroid
D = dist.cdist(current_centroids, new_centroids)
# in order to determine if we need to update, register, # in order to perform this matching we must (1) find the
# or deregister an object we need to keep track of which # smallest value in each row and then (2) sort the row
# of the rows and column indexes we have already examined # indexes based on their minimum values so that the row
usedRows = set() # with the smallest value is at the *front* of the index
usedCols = set() # list
rows = D.min(axis=1).argsort()
# loop over the combination of the (row, column) index # next, we perform a similar process on the columns by
# tuples # finding the smallest value in each column and then
for (row, col) in zip(rows, cols): # sorting using the previously computed row index list
# if we have already examined either the row or cols = D.argmin(axis=1)[rows]
# column value before, ignore it
# val
if row in usedRows or col in usedCols:
continue
# otherwise, grab the object ID for the current row, # in order to determine if we need to update, register,
# set its new centroid, and reset the disappeared # or deregister an object we need to keep track of which
# counter # of the rows and column indexes we have already examined
objectID = current_ids[row] usedRows = set()
self.update(objectID, new_objects[col]) usedCols = set()
self.disappeared[objectID] = 0
# indicate that we have examined each of the row and # loop over the combination of the (row, column) index
# column indexes, respectively # tuples
usedRows.add(row) for (row, col) in zip(rows, cols):
usedCols.add(col) # if we have already examined either the row or
# column value before, ignore it
# val
if row in usedRows or col in usedCols:
continue
# compute both the row and column index we have NOT yet # otherwise, grab the object ID for the current row,
# examined # set its new centroid, and reset the disappeared
unusedRows = set(range(0, D.shape[0])).difference(usedRows) # counter
unusedCols = set(range(0, D.shape[1])).difference(usedCols)
# in the event that the number of object centroids is
# equal or greater than the number of input centroids
# we need to check and see if some of these objects have
# potentially disappeared
if D.shape[0] >= D.shape[1]:
# loop over the unused row indexes
for row in unusedRows:
# grab the object ID for the corresponding row
# index and increment the disappeared counter
objectID = current_ids[row] objectID = current_ids[row]
self.disappeared[objectID] += 1 self.update(objectID, new_objects[col])
self.disappeared[objectID] = 0
# check to see if the number of consecutive # indicate that we have examined each of the row and
# frames the object has been marked "disappeared" # column indexes, respectively
# for warrants deregistering the object usedRows.add(row)
if self.disappeared[objectID] > self.max_disappeared: usedCols.add(col)
self.deregister(objectID)
# otherwise, if the number of input centroids is greater # compute both the row and column index we have NOT yet
# than the number of existing object centroids we need to # examined
# register each new input centroid as a trackable object unusedRows = set(range(0, D.shape[0])).difference(usedRows)
else: unusedCols = set(range(0, D.shape[1])).difference(usedCols)
for col in unusedCols:
self.register(col, new_objects[col]) # in the event that the number of object centroids is
# equal or greater than the number of input centroids
# we need to check and see if some of these objects have
# potentially disappeared
if D.shape[0] >= D.shape[1]:
# loop over the unused row indexes
for row in unusedRows:
# grab the object ID for the corresponding row
# index and increment the disappeared counter
objectID = current_ids[row]
self.disappeared[objectID] += 1
# check to see if the number of consecutive
# frames the object has been marked "disappeared"
# for warrants deregistering the object
if self.disappeared[objectID] > self.max_disappeared:
self.deregister(objectID)
# otherwise, if the number of input centroids is greater
# than the number of existing object centroids we need to
# register each new input centroid as a trackable object
else:
for col in unusedCols:
self.register(col, new_objects[col])
# Maintains the frame and object with the highest score # Maintains the frame and object with the highest score
class BestFrames(threading.Thread): class BestFrames(threading.Thread):