alex 107f48d118 find_dupes: triple-gate closeness (dHash<=6, aHash<=8, color-hist L1<=0.10) + strict linkage
Measured on verified same/different pairs: dHash alone has a false-
positive floor for same-style anime frames (distinct shots at distance
<=10 and even <=6 with different color content). Same-shot broadcast
replays are pixel-twins: near-zero color-hist distance and low aHash
distance too. Requires ALL three gates, no transitive chaining.
2026-10-18 18:51:45 -04:00

anime-facesaver

Scene-gated still extractor for building anime training datasets from video. Fork of facesaver, created Oct 2026.

Why this fork exists

The original facesaver ran stock COCO yolov11l.pt and accepted class 0 — person — as a "proxy for face" (their comment, not ours). COCO has no face class, so every frame containing a large enough person passed: full-body shots, headless crops, a hand at 75% confidence. That is why the old Gabriel Dropout output contained stills of shoes.

anime-facesaver uses a dedicated anime-face detector instead: yolov8x6_animeface — yolov8x6 fine-tuned at 1280px on 10k manually annotated anime images (P=0.957 R=0.924 mAP50=0.955 on its test set). It detects faces. Only faces. The scene-change gate from the original is preserved: at most one still is saved per detected scene, which keeps near-duplicates out of the dataset.

Setup

uv venv --python 3.12 .venv           # or: python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt       # add --index-url for your CUDA ver
python anime_facesaver.py --download-model ...   # fetches weights once

Weights land in models/yolov8x6_animeface.pt (~195 MB, AGPL-3.0).

Usage

python anime_facesaver.py \
    -I /home/alex/Documents/image-training-datasets/gabriel-dropout-show \
    -O /home/alex/Documents/image-training-datasets/gabriel-dropout-v2 \
    --gpu 1 \
    --preview-dir /home/alex/Documents/image-training-datasets/gabriel-dropout-v2-preview

GPU pinning (important on the shared .80 box)

--gpu sets CUDA_VISIBLE_DEVICES before torch initializes, so no context leaks onto the wrong card:

flag effect
--gpu 1 pin to physical GPU 1 (recommended on .80)
--gpu 0 pin to GPU 0 — OOMs while Strata holds it
--gpu cpu force CPU
--gpu auto let torch pick

Key options

flag default meaning
--conf 0.3 detector confidence (model's own recommendation; it is high-precision)
--min-width / --min-height 100 minimum face box size in px
--min-face-fraction 0.005 face box area as fraction of frame — kills distant background extras; 0 disables
--max-face-fraction 0.30 face box area upper cap — kills detector meltdowns on full-frame ED/OP effects (feather storms etc.); 0 disables
--min-aspect / --max-aspect 0.45 / 1.6 face box W/H ratio window — effect blobs rarely keep face proportions
--edge-margin 0 require faces this far inside frame edges; 0 accepts edge-touching faces (original hard-rejected them)
--scene-threshold 30.0 PySceneDetect ContentDetector threshold (same as original)
--imgsz 1280 inference size; model was trained at 1280, 640 is faster at some recall cost
--all-files off also scan OP/ED files. Off by default: music videos are full of stylized graphics (feather storms, transformation emblems) that false-positive as face-shaped; episodes carry the real character close-ups
--preview-dir off also write annotated copies (green boxes) of every saved frame for eyeball QC

Notes

  • Runs every frame of every scene until a face qualifies, then waits for the next scene start. That is the original behavior and it is why dense scenes don't flood the output.
  • The detector ships as yolov8x6 — big and a little slow (it was trained to be precise, not cheap). On a 3090 at 1280px expect roughly 8-12 fps; an hour of 24fps anime is ~15 min of inference per GPU.
  • License: the model weights are AGPL-3.0 (Fuyucchi); ultralytics is AGPL-3.0 too. Fine for personal/research dataset building; if this ever ships in a product, revisit.

QC: auditing an existing collection

audit_stills.py runs the same detector over a directory of stills and reports how many contain no face at all — e.g. measuring the damage the old person-proxy pipeline did:

python audit_stills.py -I ~/Documents/image-training-datasets/gabriel-dropout \
    --gpu 1 --write-report old_run_report.csv

Verified run (Gabriel Dropout, Oct 18 2026)

12 episodes + 2 specials, OP/ED skipped: 3,526 stills vs 887 from the old person-proxy run (~4x yield — chibi reaction faces no longer missed). QC: full box-size distribution audited (p5=0.7%, median 6.6%, max 29.9%); 5 largest and 5 smallest saves inspected with annotated copies — all real faces; zero faceless images found. The 5 largest sit against the 30% area cap and are legitimate extreme close-ups (crying faces count).

Verification workflow

Always run with --preview-dir and eyeball the _ann.png files: every saved frame should show a green box on a real face. Pay attention to saves clustered against a gate (e.g. many boxes at ~max area) — that pattern means the gate is doing work and deserves a look. For a known-dirty legacy collection, audit_stills.py quantifies the damage.

Recipe:

python anime_facesaver.py -I <show_dir> -O <out> --gpu <n> \
    --preview-dir <out>-preview
S
Description
Scene-gated anime still extractor with real anime face detection (fork of facesaver)
Readme
138 KiB
Languages
Python 100%