1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Appearance descriptors and the candidate type the tracker associates over.
use ;
/// Length of the model-free appearance descriptor the interim
/// [`HistogramAppearance`] produces. Small and fixed so it is cheap to compare
/// and carry; a learned embedding would pick its own length behind the trait.
pub const APPEARANCE_DIM: usize = 16;
/// An appearance descriptor for one candidate box.
///
/// This is a normalized feature vector: the model-free interim builds it from
/// coarse colour/gradient statistics of the box pixels; a learned re-id model
/// would emit an L2-normalized embedding of the same shape. It is compared with
/// [`AppearanceModel::similarity`], not by raw equality, so two descriptors of
/// the *same* object (under jitter / lighting change) score high and two
/// *different* objects score low.
/// Pluggable appearance backend.
///
/// `extract` turns a candidate box (its pixels, in a real impl) into an
/// [`Appearance`]; `similarity` scores two descriptors in `0..=1` (1 = identical
/// appearance, 0 = unrelated). The tracker depends only on this trait, so the
/// model-free [`HistogramAppearance`] shipped here can be swapped for a learned
/// re-id / siamese embedding with no tracker change: the new model
/// just produces longer descriptors and a learned similarity.
///
/// The default `extract` returns `None` (no pixels available, the pure /
/// synthetic path), so a tracker can run appearance-aware on descriptors handed
/// to it directly (the harness) while a deployment plugs in a real extractor.
/// The model-free interim appearance backend: cosine similarity over the
/// descriptor vectors.
///
/// It does NOT extract from pixels (a real deployment swaps in a re-id model for
/// that); it scores descriptors the caller already holds. Cosine
/// similarity is scale-invariant and maps cleanly onto `0..=1` after clamping,
/// so a colour/gradient histogram now and a learned embedding later both work
/// through the same `similarity`. Two equal vectors score 1.0; orthogonal ones
/// score 0.0.
;
/// Cosine similarity clamped to `0..=1` (negative correlation reads as "no
/// match", not as anti-match). A zero-magnitude vector yields 0.
pub
/// One candidate detection for a frame, optionally carrying its appearance
/// descriptor.
///
/// The motion-only [`SingleObjectTracker::update`](super::SingleObjectTracker::update) path wraps each wire
/// [`Detection`] with `appearance: None`; the appearance-aware
/// [`SingleObjectTracker::update_with_appearance`](super::SingleObjectTracker::update_with_appearance) path carries a descriptor per
/// candidate (extracted from pixels in a real deployment, supplied directly by
/// the synthetic harness). The descriptor never reaches the wire, only the
/// resulting lock-state / association confidence does.