A track recognised on 318 of 385 frames was owned on none, so the truth file named nobody while the matcher was accepting almost continuously. The correlation discount was an annihilator rather than an attenuator. Weight was 1 - P(same view), so once a track had one stored view every later frame of that same face scored ~0.01 and the belief stopped moving. One observation just over the accept threshold is logit(0.78) ~ 1.27, under the ownership bar — hence recognised always, owned never. Two changes, in the order they were found. Correlated evidence is now attenuated by effective sample size, n_eff = n / (1 + (n-1)·rho), each frame contributing the marginal gain. That has the right shape at both ends: uncorrelated evidence accumulates linearly, and a held pose converges on 1/rho rather than growing without bound. A constant floor was tried first and rejected — it grows linearly forever, so a long shot could out-argue genuinely varied evidence purely by lasting longer. Combination is now weighted lazy-OR: P = 1 - (1-P_old)·(1-p)^w, stored as log(1-P) so the update is additive and precision stays where it matters as P approaches 1. Each frame is new evidence that this track is that actor, and the belief is the probability that at least one sighting was right. It converges faster than summing log-odds at the same effective count — 2.98 vs 2.53 after two observations at p=0.78 — which is what a real clip needs. Note that summing log-odds was already a correct sequential Bayesian update: the matcher fits with prior 0.5, so logit(p) IS the per-frame log-likelihood ratio and the running sum carries the prior forward. It was not wrong, it was slow. What blocked ownership was the discount, not the combination rule. Also fixes a real correctness bug: the observation count lived on the discounter, which is shared by every track, so tracks pooled into one effective sample and each was discounted by how many others happened to be on screen. It is now a per-track parameter. The registry's frame scope holds its lock for its lifetime and the mutex is not recursive, so calling observe() inside a scope self-deadlocks. The pipeline never does — separate nodes — but the test did, and hung rather than failing. Documented at the call site. Verified end to end: the same clip that produced zero actors now identifies Bing Crosby and Dorothy Lamour with belief 0.97. Suite: 96 cases, 6142 assertions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: AR-025 | SR-002
61 lines
2.8 KiB
Bash
Executable File
61 lines
2.8 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Regenerate bali_offset_200s.flac — the real-audio fixture behind VR-014, the
|
|
# audio-signature offset-recovery validation.
|
|
#
|
|
# sh make_offset_fixture.sh /path/to/clips
|
|
#
|
|
# Why real audio and not a second synthetic tone: jray_audio_v1_tone.flac pins
|
|
# the *arithmetic* (IR-005) and is deliberately built so every band and every
|
|
# energy class appears. It cannot answer the question VR-014 asks — whether the
|
|
# peak-bin sequence of ordinary film audio is distinctive enough that sliding
|
|
# one signature against another finds the true alignment and only the true
|
|
# alignment. Tones are pathologically easy for that; dialogue and score are not.
|
|
#
|
|
# Source: five scene clips from "Road to Bali" (1952), the public-domain corpus
|
|
# this repo already uses for the replay fixtures — tests/fixtures/dumps/bali_*.h5
|
|
# are dumps of these same clips. Each is under the 120 s window on its own
|
|
# (29-77 s), so they are concatenated in scene order to make a source long
|
|
# enough that a 120 s window can slide inside it.
|
|
#
|
|
# 200 s is chosen, not arbitrary: the window is 120 s and the match search is
|
|
# capped at +/-600 frames (~55.7 s), so a source of 120 + 56 s is the shortest
|
|
# one that can place two windows at the edge of the cap. The 200 s here leaves
|
|
# room to go past it as well, which is what lets the test check that an
|
|
# out-of-range offset is declined rather than guessed.
|
|
#
|
|
# Encoded mono at 11025 Hz, 16-bit, which is exactly what the signature decodes
|
|
# to anyway. That keeps a 200 s fixture at ~2.4 MB instead of ~20 MB, and makes
|
|
# every trim below sample-exact — the test measures offset recovery, not the
|
|
# resampler, which tests/test_audio_signature.cpp already covers (UT-103).
|
|
#
|
|
# FLAC because it is lossless: the decoded PCM is the same on every machine, so
|
|
# a signature computed from this file is reproducible. A lossy fixture would
|
|
# make the measurement depend on the decoder version.
|
|
#
|
|
# sha256 of the committed file:
|
|
# 4a952e46a090a9acd9eae56996250ec03e08e0d04ee139ac0a42f1690a536c83
|
|
# A regenerated file that hashes differently means the source clips or the
|
|
# encoder changed, and VR-014's recorded numbers should be re-measured — the
|
|
# offsets will still be exact, but the scores are this audio's.
|
|
|
|
set -eu
|
|
|
|
CLIPS="${1:-../../../../bali}"
|
|
OUT="$(dirname "$0")/bali_offset_200s.flac"
|
|
LIST="$(mktemp)"
|
|
trap 'rm -f "$LIST"' EXIT
|
|
|
|
for scene in 13 27 28 31 46; do
|
|
clip="$CLIPS/Road_To_Bali-$scene.webm"
|
|
[ -f "$clip" ] || { echo "missing clip: $clip" >&2; exit 1; }
|
|
echo "file '$(cd "$(dirname "$clip")" && pwd)/$(basename "$clip")'" >> "$LIST"
|
|
done
|
|
|
|
ffmpeg -nostdin -v error -y -f concat -safe 0 -i "$LIST" \
|
|
-vn -t 200 -ac 1 -ar 11025 -sample_fmt s16 \
|
|
-c:a flac -compression_level 12 "$OUT"
|
|
|
|
echo "wrote $OUT"
|
|
sha256sum "$OUT" 2>/dev/null || shasum -a 256 "$OUT"
|