Bali was chosen because the TRECVID DVU set ships character mugshots, but its reference crops are unusable at scale: median detected face 27 px against a 69 px maximum, so every reference was upscaled 4x or more past what the embedder was trained for (AR-011). A 66 px floor left 2 of 69 references; no threshold exists that both keeps the faces in distribution and leaves enough of them to calibrate. SuperHero is 69 px median and 241 px max. Its gallery builds at a 66 px floor with 14 references over 5 characters, and calibrates on its own (a=15.2867 b=-4.98633, 100% train accuracy) instead of borrowing constants. Measured on the fused 17-minute film, one stream rather than per-scene clips so presence windows cross real scene boundaries as SR-002 intends: precision 1.00, recall 0.65, F1 0.79 — 13 true positives, 0 false positives, 7 misses. Every out-of-gallery character was declined rather than forced onto a nearest match. The misses are the short scenes (14 s, 38 s, 27 s), consistent with per-track accumulation needing sightings. - build_gallery gains --min-face-px, filtering the *detected face* rather than the crop. The DVU images are scene crops, not mugshots, so crop dimensions say nothing about face scale. A poisoned reference is permanent in a way a bad frame is not: it corrupts every future match against that identity. - scripts/fetch_dvu.sh fetches mugshots, scene graphs and segmentation for any DVU film. NIST names the same film three different ways, so KG_DIR and KG_FILE are overridable rather than derived. This exists as a script because the first copy of this data was assembled ad hoc in /tmp and was lost with it, taking the working gallery along. - Replay fixtures move to the artifact registry: push/pull_artifacts.sh gain a replay-fixtures target, and tests/fixtures/dumps/.gitignore keeps them out of git. superhero.h5 is ~9 MB and regenerating it needs the film, the models and a GPU — none of which CI has. The gallery ships with the dumps, since a dump only replays against the gallery it was produced with. - AR-012 and AR-013 coverage is ported onto the new fixture rather than dropped with the Bali cases: 12369 assertions, up from 7991, since the film is an order of magnitude larger than the clips. Suite: 15679 assertions, 101 test cases. TRACES: AR-011, AR-012, AR-013 | VR-001, VR-005 | SR-002
183 lines
7.7 KiB
Bash
Executable File
183 lines
7.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# push_artifacts.sh — upload benchmark artifacts (galleries, montage frames) to the
|
|
# Gitea generic package registry, decoupled from git history entirely. These are
|
|
# NOT needed to run the main app — only for benchmarks/experiments/reports.
|
|
#
|
|
# Requires GITEA_TOKEN in the environment (a Gitea access token with package
|
|
# read/write scope). Never hardcode the token; export it in your shell:
|
|
# export GITEA_TOKEN=...
|
|
#
|
|
# Usage:
|
|
# scripts/artifacts/push_artifacts.sh galleries
|
|
# scripts/artifacts/push_artifacts.sh montage-frames
|
|
# scripts/artifacts/push_artifacts.sh experiment-data
|
|
# scripts/artifacts/push_artifacts.sh report-highlights
|
|
# scripts/artifacts/push_artifacts.sh xsource
|
|
# scripts/artifacts/push_artifacts.sh replay-fixtures
|
|
# scripts/artifacts/push_artifacts.sh galleries montage-frames experiment-data report-highlights
|
|
#
|
|
# Package layout (owner=dtourolle, repo=scene-actor-extraction):
|
|
# generic/galleries/<version>/gallery_<model>.h5 (one file per model)
|
|
# generic/replay-fixtures/<version>/replay-fixtures.zip (T2 dumps + their gallery)
|
|
# generic/montage-frames/<version>/<film-slug>.zip (zipped per-film frames)
|
|
# generic/experiment-data/<version>/experiment-data.zip (manifests/trajectories/results)
|
|
# generic/report-highlights/<version>/<name>.jpg (individual, hand-picked
|
|
# illustrative frames referenced by docs/*.md that aren't reproducible via
|
|
# the automated best/worst montage selection — see scripts/docs/build_site.sh)
|
|
# version = current git short SHA, so artifacts are traceable to the code that
|
|
# produced them. Re-running with the same SHA overwrites that version's files.
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
OWNER="dtourolle"
|
|
PKG_REPO="scene-actor-extraction"
|
|
# NOTE: the generic package registry lives under /api/packages/ (no /v1/) —
|
|
# distinct from the regular REST API under /api/v1/packages/ used for listing.
|
|
BASE_URL="https://gitea.tourolle.paris/api/packages/${OWNER}"
|
|
VERSION="$(git -C "$REPO_ROOT" rev-parse --short HEAD)"
|
|
|
|
if [ -z "${GITEA_TOKEN:-}" ]; then
|
|
echo "error: GITEA_TOKEN is not set. export GITEA_TOKEN=<your token> and retry." >&2
|
|
exit 1
|
|
fi
|
|
|
|
upload() {
|
|
local package="$1" filename="$2" filepath="$3"
|
|
local url="${BASE_URL}/generic/${package}/${VERSION}/${filename}"
|
|
echo " uploading ${filename} -> ${package}/${VERSION}..."
|
|
curl -sf -X PUT "$url" \
|
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
-H "Content-Type: application/octet-stream" \
|
|
--upload-file "$filepath" \
|
|
-o /dev/null -w " HTTP %{http_code}\n"
|
|
}
|
|
|
|
push_replay_fixtures() {
|
|
echo "=== replay-fixtures (version ${VERSION}) ==="
|
|
# T2 replay fixtures: per-frame detections, landmarks and embeddings dumped
|
|
# from a real run, so the tracker and identity stages can be replayed on CPU
|
|
# with no GPU, no models and no film. Too large for git (superhero.h5 alone
|
|
# is ~9 MB) and regenerating them needs the film plus a GPU, which CI has
|
|
# neither of — so they ship as artifacts and CI pulls them.
|
|
#
|
|
# The gallery travels with them: a dump replays against the gallery it was
|
|
# produced with, and pairing a dump with a different gallery silently
|
|
# changes every identity decision in it.
|
|
local dir="${REPO_ROOT}/tests/fixtures/dumps"
|
|
if [ ! -d "$dir" ]; then
|
|
echo " no tests/fixtures/dumps dir, skipping" >&2
|
|
return
|
|
fi
|
|
local tmp
|
|
tmp="$(mktemp -d)"
|
|
( cd "$dir" && zip -qr "$tmp/replay-fixtures.zip" . )
|
|
upload "replay-fixtures" "replay-fixtures.zip" "$tmp/replay-fixtures.zip"
|
|
rm -rf "$tmp"
|
|
}
|
|
|
|
push_galleries() {
|
|
echo "=== galleries (version ${VERSION}) ==="
|
|
local dir="${REPO_ROOT}/experiments/galleries"
|
|
shopt -s nullglob
|
|
for f in "$dir"/gallery_*.h5; do
|
|
upload "galleries" "$(basename "$f")" "$f"
|
|
done
|
|
shopt -u nullglob
|
|
}
|
|
|
|
push_montage_frames() {
|
|
echo "=== montage-frames (version ${VERSION}) ==="
|
|
local root="${REPO_ROOT}/experiments/results/holdout/montage_bestworst"
|
|
if [ ! -d "$root" ]; then
|
|
echo " no montage_bestworst dir found, skipping" >&2
|
|
return
|
|
fi
|
|
local tmp
|
|
tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' RETURN
|
|
for filmdir in "$root"/*/; do
|
|
[ -d "$filmdir" ] || continue
|
|
local slug ascii_slug zipfile
|
|
slug="$(basename "$filmdir")"
|
|
# Gitea's generic package registry rejects non-ASCII filenames (verified:
|
|
# a bare é in the name 400s). Transliterate for the upload name only —
|
|
# the local directory name (with accents) is untouched.
|
|
ascii_slug="$(echo "$slug" | iconv -f utf-8 -t ascii//translit 2>/dev/null || echo "$slug")"
|
|
zipfile="${tmp}/${ascii_slug}.zip"
|
|
(cd "$filmdir" && zip -qr "$zipfile" .)
|
|
upload "montage-frames" "${ascii_slug}.zip" "$zipfile"
|
|
done
|
|
}
|
|
|
|
push_experiment_data() {
|
|
echo "=== experiment-data (version ${VERSION}) ==="
|
|
# manifests/trajectories/results are all small text/JSON — no source paths
|
|
# (manifests never carry a "movie" field; see experiments/file-lut.json)
|
|
# so this is safe to share as one bundle.
|
|
local tmp; tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' RETURN
|
|
local zipfile="${tmp}/experiment-data.zip"
|
|
(cd "$REPO_ROOT" && zip -qr "$zipfile" \
|
|
experiments/manifests experiments/trajectories experiments/results \
|
|
-x '*.log' -x '*/holdout/montage_bestworst/*' -x '*/holdout/frames/*' \
|
|
-x '*/holdout/montage/*' -x '*/holdout/*.jsonl' -x '*/holdout/pred_*.json' \
|
|
-x '*/holdout/raw_*.jsonl')
|
|
upload "experiment-data" "experiment-data.zip" "$zipfile"
|
|
}
|
|
|
|
push_report_highlights() {
|
|
echo "=== report-highlights (version ${VERSION}) ==="
|
|
local src="${REPO_ROOT}/experiments/results/holdout/montage/many_saints/out_of_cast_fpi/10_t003641.jpg"
|
|
if [ ! -f "$src" ]; then
|
|
echo " [warn] $src not found, skipping" >&2
|
|
return
|
|
fi
|
|
upload "report-highlights" "germar_beats_xray.jpg" "$src"
|
|
}
|
|
|
|
push_xsource() {
|
|
echo "=== xsource (version ${VERSION}) ==="
|
|
local root="${REPO_ROOT}/experiments/xsource"
|
|
if [ ! -d "$root/labelling" ]; then
|
|
echo " no experiments/xsource/labelling found, skipping" >&2
|
|
return
|
|
fi
|
|
|
|
# Source recordings. Already compressed, so uploaded as-is rather than zipped.
|
|
shopt -s nullglob
|
|
for f in "$root"/clips/*.mp4; do
|
|
upload "xsource" "$(basename "$f")" "$f"
|
|
done
|
|
shopt -u nullglob
|
|
|
|
# The hand-sorted crops and their manifests. This is human ground truth and
|
|
# the expensive part of the study — a person looked at every crop and put it
|
|
# in a folder. Frames are deliberately NOT pushed: they are deterministic
|
|
# from the clips, and pulling regenerates them.
|
|
local tmp; tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' RETURN
|
|
local zipfile="${tmp}/labelling.zip"
|
|
(cd "$root" && zip -qr "$zipfile" labelling -x 'labelling/*.html' -x 'labelling/review_*.jpg' \
|
|
-x 'labelling/verify_*.jpg')
|
|
upload "xsource" "labelling.zip" "$zipfile"
|
|
}
|
|
|
|
if [ $# -eq 0 ]; then
|
|
echo "usage: $0 <galleries|montage-frames|experiment-data|report-highlights|xsource> [...]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
for target in "$@"; do
|
|
case "$target" in
|
|
galleries) push_galleries ;;
|
|
montage-frames) push_montage_frames ;;
|
|
experiment-data) push_experiment_data ;;
|
|
report-highlights) push_report_highlights ;;
|
|
xsource) push_xsource ;;
|
|
replay-fixtures) push_replay_fixtures ;;
|
|
*) echo "unknown target: $target (expected galleries, montage-frames, experiment-data, report-highlights, xsource, or replay-fixtures)" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
echo "Done. Browse at: https://gitea.tourolle.paris/${OWNER}/${PKG_REPO}/packages"
|