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.
168 lines
5.6 KiB
Rust
168 lines
5.6 KiB
Rust
//! TRACES: FR-CULL-8 | FR-EXP-9
|
|
//! Measure what indexing at native resolution is actually worth.
|
|
//!
|
|
//! cargo run -p dr-ui --example face_native -- DET.onnx EMB.onnx [--export DIR] FILE [FILE…]
|
|
//!
|
|
//! Renders each file once at native resolution, then indexes it twice: the way
|
|
//! FR-CULL-8 now specifies, and the way it used to be done — everything, both
|
|
//! stages, from a 1024px proxy. Prints the faces found and the `crop_px` each
|
|
//! run gave the embedder.
|
|
//!
|
|
//! It exists because the case for the change was made from `crop_px` readings
|
|
//! taken out of a catalog after the fact. That is evidence about what happened;
|
|
//! this is evidence about what the new code does, on the same photographs, with
|
|
//! nothing between the two runs but the resolution.
|
|
//!
|
|
//! `--export DIR` also writes each native render out as a JPEG, so the
|
|
//! model-free tooling in `dr-face`'s examples — `eyes` above all — can be
|
|
//! run over native pixels rather than proxies (docs/dev/faces.md §17.4).
|
|
//!
|
|
//! The models must have had their input dims frozen first; see
|
|
//! `tools/fix-face-model-shapes.sh`.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
const MODEL_ID: &str = "w600k_mbf";
|
|
|
|
fn main() {
|
|
env_logger::init();
|
|
|
|
let mut args: Vec<String> = std::env::args().skip(1).collect();
|
|
let export = args.iter().position(|a| a == "--export").map(|i| {
|
|
args.remove(i);
|
|
PathBuf::from(args.remove(i))
|
|
});
|
|
if args.len() < 3 {
|
|
eprintln!("usage: face_native DETECTOR.onnx EMBEDDER.onnx [--export DIR] FILE [FILE…]");
|
|
std::process::exit(2);
|
|
}
|
|
if let Some(d) = &export {
|
|
std::fs::create_dir_all(d).expect("export dir");
|
|
}
|
|
let (detector_model, embedder_model) = (PathBuf::from(&args[0]), PathBuf::from(&args[1]));
|
|
|
|
let Some(gpu) = pollster::block_on(dr_gpu::GpuContext::new_headless()).ok() else {
|
|
eprintln!("no GPU adapter; a native render needs one");
|
|
std::process::exit(1);
|
|
};
|
|
|
|
let mut detector = match dr_face::Detector::from_path(&detector_model) {
|
|
Ok(d) => d,
|
|
Err(e) => {
|
|
eprintln!("detector: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
let mut embedder =
|
|
match dr_face::Embedder::from_path(&embedder_model, dr_face::ModelId::new(MODEL_ID)) {
|
|
Ok(e) => e,
|
|
Err(e) => {
|
|
eprintln!("embedder: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
let options = dr_face::DetectOptions::default();
|
|
|
|
println!(
|
|
"{:<28} {:>11} {:>17} {:>17}",
|
|
"file", "native", "native faces/px", "1024 faces/px"
|
|
);
|
|
|
|
let (mut n_native, mut n_proxy) = (0usize, 0usize);
|
|
let (mut px_native, mut px_proxy) = (0.0f32, 0.0f32);
|
|
|
|
for path in &args[2..] {
|
|
let bytes = match std::fs::read(path) {
|
|
Ok(b) => b,
|
|
Err(e) => {
|
|
println!("{path}: cannot read: {e}");
|
|
continue;
|
|
}
|
|
};
|
|
let frame = match dr_ui::render_native(&gpu, &bytes) {
|
|
Ok(f) => f,
|
|
Err(e) => {
|
|
println!("{path}: cannot render: {e}");
|
|
continue;
|
|
}
|
|
};
|
|
let (w, h) = (frame.width as usize, frame.height as usize);
|
|
if let Some(dir) = &export {
|
|
let stem = std::path::Path::new(path)
|
|
.file_stem()
|
|
.map(|s| s.to_string_lossy().into_owned())
|
|
.unwrap_or_default();
|
|
match dr_thumbs::codec::encode_rgba(frame.width, frame.height, &frame.rgba) {
|
|
Ok(bytes) => std::fs::write(dir.join(format!("{stem}.jpg")), bytes).expect("write"),
|
|
Err(e) => println!("{path}: export: {e}"),
|
|
}
|
|
}
|
|
|
|
let native = dr_ui::faces::index_native(
|
|
&mut detector,
|
|
&mut embedder,
|
|
None,
|
|
&frame.rgba,
|
|
w,
|
|
h,
|
|
&options,
|
|
)
|
|
.unwrap_or_default();
|
|
|
|
// The old path, reproduced exactly: one buffer at 1024, used for both
|
|
// detection and the crop.
|
|
let mut small = dr_decode::Preview {
|
|
width: frame.width,
|
|
height: frame.height,
|
|
rgba: frame.rgba.clone(),
|
|
};
|
|
small.downscale_to(dr_thumbs::ThumbSize::Large.edge());
|
|
let proxy =
|
|
dr_ui::faces::index_preview(&mut detector, &mut embedder, None, &small, &options)
|
|
.map(|(f, _)| f)
|
|
.unwrap_or_default();
|
|
|
|
let mean = |v: &[dr_catalog::faces::DetectedFace]| {
|
|
if v.is_empty() {
|
|
0.0
|
|
} else {
|
|
v.iter().map(|f| f.crop_px).sum::<f32>() / v.len() as f32
|
|
}
|
|
};
|
|
let name = std::path::Path::new(path)
|
|
.file_name()
|
|
.map(|s| s.to_string_lossy().into_owned())
|
|
.unwrap_or_else(|| path.clone());
|
|
|
|
println!(
|
|
"{:<28} {:>11} {:>10} /{:>5.0} {:>10} /{:>5.0}",
|
|
name,
|
|
format!("{}x{}", frame.width, frame.height),
|
|
native.len(),
|
|
mean(&native),
|
|
proxy.len(),
|
|
mean(&proxy),
|
|
);
|
|
|
|
n_native += native.len();
|
|
n_proxy += proxy.len();
|
|
px_native += native.iter().map(|f| f.crop_px).sum::<f32>();
|
|
px_proxy += proxy.iter().map(|f| f.crop_px).sum::<f32>();
|
|
}
|
|
|
|
println!(
|
|
"\ntotal: native {n_native} face(s), mean crop {:.0}px | \
|
|
1024 proxy {n_proxy} face(s), mean crop {:.0}px",
|
|
if n_native == 0 {
|
|
0.0
|
|
} else {
|
|
px_native / n_native as f32
|
|
},
|
|
if n_proxy == 0 {
|
|
0.0
|
|
} else {
|
|
px_proxy / n_proxy as f32
|
|
},
|
|
);
|
|
}
|