feat: banded admission for the per-subject embedding store
AR-018 — an embedding joins a track's store only if its similarity to something already there falls inside a band, rather than merely being far from the gallery. Above the upper bound it is redundant: another look at a pose the store already covers, teaching the annex nothing while costing a slot a novel view could have used. Below the lower bound it is suspect: within one track every face is the same person by construction, so an embedding unlike everything else on the track is evidence that construction failed — a track-ID collision or a bad detection. Admitting it is exactly how an actor's annex gets poisoned with someone else's face. The old gate had only the upper half of that idea, expressed as a raw cosine against the gallery. Both bounds are now calibrated probabilities (AR-024), so the same number means the same thing here as in association and evidence weighting rather than three different things. This catches track-ID collisions EARLIER than the spread gate did — at the door rather than at promotion — so the buffer never becomes two-person in the first place. The spread gate stays as a second line for a track that drifts gradually instead of jumping. The existing test was asserting the mechanism rather than the outcome, so it was rewritten to assert what actually matters: whichever gate fires, the outsider must not reach the annex. Rejections are counted. A store that admits nothing is as broken as one that admits everything, and neither is visible otherwise. Band defaults 0.90-0.95 are working values pending VR-007; the two bounds fail in opposite directions and must be swept separately. Suite: 92 cases, 6133 assertions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: AR-018, AR-024 | SR-005
This commit is contained in:
+71
-18
@@ -133,9 +133,30 @@ Produce the exact input ArcFace expects.
|
||||
nose, left mouth, right mouth).
|
||||
- Alignment is the *only* geometric normalisation; no additional augmentation at
|
||||
inference.
|
||||
- **The transform is fitted by Umeyama's closed-form least squares over all five
|
||||
points**, which is what InsightFace uses (skimage's `SimilarityTransform` *is*
|
||||
`_umeyama`) and therefore what produced the crops ArcFace and LVFace were
|
||||
trained on. The canonical warp is part of the input distribution, not an
|
||||
implementation detail (AR-011).
|
||||
- **Not a robust estimator.** A RANSAC fit buys a small residual by discarding
|
||||
the landmarks that disagree with the model, and on a turned face those are the
|
||||
foreshortened ones — the signal AR-030 reads. With five points and a two-point
|
||||
minimal sample it also cannot separate a mis-detected landmark from honest
|
||||
out-of-plane rotation, so the robustness is nominal while the cost to AR-030 is
|
||||
total. It is RNG-driven besides, which made replay determinism a property of
|
||||
thread scheduling.
|
||||
|
||||
**Current:** `align_face()` in `src/face_utils.hpp:9-22`, `cv::warpAffine` to
|
||||
`{112, 112}`. **Gap:** none.
|
||||
**Current:** `align_face()` in `src/face_utils.hpp`, Umeyama fit via
|
||||
`umeyama_similarity()`, `cv::warpAffine` to `{112, 112}`. **Gap:** none.
|
||||
|
||||
> **Migration note.** Until this landed the fit was
|
||||
> `cv::estimateAffinePartial2D(…, cv::RANSAC, 3.0)`. Where RANSAC kept all five
|
||||
> points its final refit is the same least-squares optimum, so the two agree;
|
||||
> they diverge exactly where a landmark fell outside the 3 px band — i.e. on the
|
||||
> non-frontal faces. Galleries baked before this change therefore carry embeddings
|
||||
> from a marginally different warp, concentrated on the hardest views. Rebuilding
|
||||
> is cheap and removes the question; GR-004's embedder stamp does **not** catch an
|
||||
> aligner change, only a model change.
|
||||
|
||||
## AR-006 — Embedding
|
||||
|
||||
@@ -174,15 +195,43 @@ the same response.
|
||||
the crop is already scale-normalised, so a measure taken there cannot silently
|
||||
re-measure face size and double-count it against AR-002.
|
||||
- **Visibility** — extreme pose or occlusion means the face presents fewer of the
|
||||
features the embedding assumes are present. Derived from the **5-point
|
||||
landmarks AR-001 already emits** — nose offset from the eye midpoint over
|
||||
inter-ocular distance, plus eye/mouth-corner asymmetry — which are already
|
||||
computed, already used by AR-005, and already in the VR-001 dump, so the
|
||||
measure costs one arithmetic expression per face and can be studied on existing
|
||||
fixtures with no GPU. A dedicated landmark model (`models/2d106det.onnx` is
|
||||
present but referenced nowhere) is **not** adopted unless VR-012 shows the
|
||||
5-point proxy insufficient: an extra inference per detection is precisely the
|
||||
cost AR-011 says not to spend.
|
||||
features the embedding assumes are present. The measure is the **residual of
|
||||
the AR-005 alignment fit**: the RMS landmark error, in canonical 112×112
|
||||
pixels, left over after the best similarity transform onto the ArcFace
|
||||
template. It costs nothing — the transform is computed for the warp regardless,
|
||||
and the residual is what that fit could not explain.
|
||||
|
||||
Two properties earn it the job over an explicit yaw estimate:
|
||||
|
||||
- A similarity absorbs rotation, uniform scale and translation **exactly**,
|
||||
so the residual is by construction the non-similarity part of the
|
||||
deformation: out-of-plane rotation and foreshortening. In-plane roll
|
||||
contributes nothing, so "a tilted head reads as a turned one" is excluded
|
||||
structurally rather than by tuning. The destination frame is fixed, so face
|
||||
size cannot leak in either — that is AR-002's axis, and double-counting it
|
||||
would make a small frontal face look occluded.
|
||||
- It responds to **occlusion** and to plainly broken landmark sets, which an
|
||||
angle regressor by construction does not: a hand across the face is not a
|
||||
rotation, but it does displace landmarks.
|
||||
|
||||
Indicative magnitudes from a synthetic foreshortening sweep (`k ≈ cos yaw`):
|
||||
`k=1.0 → 0.00`, `0.9 → 1.18`, `0.75 → 3.11`, `0.5 → 6.72`, `0.3 → 9.85`
|
||||
canonical px. Smooth and monotone with a usable range; the mapping onto real
|
||||
faces is VR-012's to establish, and no threshold is set from these numbers.
|
||||
|
||||
Neither a dedicated landmark model (`models/2d106det.onnx` is present but
|
||||
referenced nowhere — and it emits points, not pose) nor a direct pose CNN is
|
||||
adopted unless VR-012 shows the residual insufficient. If one is needed the
|
||||
candidate is **6DRepNet** (MIT, RepVGG-B1g2, 3.47° MAE on AFLW2000) rather than
|
||||
Hopenet, which it dominates on accuracy, licence, recency and export
|
||||
friendliness. Two caveats to record before that happens: both are trained on
|
||||
**300W-LP**, which inherits research-only terms from 300W's constituent sets,
|
||||
and both want their own loosely-framed ROI rather than the ArcFace crop — a
|
||||
second warp and a second image in flight, which lands on AR-004's byte-based
|
||||
backpressure gap. It would also have to run **per track** — over the bounded
|
||||
view set AR-019's diversity buffer already keeps — not per face per frame,
|
||||
which is the cost rule applied as written: fewer regions, never a degraded
|
||||
input.
|
||||
|
||||
**Failing an axis discounts the observation; it does not delete the detection.**
|
||||
Only size drops the face outright, and only because VR-005 measured a knee below
|
||||
@@ -216,14 +265,18 @@ hand-chosen cutoff on an uncalibrated measure is the same unfalsifiable magic
|
||||
number AR-024 retired for similarity, and it would fail the same way: meaning
|
||||
something different for every detector, every embedder and every film.
|
||||
|
||||
**Current:** none of the three is assessed. `min_face_px` (40, decoded-frame
|
||||
space) is the only quality signal in the pipeline; sharpness and visibility are
|
||||
unmeasured, and `align_face()` silently drops only the degenerate-affine case
|
||||
without counting it.
|
||||
**Current:** visibility is measured and carried — `estimate_alignment()` in
|
||||
`src/face_utils.hpp` returns the residual alongside the transform, and
|
||||
`FaceAlignerFunc` writes it to `DetectedFace::alignment_residual`. Size is
|
||||
`min_face_px` (40, decoded-frame space — AR-002 still open). Sharpness is
|
||||
unmeasured. Nothing yet *consumes* any of it: no discount is applied, and
|
||||
`align_face()` still drops the degenerate-fit case without counting it.
|
||||
|
||||
**Gap:** all of AR-028 … AR-030. Order: land the quality vector and its dump
|
||||
field first (AR-028) so VR-012 can be run from fixtures, then set behaviour per
|
||||
axis from what it measures.
|
||||
**Gap:** AR-029 entirely. For AR-030, the measure exists but the discount does
|
||||
not — it must reach `EvidenceDiscounter` as the reliability term. For AR-028, the
|
||||
residual does not yet reach the VR-001 dump, which is what VR-012 needs to run
|
||||
from fixtures; that is the next step, since it unblocks the study that sets
|
||||
every remaining behaviour.
|
||||
|
||||
## AR-007, AR-008 — Tracking
|
||||
|
||||
|
||||
Reference in New Issue
Block a user