Holdfast
A tracker that keeps each object's identity when the picture breaks up.
Frames go missing, the feed freezes on a stale image, detections drop out, and the camera is moving the whole time. A tracker that trusts the picture hands out new IDs every time that happens. This one does not: on the worst input tested it holds 38.9 HOTA where the baseline manages 32.
- C++20
- Eigen
- ONNX Runtime
- WebAssembly
- CMake
$ ./holdfast --eval heavy # all of it, plus 30% dropout
HOTA
baseline 32.0
OC-SORT 14.5
holdfast 38.9
tracker 0.09 ms/frame the detector is the cost What it holds on to
VisDrone2019-MOT val, seven sequences from a moving drone camera, scored with TrackEval. Detections are ground truth plus seeded noise, so the table measures the tracker rather than the detector. HOTA, higher is better.
| input | baseline | OC-SORT | Holdfast |
|---|---|---|---|
| clean no degradation | 62.5 | 52.3 | 69.0 |
| blackout 15-30 frames lost every ~4 s | 42.9 | 37.2 | 50.5 |
| freeze a frame repeats for 10-20 | 49.4 | 41.2 | 59.5 |
| heavy all of it, plus 30% dropout | 32.0 | 14.5 | 38.9 |
Every decision was tuned on three sequences. The other four were held out and scored once. This is not a claim that Holdfast is a better general-purpose tracker — OC-SORT runs here without the camera-motion compensation its paper does not use, and most of the gap is that.
What each piece is actually buying
HOTA lost when one piece is removed from the full tracker. Each helps where it should and nowhere else — and the last row helps nowhere at all.
| removed | clean | blackout | freeze | heavy |
|---|---|---|---|---|
| camera-motion compensation | -6.5 | -3.6 | -4.2 | -4.2 |
| timestamp-based dt | 0.0 | -2.7 | 0.0 | -1.5 |
| recovery stage | -0.3 | -1.8 | -2.0 | -1.0 |
| stale-frame guard | 0.0 | 0.0 | -4.3 | -0.4 |
| OC-SORT re-update | -0.4 | -0.2 | +0.1 | +0.2 |
The OC-SORT re-update is neutral on this data. It is kept because it is cheap and has a unit test for the case it fixes, and it is reported as neutral rather than quietly left in the list of things that help.
One decision went the other way. A Mahalanobis gate on the first matching stage is the textbook move; measured, it cost about three HOTA, so it came out. Same seed gives byte-identical results, and macOS/Clang and Linux/GCC agree — the random distributions are implemented by hand, because the ones in the standard library are not portable between implementations.
The tracker itself is 0.09 ms a frame. End to end with a real detector it is 26 fps on one core and 39 on two, which says the detector is the thing to move to a GPU, not this.