Skip to content

Commit

Permalink
Add option to only use alive objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Agustín Castro committed Mar 8, 2024
1 parent 93340cf commit cdd3e58
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 75 deletions.
116 changes: 61 additions & 55 deletions demos/multi_camera/src/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,24 @@ def draw_feet(
for cluster in clusters:
color = Palette.choose_color(cluster.id)
cluster_center = 0
cluster_is_alive = False
for tracked_object in cluster.tracked_objects.values():
point = get_absolute_feet(tracked_object)
if transformation_in_reference is not None:
point = transformation_in_reference.abs_to_rel(np.array([point]))[0]

cluster_center += point
frame = Drawer.circle(
frame,
tuple(point.astype(int)),
radius=radius,
color=color,
thickness=thickness,
)
if tracked_object.live_points.any():
cluster_is_alive = True
point = get_absolute_feet(tracked_object)
if transformation_in_reference is not None:
point = transformation_in_reference.abs_to_rel(np.array([point]))[0]

cluster_center += point
frame = Drawer.circle(
frame,
tuple(point.astype(int)),
radius=radius,
color=color,
thickness=thickness,
)

if draw_cluster_ids:
if draw_cluster_ids and cluster_is_alive:
cluster_center /= len(cluster.tracked_objects)
frame = Drawer.text(
frame,
Expand All @@ -127,41 +130,42 @@ def draw_cluster_bboxes(
for cluster in clusters:
color = Palette.choose_color(cluster.id)
for path, tracked_object in cluster.tracked_objects.items():
frame = images[path]

if thickness is None:
current_thickness = max(int(max(frame.shape) / 500), 1)
else:
current_thickness = thickness

# draw the bbox
points = tracked_object.estimate.astype(int)
frame = Drawer.rectangle(
frame,
tuple(points),
color=color,
thickness=current_thickness,
)

if draw_cluster_ids:
text = f"{cluster.id}"
if tracked_object.live_points.any():
frame = images[path]

# the anchor will become the bottom-left of the text,
# we select-top left of the bbox compensating for the thickness of the box
text_anchor = (
points[0, 0] - current_thickness // 2,
points[0, 1] - current_thickness // 2 - 1,
)
if thickness is None:
current_thickness = max(int(max(frame.shape) / 500), 1)
else:
current_thickness = thickness

frame = Drawer.text(
# draw the bbox
points = tracked_object.estimate.astype(int)
frame = Drawer.rectangle(
frame,
text,
position=text_anchor,
size=text_size,
tuple(points),
color=color,
thickness=text_thickness,
thickness=current_thickness,
)
images[path] = frame

if draw_cluster_ids:
text = f"{cluster.id}"

# the anchor will become the bottom-left of the text,
# we select-top left of the bbox compensating for the thickness of the box
text_anchor = (
points[0, 0] - current_thickness // 2,
points[0, 1] - current_thickness // 2 - 1,
)

frame = Drawer.text(
frame,
text,
position=text_anchor,
size=text_size,
color=color,
thickness=text_thickness,
)
images[path] = frame
return images


Expand Down Expand Up @@ -272,22 +276,22 @@ def run():
default=0.2,
)
parser.add_argument(
"--distance-threshold",
"--foot-distance-threshold",
type=float,
default=1.5,
help="Maximum distance to consider when matching detections and tracked objects",
default=0.2,
help="Maximum spatial distance that two tracked objects of different videos can have in order to match",
)
parser.add_argument(
"--foot-distance-threshold",
"--reid-embedding-correlation-threshold",
type=float,
default=0.1,
help="Maximum spatial distance that two tracked objects of different videos can have in order to match",
default=0.5,
help="Threshold for embedding match during a reid phase after object has been lost. (The 1-correlation distance we use is bounded in [0, 2])",
)
parser.add_argument(
"--embedding-correlation-threshold",
type=float,
default=0.9,
help="Threshold for embedding match.",
default=1,
help="Threshold for embedding match. (The 1-correlation distance we use is bounded in [0, 2]",
)
parser.add_argument(
"--max-votes-grow",
Expand Down Expand Up @@ -340,8 +344,8 @@ def run():
parser.add_argument(
"--reid-hit-counter-max",
type=int,
default=150,
help="Maximum amount of frames trying to reidentify the object",
default=300,
help="Maximum amount of frames trying to reidentify the object. (Use a value >=0)",
)
parser.add_argument(
"--nms-threshold", type=float, help="Iou threshold for detector", default=0.15
Expand Down Expand Up @@ -509,14 +513,15 @@ def conditional_embedding_to_spatial(detection, tracked_object):
trackers[path] = Tracker(
distance_function=distance_functions[path],
detection_threshold=args.confidence_threshold,
distance_threshold=args.distance_threshold,
distance_threshold=args.embedding_correlation_threshold,
initialization_delay=args.initialization_delay,
hit_counter_max=args.hit_counter_max,
camera_name=path,
past_detections_length=10,
reid_distance_function=embedding_distance,
reid_distance_threshold=0.5,
reid_distance_threshold=args.reid_embedding_correlation_threshold,
reid_hit_counter_max=args.reid_hit_counter_max,
pointwise_hit_counter_max=2,
)
tracked_objects[path] = []

Expand Down Expand Up @@ -557,6 +562,7 @@ def clusterizer_distance(tracker1, tracker2):
memory=args.memory,
initialization_delay=args.clusterizer_initialization_delay,
reid_hit_counter_max=args.reid_hit_counter_max,
use_only_living_trackers=False,
)

while True:
Expand Down
56 changes: 36 additions & 20 deletions norfair/multi_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ def cluster_intersection_matrix(current_clusters, clusters):


def generate_current_clusters(
trackers_by_camera, distance_function, distance_threshold, join_distance_by="mean"
trackers_by_camera,
distance_function,
distance_threshold,
join_distance_by="mean",
use_only_living_trackers=False,
):

# In case number of camera is variable, I will redefine the distance function
Expand All @@ -141,6 +145,10 @@ def generate_current_clusters(

current_clusters = flatten_list(trackers_by_camera)

# use only alive trackers:
if use_only_living_trackers:
current_clusters = [obj for obj in current_clusters if obj.live_points.any()]

if len(current_clusters) > 0:
distance_matrix = (
np.zeros((len(current_clusters), len(current_clusters)))
Expand Down Expand Up @@ -399,24 +407,26 @@ def remove_current_cluster_from_clusters(
def swap_cluster_ids(clusters, cluster_number, cluster_number_with_oldest_tracker):

old_cluster = clusters[cluster_number_with_oldest_tracker]
old_cluster_id = old_cluster.id
old_cluster_fake_id = old_cluster.fake_id
old_cluster_age = old_cluster.age

cluster = clusters[cluster_number]
cluster_id = cluster.id
cluster_fake_id = cluster.fake_id
cluster_age = cluster.age

cluster.id = old_cluster_id
cluster.fake_id = old_cluster_fake_id
cluster.age = old_cluster_age
old_cluster.id = cluster_id
old_cluster.fake_id = cluster_fake_id
old_cluster.age = cluster_age
if old_cluster_fake_id < cluster_fake_id:
old_cluster_age = old_cluster.age
old_cluster_id = old_cluster.id

cluster_age = cluster.age
cluster_id = cluster.id
cluster.id = old_cluster_id
cluster.fake_id = old_cluster_fake_id
cluster.age = old_cluster_age
old_cluster.id = cluster_id
old_cluster.fake_id = cluster_fake_id
old_cluster.age = cluster_age

clusters[cluster_number_with_oldest_tracker] = old_cluster
clusters[cluster_number] = cluster
clusters[cluster_number_with_oldest_tracker] = old_cluster
clusters[cluster_number] = cluster
return clusters


Expand All @@ -431,6 +441,7 @@ def __init__(
memory: int = 3,
initialization_delay: int = 4,
reid_hit_counter_max: int = 0,
use_only_living_trackers: bool = False,
):
"""
Associate trackers from different cameras/videos.
Expand Down Expand Up @@ -467,6 +478,10 @@ def __init__(
- reid_hit_counter_max: int.
If doing reid in the tracking, then provide the reid_hit_counter_max so that the MultiCameraClusterizer instance knows
for how long to keep storing clusters of tracked objects that have dissapeared.
- use_only_living_trackers: bool.
Filter tracked objects that have no alive points. This can be useful since tracked objects that are not alive might have
position that will not match well with their position in a different camera.
"""
if max_votes_grow < 1:
raise ValueError("max_votes_grow parameter needs to be >= 1")
Expand Down Expand Up @@ -500,6 +515,7 @@ def __init__(
self.initialization_delay = initialization_delay + max_votes_grow

self.reid_hit_counter_max = reid_hit_counter_max + 1
self.use_only_living_trackers = use_only_living_trackers

def update(self, trackers_by_camera):

Expand All @@ -521,6 +537,7 @@ def update(self, trackers_by_camera):
self.distance_function,
self.distance_threshold,
self.join_distance_by,
self.use_only_living_trackers,
)
self.past_clusters.insert(0, deepcopy(current_clusters))

Expand Down Expand Up @@ -591,13 +608,12 @@ def update(self, trackers_by_camera):

self.clusters[cluster_number] = cluster

# keep the id of the cluster with the oldest object
if cluster_number not in cluster_numbers_with_oldest_tracker:
self.clusters = swap_cluster_ids(
self.clusters,
cluster_number,
cluster_numbers_with_oldest_tracker[0],
)
# keep the smallest id with the oldest object
self.clusters = swap_cluster_ids(
self.clusters,
cluster_number,
np.array(cluster_numbers_with_oldest_tracker).min(),
)

# update the matrix of intersections so that the current cluster is now contained in self.clusters[cluster_number]
intersection_matrix_ids[cluster_number][
Expand Down

0 comments on commit cdd3e58

Please sign in to comment.