Skip to content

Commit 529fe18

Browse files
authored
Greedy assignment for person tracking. (#767)
* Skeleton for abstract base class Tracker * Creating updateTracks() which cleans up track store based on track age. Also, updating property names to be mixed case instead of using underscores * Basic formatting updates. * Adding lowerCamelCase for all variables and a couple more formatting updates * Creating tracker_utils.ts to validate tracker config. * Making computeSimilarity() abstract to allow for batch implementations. Also fixing some comments and validation checks * Updating flow so that tracks are filtered by age prior to assignment with new detections * Updating the documentation for maxTracks to indicate that this should be set higher than maxPoses * Moving track filtering before the computation of similarity matrices * Implementing assignTracks(), which is a greedy assigner. * Merge remote-tracking branch 'upstream/master' into tracker, and add new tracking changes. * Fixing linting issues. * Updating assignTracks so that unmatched detections are kept track of
1 parent 26ca54a commit 529fe18

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

pose-detection/src/calculators/interfaces/config_interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,6 @@ export interface TrackerConfig {
8181
// like to recover people that are not detected for long
8282
// stretches of time (at the cost of potential false
8383
// re-identifications).
84+
minSimilarity: number; // New poses will only be linked with tracks if the
85+
// similarity score exceeds this threshold.
8486
}

pose-detection/src/calculators/tracker.ts

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ export abstract class Tracker {
2929
private tracks: Track[];
3030
private readonly maxTracks: number;
3131
private readonly maxAge: number;
32+
private readonly minSimilarity: number;
33+
private nextID: number;
3234

3335
constructor(config: TrackerConfig) {
3436
validateTrackerConfig(config);
3537
this.maxTracks = config.maxTracks;
3638
this.maxAge = config.maxAge;
39+
this.minSimilarity = config.minSimilarity;
40+
this.nextID = 1;
3741
}
3842

3943
/**
@@ -72,17 +76,60 @@ export abstract class Tracker {
7276
}
7377

7478
/**
75-
* Performs an optimization to link detections with tracks. The `poses`
79+
* Performs a greedy optimization to link detections with tracks. The `poses`
7680
* array is updated in place by providing an `id` property. If incoming
7781
* detections are not linked with existing tracks, new tracks will be created.
78-
* @param poses An array of detected `Pose's.
82+
* @param poses An array of detected `Pose's. It's assumed that poses are
83+
* sorted from most confident to least confident.
7984
* @param simMatrix A 2D array of shape [num_det, num_tracks] with pairwise
8085
* similarity scores between detections and tracks.
8186
* @param timestamp The current timestamp in milliseconds.
8287
*/
8388
assignTracks(
8489
poses: Pose[], simMatrix: number[][], timestamp: number): void {
85-
//TODO: Implement optimization and track store mechanics.
90+
const unmatchedTrackIndices = Array.from(Array(simMatrix[0].length).keys());
91+
const detectionIndices = Array.from(Array(poses.length).keys());
92+
const unmatchedDetectionIndices: number[] = [];
93+
94+
for (const detectionIndex of detectionIndices) {
95+
if (unmatchedTrackIndices.length === 0) {
96+
break;
97+
}
98+
99+
// Assign the detection to the track which produces the highest pairwise
100+
// similarity score, assuming the score exceeds the minimum similarity
101+
// threshold.
102+
let maxTrackIndex = -1;
103+
let maxSimilarity = -1;
104+
for (const trackIndex of unmatchedTrackIndices) {
105+
const similarity = simMatrix[detectionIndex][trackIndex];
106+
if (similarity >= this.minSimilarity && similarity > maxSimilarity) {
107+
maxTrackIndex = trackIndex;
108+
maxSimilarity = similarity;
109+
}
110+
}
111+
if (maxTrackIndex >= 0) {
112+
// Link the detection with the highest scoring track.
113+
this.tracks[maxTrackIndex].lastTimestamp = timestamp;
114+
this.tracks[maxTrackIndex].keypoints = (
115+
poses[detectionIndex].keypoints.slice());
116+
const index = unmatchedTrackIndices.indexOf(maxTrackIndex);
117+
unmatchedTrackIndices.splice(index, 1);
118+
} else {
119+
unmatchedDetectionIndices.push(detectionIndex);
120+
}
121+
}
122+
123+
// Spawn new tracks for all unmatched detections.
124+
for (const detectionIndex of unmatchedDetectionIndices) {
125+
const newID = this.nextTrackID();
126+
const newTrack: Track = {
127+
id: newID,
128+
lastTimestamp: timestamp,
129+
keypoints: poses[detectionIndex].keypoints.slice()
130+
};
131+
this.tracks.push(newTrack);
132+
}
86133
}
87134

88135
/**
@@ -102,6 +149,15 @@ export abstract class Tracker {
102149
this.tracks = this.tracks.slice(0, this.maxTracks);
103150
}
104151

152+
/**
153+
* Returns the next free track ID.
154+
*/
155+
nextTrackID() {
156+
const nextID = this.nextID;
157+
this.nextID += 1;
158+
return nextID;
159+
}
160+
105161
/**
106162
* Removes specific tracks, based on their ids.
107163
*/

0 commit comments

Comments
 (0)