Answer M1: tract loads both face graphs once their dims are pinned

Neither InsightFace export parses as shipped -- SCRFD fails at its input
node, ArcFace at the first Conv -- which is the same wall dr-segment hit
on YOLO's dynamic export. Both load cleanly with the input dims frozen,
so the pure-Rust runtime holds for the face pipeline too.

tools/fix-face-model-shapes.sh does the freezing, and exists so the
artefact is reproducible rather than a binary someone once produced. It
takes two forms because the two graphs need different ones: ArcFace's
batch is a named dim_param, SCRFD's H and W are dynamic but unnamed.

Also notes YuNet loading with no intervention, which matters for the
licence question in faces.md 2.3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 19:51:02 +02:00
co-authored by Claude Opus 5
parent ec740115b6
commit 72410f39c6
9 changed files with 435 additions and 1 deletions
+66
View File
@@ -0,0 +1,66 @@
//! M1 (docs/faces.md §12) — will tract load these graphs at all?
//!
//! The one measurement everything else in the face subsystem is conditional
//! on. `det_500m.onnx` has a dynamic H/W input, which is exactly what tract
//! failed on for YOLO26n-seg, so a plain "no" here is the expected outcome and
//! the interesting part is the error it gives.
//!
//! cargo run -p dr-face --features inference --example probe -- MODEL...
fn main() {
env_logger::init();
let paths: Vec<String> = std::env::args().skip(1).collect();
if paths.is_empty() {
eprintln!("usage: probe MODEL.onnx [MODEL.onnx ...]");
std::process::exit(2);
}
let mut failures = 0;
for path in &paths {
println!("\n=== {path} ===");
let bytes = match std::fs::read(path) {
Ok(b) => b,
Err(e) => {
println!(" UNREADABLE: {e}");
failures += 1;
continue;
}
};
println!(" {} bytes", bytes.len());
dr_face::install_backend_for_probe();
let session = ort::session::Session::builder()
.and_then(|mut b| b.commit_from_memory(&bytes));
match session {
Err(e) => {
println!(" LOAD FAILED: {e}");
failures += 1;
}
Ok(s) => {
println!(" LOADED");
for i in s.inputs() {
println!(
" in {:<24} {:?}",
i.name(),
i.dtype().tensor_shape()
);
}
for o in s.outputs() {
println!(
" out {:<24} {:?}",
o.name(),
o.dtype().tensor_shape()
);
}
}
}
}
println!("\n{} of {} failed", failures, paths.len());
if failures > 0 {
std::process::exit(1);
}
}