Files
DarkRoom/models/face/README.md
T
dtourolle 84fade99ec Put the developer docs under docs/dev and index the folder for users first
docs/ had 26 developer documents flat beside the manual, and the two
audiences are very differently sized: most readers want the manual and
the gesture reference, a few want the register, the designs and the
measurements. The manual and gestures.md stay at the top; everything for
someone changing the code moves to docs/dev/, and the two documents that
name their own successors — the v0.1 milestone and the UI-refinement plan
— go to docs/dev/archive/ rather than being deleted, since both are still
cited. docs/README.md is the index, users first.

Every reference follows: code comments, Cargo manifests, the workflows,
the pre-commit hook, the bench and traceability tools (which locate the
repo root by docs/dev/requirements.md now), packaging, the Docker READMEs,
CLAUDE.md, CONTRIBUTING.md and the README. The matrix links one level
deeper and is regenerated. Links out of the moved documents into the tree
gain a level; a link checker over every Markdown file finds none broken.
2026-09-20 21:16:03 +02:00

100 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Face models
The shape-fixed SCRFD detectors and the ArcFace/MobileFaceNet embedder, in LFS. One copy, packaged
by every platform:
| Platform | How it ships | Where it lands |
|---|---|---|
| Android | `assemble-apk.sh` bundles them as APK assets; `android_main` unpacks on first launch | the shared user data directory |
| Arch | `PKGBUILD` installs them | `/usr/share/darkroom/models/` |
`library::face_models` searches the account's own directory, then the shared user directory, then
`$XDG_DATA_DIRS` — so a pair the user placed by hand always outranks the packaged one.
scrfd_500m_640.onnx 2.5 MB "Fast" in settings — what every library was indexed with until 0.12
scrfd_2.5g_640.onnx 3.3 MB "Balanced" — 14% more faces for 12% more time (faces.md §12.3)
scrfd_10g_640.onnx 17 MB "Thorough" — a further 12% for 3× the time
arcface_mbf_b1.onnx 13 MB
2d106det_b1.onnx 4.8 MB 106 landmarks, for the eye boxes (faces.md §17)
ocec_s_b1.onnx 483 KB eyes open or closed, per eye
sgc_l_48_b1.onnx 6.1 MB sunglasses or not, per head
Which detector runs is a per-device setting (`FaceSettings::detector`); all three are installed so
the choice exists on every platform. Each is its own `faces.model_id`, so changing it re-indexes.
The last three are optional to the app: `library::face_models` reports them beside the pair when
all three are there, and a library without them indexes faces and simply has no eye readings —
every reader treats "never read" as unknown, never as closed. They are found in the *same*
directory as the pair, so a hand-placed pair does not pick up a package's eye models from a
directory it otherwise outranks.
scrfd_500m_640.int8.onnx 0.8 MB the same three, in the form the Hexagon NPU takes
scrfd_2.5g_640.int8.onnx 0.9 MB (docs/dev/inference.md §5) — opset 17, per-channel int8
scrfd_10g_640.int8.onnx 4.3 MB weights, uint8 activations, calibrated on 96 photographs
The int8 files are **derived** by `tools/quantise-models.sh` from the f32 ones beside them and
travel with them: the engine loads the `.int8.onnx` sibling when the device's backend wants it and
the canonical file otherwise, and a library indexed on the int8 form records it as a different
detector (`scrfd_500m_i8+w600k_mbf`), because it finds a different set of faces. Every other
platform ignores them. The embedder has no int8 form and never will (§7 of the same document).
**A clone without git-lfs gets a ~130-byte pointer where each model should be.** Both packagers check
for exactly that and refuse, rather than shipping the pointer and failing inside tract on the user's
machine. Fix it with `git lfs pull`.
These are not what InsightFace ships. They came from `buffalo_sc.zip`, `buffalo_m.zip`, `buffalo_l.zip`
and `buffalo_s.zip` on the InsightFace v0.7 release with their input dimensions pinned, because tract
cannot parse any of the graphs while they are dynamic:
./tools/fix-face-model-shapes.sh det_500m.onnx models/face/scrfd_500m_640.onnx --input input.1=1,3,640,640
./tools/fix-face-model-shapes.sh det_2.5g.onnx models/face/scrfd_2.5g_640.onnx --input input.1=1,3,640,640
./tools/fix-face-model-shapes.sh det_10g.onnx models/face/scrfd_10g_640.onnx --input input.1=1,3,640,640
./tools/fix-face-model-shapes.sh w600k_mbf.onnx models/face/arcface_mbf_b1.onnx --dim None=1
./tools/fix-face-model-shapes.sh 2d106det.onnx models/face/2d106det_b1.onnx --dim None=1
`2d106det_b1.onnx` is from the same `buffalo_l.zip` as `det_10g.onnx` and under the same grant: the
106-point landmark model whose lid contours the eye boxes are cut from (docs/dev/faces.md §17.2).
sha256 as fetched `f001b856…a7109dbf`, as shipped `afc2984c…03368ef26`.
The weights carry a non-commercial research-only grant. They are here because this is a private
repository and self-installed builds; they come back out before anything is published, and the
restriction binds whoever uses the app, not only the project. docs/dev/faces.md §2.2a is the decision.
## The two classifiers are a different matter
`ocec_s_b1.onnx` and `sgc_l_48_b1.onnx` are **MIT, code and weights**, from Katsuya Hyodo's
ultra-lightweight classifier series — the same author as the whole-body detector the reference
pipeline uses. They are not under the InsightFace grant and do not come out when the project
publishes. Read 2026-09-19:
| | OCEC — open/closed eyes | SGC — sunglasses |
|---|---|---|
| Source | `github.com/PINTO0309/OCEC`, release `onnx`, `ocec_s.onnx` | `github.com/PINTO0309/SGC`, release `onnx`, `sgc_is_l_48x48.onnx` |
| Licence | MIT (repository `LICENSE`; no separate grant on the weights) | MIT, likewise |
| Training data | *Open and Closed Eyes* (Młodawski 2024, HF, **ODC-By 1.0** — attribution only), crops cut by DEIMv2-Wholebody34 (Apache 2.0) | **Not stated.** The README names no dataset and carries no acknowledgement; `data/` holds a class-ratio plot and nothing else. |
| Input | one eye, 40×24, RGB, `x/255` | one head, 48×48, RGB, `x/255` |
| Output | `prob_open`, a sigmoid | `prob_sunglasses`, a sigmoid |
| sha256 as fetched | `9a346a08…c604ba9b64` | `9c13d937…a4063c73` |
| sha256 as shipped | `c848d34c…4b29d04c7c` | `d49b6206…7e9538525c6` |
Both shipped with a dynamic batch dimension that tract loads but the project pins anyway, with the
same script as the pair:
./tools/fix-face-model-shapes.sh ocec_s.onnx models/face/ocec_s_b1.onnx --dim batch=1
./tools/fix-face-model-shapes.sh sgc_is_l_48x48.onnx models/face/sgc_l_48_b1.onnx --dim batch=1
The one thing D13's discipline turns up here is SGC's undocumented training set. The weights'
grant is MIT and that is what binds a redistributor; but "trained on what" is the question this
project reads first, and for SGC it has no answer. Recorded so it is a known gap rather than an
assumption, and so that whoever finds a sunglasses classifier with a stated dataset knows what to
replace.
Attribution, as ODC-By asks for the eye dataset:
> Michał Młodawski, *Open and Closed Eyes Dataset*, July 2024,
> https://huggingface.co/datasets/MichalMlodawski/closed-open-eyes
The variant choice was measured, not taken from the F1 column: on family snapshots the S variant
of OCEC read more open eyes as open than M or L did, which overfit their own domain
(docs/dev/faces.md §17).