Files
DarkRoom/core/dr-face/src/align.rs
T
dtourolle 6b51726322 Read each face's eyes, and whether sunglasses hide them
Two MIT classifiers from the same author as the reference pipeline's
whole-body detector: OCEC answers P(open) for one 40×24 eye, SGC
P(sunglasses) for a 48×48 head. Both load in tract once their batch
dimension is pinned by tools/fix-face-model-shapes.sh, like the embedder.

The crops come through the same fitted similarity the aligned face does,
so an eye window is a constant in template units rather than a second
warp, and a tilted head yields an upright eye. Measured on 60 proxies
from the reference library: the eye window plateaus at 22×11, the S
variant beats M and L (which overfit their own domain), and for
sunglasses the aligned face beats a head framing but the higher of the
two catches 11 of 12 pairs against 9 for either alone.

The reading keeps both eyes and the sunglasses number apart, because a
wink averages to the least informative value and a lens of dark glass
draws a confident answer from the eye classifier — over a woman in
sunglasses it read the right eye 0.97 open. Sunglasses take precedence,
and a face behind them is neither open nor a blink.
2026-09-19 14:03:31 +02:00

1044 lines
40 KiB
Rust
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.
//! Five-point face alignment (docs/faces.md §5).
//!
//! ArcFace embeddings are trained on faces warped to a canonical 112×112
//! arrangement. Feeding the model a plain bounding-box crop *works* — it
//! produces 512 numbers, they are unit-norm, and cosine similarities between
//! them look entirely reasonable. They are just much worse, and nothing in the
//! system reports it.
//!
//! That is the whole reason this module exists, and the reason [`Aligned112`]
//! is a newtype only [`warp`] can construct: the mistake is not one a reviewer
//! catches, so the type system catches it instead.
//!
//! Model-free, so it builds and tests without the `inference` feature.
/// Canonical landmark positions for a 112×112 ArcFace crop.
///
/// # The naming is a trap; the order is not
///
/// Point 0 sits at x=38 on a 112-wide canvas — left of centre *in the image*,
/// which is the subject's **right** eye. Both namings are in circulation and
/// they are opposite, so the array is written in the detector's order and the
/// comment says whose left is whose:
///
/// ```text
/// 0 subject's right eye (image-left)
/// 1 subject's left eye (image-right)
/// 2 nose tip
/// 3 subject's right mouth corner
/// 4 subject's left mouth corner
/// ```
///
/// SCRFD emits its five points in this same order, so the correct amount of
/// reordering between detector and template is **none**. A detector with a
/// different order carries its own permutation beside its model id rather than
/// this constant growing an assumption.
pub const ARCFACE_TEMPLATE: [(f32, f32); 5] = [
(38.2946, 51.6963),
(73.5318, 51.5014),
(56.0252, 71.7366),
(41.5493, 92.3655),
(70.7299, 92.2041),
];
/// Edge of the aligned crop, in pixels. Fixed by the embedder's input.
pub const ALIGNED_EDGE: usize = 112;
/// A face warped to [`ARCFACE_TEMPLATE`], ready for the embedder.
///
/// Constructible only by [`warp`]. That is the point: an `Embedder` that took
/// a plain `&[f32]` would accept an unaligned bounding-box crop and silently
/// return worse embeddings, which is a failure no test of the embedder itself
/// would catch.
pub struct Aligned112 {
/// `112 × 112 × 3`, row-major RGB in `0.0..=1.0`.
pixels: Vec<f32>,
/// Source pixels across the crop before warping — `crop_px` in the catalog.
///
/// Carried here rather than recomputed later because the scale factor is
/// known exactly at warp time and only approximately from the box
/// afterwards. §7: it is the honest quality signal, and a feature in the
/// calibration.
source_px: f32,
}
impl Aligned112 {
pub fn pixels(&self) -> &[f32] {
&self.pixels
}
/// Source pixels spanned by the 112-pixel crop.
///
/// Below ~112 the face was upsampled to reach the embedder and the
/// embedding is correspondingly weaker; above it, downsampled and healthy.
pub fn source_px(&self) -> f32 {
self.source_px
}
/// How sharp the face the embedder is about to see actually is.
///
/// # Why size is not enough
///
/// A face can be large and useless. A subject walking through a half-second
/// exposure, a frame focused on the person behind them, a hand-held shot at
/// 1/15 — all yield a big box, a confident detection and five landmarks in
/// plausible places. The embedding that comes back is not *wrong* in any
/// way the system can see: it is unit-norm and its cosines look ordinary.
/// It is simply an embedding of a blur, and blurs resemble each other more
/// than they resemble the people they were, so they cluster together and
/// bridge identities that have nothing to do with one another.
///
/// That is the failure this exists to prevent, and it is the same class of
/// fault as the unaligned-crop one the [`Aligned112`] newtype guards
/// against: plausible output, no error, worse results, nothing reported.
///
/// # The measure
///
/// Variance of the Laplacian — the standard blur metric — **divided by the
/// variance of the luma it was taken over**. The division is what makes it
/// usable here. Raw Laplacian variance scales with contrast, so a sharp
/// face in flat, hazy or backlit light scores like a blurred one in hard
/// light, and a threshold on it would quietly throw away every face shot
/// against a bright sky. The ratio asks the question that actually matters
/// — *how much of this crop's variation is edges rather than broad
/// gradients* — and is invariant to exposure and contrast.
///
/// Computed on luma over the interior, so the 3x3 kernel never needs a
/// border rule. Returns 0.0 for a crop with no variation at all, which is
/// a flat patch and correctly unusable rather than infinitely sharp.
///
/// # This is not independent of size
///
/// A face smaller than 112 pixels was *upsampled* to reach the embedder,
/// and upsampling invents no edges — so a small face scores low here even
/// when the original was perfectly sharp. That is not a flaw to correct: it
/// is the honest statement that the embedder is looking at a soft image.
/// The size floor and this one overlap deliberately, and
/// `face_index --quality` prints the joint distribution so the two are
/// chosen together rather than each in ignorance of the other.
pub fn sharpness(&self) -> f32 {
let e = ALIGNED_EDGE;
let luma: Vec<f32> = self
.pixels
.chunks_exact(3)
.map(|p| 0.2126 * p[0] + 0.7152 * p[1] + 0.0722 * p[2])
.collect();
let (mut lap_sum, mut lap_sq) = (0.0_f64, 0.0_f64);
let (mut lum_sum, mut lum_sq) = (0.0_f64, 0.0_f64);
let mut n = 0.0_f64;
for y in 1..e - 1 {
for x in 1..e - 1 {
let i = y * e + x;
// Four-neighbour Laplacian. The 8-neighbour form is more
// sensitive to diagonal detail and also to noise, which on a
// high-ISO frame is exactly the thing that must not read as
// sharpness.
let lap = 4.0 * luma[i] - luma[i - 1] - luma[i + 1] - luma[i - e] - luma[i + e];
let lap = lap as f64;
lap_sum += lap;
lap_sq += lap * lap;
let l = luma[i] as f64;
lum_sum += l;
lum_sq += l * l;
n += 1.0;
}
}
if n == 0.0 {
return 0.0;
}
let lap_var = (lap_sq / n - (lap_sum / n).powi(2)).max(0.0);
let lum_var = (lum_sq / n - (lum_sum / n).powi(2)).max(0.0);
// A crop with no luma variation has no edges to find either, so the
// ratio is 0/0. Zero is the right answer: nothing there is a face.
if lum_var <= 1e-9 {
return 0.0;
}
(lap_var / lum_var) as f32
}
}
/// A similarity transform: rotation, uniform scale, translation.
///
/// Stored as the four independent parameters rather than a 2×3 matrix so that
/// [`Similarity::scale`] is readable without a decomposition.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Similarity {
a: f32,
b: f32,
tx: f32,
ty: f32,
}
impl Similarity {
/// `x' = a·x − b·y + tx`, `y' = b·x + a·y + ty`.
pub fn apply(&self, x: f32, y: f32) -> (f32, f32) {
(
self.a * x - self.b * y + self.tx,
self.b * x + self.a * y + self.ty,
)
}
/// Uniform scale factor — destination pixels per source pixel.
pub fn scale(&self) -> f32 {
(self.a * self.a + self.b * self.b).sqrt()
}
fn invert(&self, u: f32, v: f32) -> (f32, f32) {
let det = self.a * self.a + self.b * self.b;
let du = u - self.tx;
let dv = v - self.ty;
(
(self.a * du + self.b * dv) / det,
(-self.b * du + self.a * dv) / det,
)
}
}
/// Least-squares similarity transform from `src` onto `dst`.
///
/// # Why least squares and not RANSAC
///
/// The reference C++ implementation (docs/faces.md §1.1) fits this with
/// OpenCV's `estimateAffinePartial2D` under RANSAC. RANSAC over five points is
/// a strange fit: the minimal sample for a similarity is two, so it can discard
/// landmarks it judges outliers and solve from a subset — and on a profile face
/// the "outlier" is as likely to be the correct geometry as the wrong one.
/// InsightFace's own pipeline uses plain least squares over all five points,
/// which cannot silently drop anything, and that is what this is.
///
/// # The closed form
///
/// A 2-D similarity is linear in its four parameters:
///
/// ```text
/// x' = a·x − b·y + tx
/// y' = b·x + a·y + ty
/// ```
///
/// so this is an ordinary linear least-squares problem, not an SVD one.
/// Centring both point sets kills `tx`/`ty` from the normal equations and
/// leaves `a` and `b` as two dot products over a common denominator — which is
/// why there is no matrix decomposition anywhere in this function.
///
/// Returns `None` when the source points are degenerate (coincident or
/// collinear to within f32), which does happen: a detector firing on a
/// motion-blurred profile can put all five landmarks on a line.
pub fn fit_similarity(src: &[(f32, f32); 5], dst: &[(f32, f32); 5]) -> Option<Similarity> {
let n = 5.0_f32;
let (mut sx, mut sy, mut dx, mut dy) = (0.0, 0.0, 0.0, 0.0);
for i in 0..5 {
sx += src[i].0;
sy += src[i].1;
dx += dst[i].0;
dy += dst[i].1;
}
let (sx, sy, dx, dy) = (sx / n, sy / n, dx / n, dy / n);
let mut var = 0.0_f32;
let mut num_a = 0.0_f32;
let mut num_b = 0.0_f32;
for i in 0..5 {
let (px, py) = (src[i].0 - sx, src[i].1 - sy);
let (qx, qy) = (dst[i].0 - dx, dst[i].1 - dy);
var += px * px + py * py;
num_a += px * qx + py * qy;
num_b += px * qy - py * qx;
}
// Degenerate: every landmark on one point. Collinear input still solves,
// but with a scale that can be absurd, so the caller's sanity check on
// `scale()` is what catches that case.
if var <= f32::EPSILON {
return None;
}
let a = num_a / var;
let b = num_b / var;
if !a.is_finite() || !b.is_finite() || (a * a + b * b) <= f32::EPSILON {
return None;
}
Some(Similarity {
a,
b,
tx: dx - (a * sx - b * sy),
ty: dy - (b * sx + a * sy),
})
}
/// Warp a face onto the canonical 112×112 arrangement.
///
/// `rgb` is tightly packed `f32` RGB in `0.0..=1.0`, row-major — the same
/// convention `dr-segment` uses, so both read the same proxy.
///
/// Sampling is bilinear **from the source in one step**: never crop-then-warp,
/// which resamples twice and throws away detail the warp could have used.
/// Pixels falling outside the source read as black.
pub fn warp(
rgb: &[f32],
width: usize,
height: usize,
landmarks: &[(f32, f32); 5],
) -> Option<Aligned112> {
warp_pixels(Pixels::RgbF32(rgb), width, height, landmarks)
}
/// TRACES: FR-CULL-8
/// What the warp may sample, in whichever layout the caller already holds.
///
/// # Why the 8-bit variant exists
///
/// FR-CULL-8 requires the crop to come from the **native** render, and a native
/// render is large: a 24 MP frame is 96 MB as `RGBA8` and 288 MB converted to
/// the `f32` RGB this module was originally written against. Converting the
/// whole frame to sample 112×112 from it is three hundred megabytes allocated
/// to read about forty thousand pixels, per image, on a pass that runs over a
/// whole library — and on Android it is NFR-RES-2's budget spent outright.
///
/// So the warp reads whatever the caller has instead. It touches so few pixels
/// that the per-sample conversion is free, and the buffer never has to be
/// duplicated in another layout.
#[derive(Debug, Clone, Copy)]
pub enum Pixels<'a> {
/// Tightly packed `f32` RGB in `0.0..=1.0`, row-major.
RgbF32(&'a [f32]),
/// Tightly packed 8-bit RGBA, row-major. Alpha is ignored: a face crop has
/// no use for it and carrying it would change what the embedder receives.
Rgba8(&'a [u8]),
}
impl Pixels<'_> {
/// Whether the buffer is the size `width × height` implies.
fn fits(&self, width: usize, height: usize) -> bool {
match self {
Pixels::RgbF32(v) => v.len() == width * height * 3,
Pixels::Rgba8(v) => v.len() == width * height * 4,
}
}
/// One channel of one pixel, as `0.0..=1.0`. Outside the buffer reads black.
///
/// Public because the face *crop* stored for the People screen is cut from
/// the same buffer by the same caller, and it should not need a second
/// copy of this to do it.
pub fn channel(&self, w: usize, h: usize, x: isize, y: isize, c: usize) -> f32 {
if x < 0 || y < 0 || x >= w as isize || y >= h as isize {
return 0.0;
}
let i = y as usize * w + x as usize;
match self {
Pixels::RgbF32(v) => v[i * 3 + c],
Pixels::Rgba8(v) => v[i * 4 + c] as f32 / 255.0,
}
}
}
/// [`warp`], over any layout [`Pixels`] describes.
pub fn warp_pixels(
px: Pixels<'_>,
width: usize,
height: usize,
landmarks: &[(f32, f32); 5],
) -> Option<Aligned112> {
if !px.fits(width, height) {
return None;
}
let m = fit_similarity(landmarks, &ARCFACE_TEMPLATE)?;
let e = ALIGNED_EDGE;
let window = TemplateWindow {
x: 0.0,
y: 0.0,
w: e as f32,
h: e as f32,
};
Some(Aligned112 {
pixels: sample_window(px, width, height, &m, &window, e, e),
// The warp maps `scale` source pixels to one destination pixel, so the
// crop spans 112/scale of the source.
source_px: ALIGNED_EDGE as f32 / m.scale(),
})
}
/// A rectangle in **template** coordinates — the 112-unit frame
/// [`ARCFACE_TEMPLATE`] is written in — that a crop is sampled from.
///
/// Every crop this module makes is one of these resampled through the same
/// fitted similarity: the aligned face is the window `(0, 0, 112, 112)`, an
/// eye is a small window around its template point, a head is a window larger
/// than the face. Stating them all in one frame is what lets a second crop be
/// added as a constant rather than a second warp, and what keeps them
/// consistent with each other — the eye window sits where the eye landmark
/// lands *after* alignment, so a tilted face gets an upright eye.
#[derive(Debug, Clone, Copy, PartialEq)]
struct TemplateWindow {
x: f32,
y: f32,
w: f32,
h: f32,
}
/// Resample `window` of the template frame into an `out_w × out_h` RGB buffer.
///
/// Bilinear, from the source, in one step — the property [`warp`] insists on,
/// and every crop through here inherits it. The output pixel `(u, v)` is placed
/// at its centre in the window, taken back through `m` to source coordinates,
/// and sampled there; the window's aspect is **not** preserved when it differs
/// from the output's, which is deliberate for the eye classifier (it was
/// trained on detector boxes resized the same way) and moot for the others.
fn sample_window(
px: Pixels<'_>,
width: usize,
height: usize,
m: &Similarity,
window: &TemplateWindow,
out_w: usize,
out_h: usize,
) -> Vec<f32> {
let mut pixels = vec![0.0_f32; out_w * out_h * 3];
let sx = window.w / out_w as f32;
let sy = window.h / out_h as f32;
for v in 0..out_h {
for u in 0..out_w {
// Pixel centres, so the transform is not off by half a pixel —
// which is small enough to survive review and large enough to
// matter on a 40-pixel face.
let tx = window.x + (u as f32 + 0.5) * sx;
let ty = window.y + (v as f32 + 0.5) * sy;
let (x, y) = m.invert(tx, ty);
let (x, y) = (x - 0.5, y - 0.5);
let out = (v * out_w + u) * 3;
sample_bilinear(px, width, height, x, y, &mut pixels[out..out + 3]);
}
}
pixels
}
// ── eyes ──────────────────────────────────────────────────────────────────
/// Width of an eye crop as the classifier reads it, in pixels. Fixed by the
/// OCEC input (`docs/faces.md` §17): 40 wide, 24 high.
pub const EYE_PATCH_WIDTH: usize = 40;
/// Height of an eye crop as the classifier reads it, in pixels.
pub const EYE_PATCH_HEIGHT: usize = 24;
/// The window read around each eye, in template units: width and height.
///
/// The classifier was trained on the *eye* boxes of a whole-body detector —
/// tight boxes round the palpebral fissure, on the reference footage about
/// twice as wide as they are high — and this is that box expressed in the
/// aligned frame, where the two eyes sit 35 template units apart. A human eye
/// is close to half the interocular distance wide, so the first guess was
/// 20×10; measured over 25 clearly open-eyed faces from the reference
/// library (`examples/eyes.rs --eye`), recall was flat from 20×10 to 34×17
/// and fell off below it, and 22×11 was the best of the plateau. docs/faces.md
/// §17 has the table.
pub const EYE_WINDOW: (f32, f32) = (22.0, 11.0);
/// One eye, resampled to the classifier's input.
///
/// Constructible only by [`eye_patches`], for the reason [`Aligned112`] is
/// only constructible by [`warp`]: the classifier accepting a plain buffer
/// would accept any 40×24 of anything, and its answer would still be a
/// plausible probability.
#[derive(Debug, Clone, PartialEq)]
pub struct EyePatch {
/// `24 × 40 × 3`, row-major RGB in `0.0..=1.0`.
pixels: Vec<f32>,
}
impl EyePatch {
pub fn pixels(&self) -> &[f32] {
&self.pixels
}
}
/// Both eyes of one face, in the detector's landmark order.
#[derive(Debug, Clone, PartialEq)]
pub struct EyePatches {
/// The subject's **right** eye — image-left, landmark 0.
pub right: EyePatch,
/// The subject's **left** eye — image-right, landmark 1.
pub left: EyePatch,
}
/// Cut both eyes out of the source, aligned, at the classifier's size.
///
/// The same similarity [`warp`] fits, so the eyes come out upright whatever
/// the head's tilt, and the same one-step bilinear sampling from the native
/// buffer, so a large face gives the classifier real pixels rather than a
/// re-enlargement of the 112-pixel crop. A face too small for the window to
/// hold a real eye is not refused here: the classifier was trained down to
/// eyes a dozen pixels across, and the caller's size gate has already spoken.
pub fn eye_patches(
px: Pixels<'_>,
width: usize,
height: usize,
landmarks: &[(f32, f32); 5],
) -> Option<EyePatches> {
eye_patches_in(px, width, height, landmarks, EYE_WINDOW)
}
/// [`eye_patches`] over a window other than [`EYE_WINDOW`].
///
/// For measuring the window, which is how [`EYE_WINDOW`] was chosen
/// (`examples/eyes.rs --eye`); production callers use the constant.
pub fn eye_patches_in(
px: Pixels<'_>,
width: usize,
height: usize,
landmarks: &[(f32, f32); 5],
window: (f32, f32),
) -> Option<EyePatches> {
if !px.fits(width, height) {
return None;
}
let m = fit_similarity(landmarks, &ARCFACE_TEMPLATE)?;
let (ww, wh) = window;
let eye = |i: usize| {
let (cx, cy) = ARCFACE_TEMPLATE[i];
let window = TemplateWindow {
x: cx - ww / 2.0,
y: cy - wh / 2.0,
w: ww,
h: wh,
};
EyePatch {
pixels: sample_window(
px,
width,
height,
&m,
&window,
EYE_PATCH_WIDTH,
EYE_PATCH_HEIGHT,
),
}
};
Some(EyePatches {
right: eye(0),
left: eye(1),
})
}
// ── sunglasses ────────────────────────────────────────────────────────────
/// Edge of the crop the sunglasses classifier reads. Fixed by the SGC input:
/// 48×48.
pub const SUNGLASSES_EDGE: usize = 48;
/// The windows read for the sunglasses classifier, in template units:
/// `(x, y, w, h)`.
///
/// **Two framings, and the classifier's answer is the higher of the two.**
/// It was trained on a whole-body detector's *head* boxes, and a head box
/// is not reproducible from five landmarks: how much hair and hat it took in
/// depended on the person. So it is shown the face twice — once as the
/// aligned crop itself, once shifted up and widened to take in hair and
/// hat at the cost of the chin, which is roughly where a head box falls —
/// and a pair of sunglasses counts if it looks like one in either.
///
/// Measured over 12 faces in sunglasses and 28 with plainly visible eyes
/// from the reference library (`examples/eyes.rs --head`), at the 0.5
/// threshold:
///
/// | window | sunglasses found | clear eyes kept |
/// |---|---|---|
/// | the aligned face, `(0, 0, 112, 112)` | 9 | 28 |
/// | a head, `(-5, -14, 122, 122)` | 6 | 27 |
/// | a larger head, `(-30, -55, 172, 190)` | 6 | 25 |
/// | **the higher of the first two** | **11** | 27 |
///
/// The face-tight crop alone was the best single framing, which was not the
/// expectation; the head framing found the sunglasses under a cap that the
/// face crop missed. The one clear-eyed face the pair loses wears a cap and
/// clear glasses, at 0.68. Erring towards "sunglasses" is the safe direction
/// for what this feeds: a face called sunglasses is left alone by the
/// eyes-open filter, where a pair of sunglasses missed hands the eye
/// classifier a lens to guess at (docs/faces.md §17).
pub const SUNGLASSES_WINDOWS: [(f32, f32, f32, f32); 2] =
[(0.0, 0.0, 112.0, 112.0), (-5.0, -14.0, 122.0, 122.0)];
/// The framings of one face the sunglasses classifier is shown.
///
/// A newtype for the reason [`EyePatch`] is one.
#[derive(Debug, Clone, PartialEq)]
pub struct HeadViews {
/// Each `48 × 48 × 3`, row-major RGB in `0.0..=1.0`.
views: Vec<Vec<f32>>,
}
impl HeadViews {
pub fn views(&self) -> impl Iterator<Item = &[f32]> {
self.views.iter().map(Vec::as_slice)
}
}
/// Cut the [`SUNGLASSES_WINDOWS`] out of the source, aligned, at the
/// classifier's size.
pub fn head_views(
px: Pixels<'_>,
width: usize,
height: usize,
landmarks: &[(f32, f32); 5],
) -> Option<HeadViews> {
head_views_in(px, width, height, landmarks, &SUNGLASSES_WINDOWS)
}
/// [`head_views`] over windows other than [`SUNGLASSES_WINDOWS`].
///
/// For measuring them, which is how the constant was chosen
/// (`examples/eyes.rs --head`); production callers use the constant.
pub fn head_views_in(
px: Pixels<'_>,
width: usize,
height: usize,
landmarks: &[(f32, f32); 5],
windows: &[(f32, f32, f32, f32)],
) -> Option<HeadViews> {
if !px.fits(width, height) || windows.is_empty() {
return None;
}
let m = fit_similarity(landmarks, &ARCFACE_TEMPLATE)?;
let views = windows
.iter()
.map(|&(x, y, w, h)| {
let window = TemplateWindow { x, y, w, h };
sample_window(
px,
width,
height,
&m,
&window,
SUNGLASSES_EDGE,
SUNGLASSES_EDGE,
)
})
.collect();
Some(HeadViews { views })
}
fn sample_bilinear(px: Pixels<'_>, w: usize, h: usize, x: f32, y: f32, out: &mut [f32]) {
let x0 = x.floor();
let y0 = y.floor();
let fx = x - x0;
let fy = y - y0;
let x0 = x0 as isize;
let y0 = y0 as isize;
for (c, o) in out.iter_mut().enumerate() {
let get = |xi: isize, yi: isize| -> f32 { px.channel(w, h, xi, yi, c) };
let top = get(x0, y0) * (1.0 - fx) + get(x0 + 1, y0) * fx;
let bot = get(x0, y0 + 1) * (1.0 - fx) + get(x0 + 1, y0 + 1) * fx;
*o = top * (1.0 - fy) + bot * fy;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn shifted_scaled(scale: f32, dx: f32, dy: f32, rot: f32) -> [(f32, f32); 5] {
let (s, c) = (rot.sin(), rot.cos());
let mut out = [(0.0, 0.0); 5];
for (i, &(x, y)) in ARCFACE_TEMPLATE.iter().enumerate() {
out[i] = (scale * (c * x - s * y) + dx, scale * (s * x + c * y) + dy);
}
out
}
#[test]
fn template_onto_itself_is_the_identity() {
let m = fit_similarity(&ARCFACE_TEMPLATE, &ARCFACE_TEMPLATE).unwrap();
for &(x, y) in &ARCFACE_TEMPLATE {
let (u, v) = m.apply(x, y);
assert!((u - x).abs() < 1e-3, "{u} vs {x}");
assert!((v - y).abs() < 1e-3, "{v} vs {y}");
}
assert!((m.scale() - 1.0).abs() < 1e-4);
}
/// The property that matters: whatever similarity the face was seen under,
/// the fit must undo it and land the landmarks back on the template. This
/// is the test that fails if the transform is ever "simplified" into an
/// affine or a bare scale-and-translate.
#[test]
fn any_similarity_of_the_template_maps_back_onto_it() {
for &(scale, dx, dy, rot) in &[
(1.0_f32, 0.0_f32, 0.0_f32, 0.0_f32),
(2.5, 100.0, -40.0, 0.0),
(0.4, -12.0, 300.0, 0.6),
(1.7, 5.0, 5.0, -1.2),
] {
let observed = shifted_scaled(scale, dx, dy, rot);
let m = fit_similarity(&observed, &ARCFACE_TEMPLATE).unwrap();
for (i, &(tx, ty)) in ARCFACE_TEMPLATE.iter().enumerate() {
let (u, v) = m.apply(observed[i].0, observed[i].1);
assert!(
(u - tx).abs() < 1e-2 && (v - ty).abs() < 1e-2,
"scale={scale} rot={rot}: point {i} landed at ({u}, {v}), want ({tx}, {ty})"
);
}
assert!(
(m.scale() - 1.0 / scale).abs() < 1e-3,
"scale {} should invert {scale}",
m.scale()
);
}
}
#[test]
fn coincident_landmarks_are_rejected_rather_than_producing_a_crop() {
let degenerate = [(50.0, 50.0); 5];
assert!(fit_similarity(&degenerate, &ARCFACE_TEMPLATE).is_none());
let rgb = vec![0.5_f32; 64 * 64 * 3];
assert!(warp(&rgb, 64, 64, &degenerate).is_none());
}
#[test]
fn source_px_reports_the_face_size_the_embedder_actually_saw() {
let rgb = vec![0.5_f32; 400 * 400 * 3];
// A face twice the template's size spans 224 source pixels.
let big = shifted_scaled(2.0, 80.0, 80.0, 0.0);
let a = warp(&rgb, 400, 400, &big).unwrap();
assert!((a.source_px() - 224.0).abs() < 0.5, "{}", a.source_px());
// Half-size: 56 source pixels upsampled to 112, which §7 calls the
// degraded bucket.
let small = shifted_scaled(0.5, 10.0, 10.0, 0.0);
let a = warp(&rgb, 400, 400, &small).unwrap();
assert!((a.source_px() - 56.0).abs() < 0.5, "{}", a.source_px());
}
/// A white square on black, warped by a transform that should centre it:
/// checks the sampler's geometry rather than the fit's algebra.
#[test]
fn warp_resamples_the_right_pixels() {
let (w, h) = (224, 224);
let mut rgb = vec![0.0_f32; w * h * 3];
for y in 0..h {
for x in 0..w {
if (56..168).contains(&x) && (56..168).contains(&y) {
for c in 0..3 {
rgb[(y * w + x) * 3 + c] = 1.0;
}
}
}
}
// Landmarks placed so the fit is a pure translation of (56, 56):
// the white square maps exactly onto the 112×112 output.
let lm = shifted_scaled(1.0, 56.0, 56.0, 0.0);
let a = warp(&rgb, w, h, &lm).unwrap();
let px = a.pixels();
for (i, v) in px.iter().enumerate() {
assert!((v - 1.0).abs() < 1e-3, "pixel {i} is {v}, expected white");
}
}
/// A source whose red channel is its x coordinate and green its y, so a
/// crop's mean colour says where in the source it was taken from.
fn coordinate_image(w: usize, h: usize) -> Vec<f32> {
let mut rgb = vec![0.0_f32; w * h * 3];
for y in 0..h {
for x in 0..w {
rgb[(y * w + x) * 3] = x as f32 / w as f32;
rgb[(y * w + x) * 3 + 1] = y as f32 / h as f32;
}
}
rgb
}
fn mean_channel(px: &[f32], c: usize) -> f32 {
let n = px.len() / 3;
px.chunks_exact(3).map(|p| p[c]).sum::<f32>() / n as f32
}
/// The eye windows are cut where the landmarks say the eyes are, in the
/// detector's order — subject's right (image-left) first.
#[test]
fn eye_patches_are_cut_around_each_eye_landmark() {
let (w, h) = (224, 224);
let rgb = coordinate_image(w, h);
// Pure translation by (56, 56): template (x, y) is source (x+56, y+56).
let lm = shifted_scaled(1.0, 56.0, 56.0, 0.0);
let eyes = eye_patches(Pixels::RgbF32(&rgb), w, h, &lm).unwrap();
assert_eq!(
eyes.right.pixels().len(),
EYE_PATCH_WIDTH * EYE_PATCH_HEIGHT * 3
);
for (patch, (tx, ty)) in [
(&eyes.right, ARCFACE_TEMPLATE[0]),
(&eyes.left, ARCFACE_TEMPLATE[1]),
] {
let want_x = (tx + 56.0) / w as f32;
let want_y = (ty + 56.0) / h as f32;
let got_x = mean_channel(patch.pixels(), 0);
let got_y = mean_channel(patch.pixels(), 1);
assert!((got_x - want_x).abs() < 0.01, "x {got_x} vs {want_x}");
assert!((got_y - want_y).abs() < 0.01, "y {got_y} vs {want_y}");
}
// And the two are distinct eyes, the right one image-left of the left.
assert!(mean_channel(eyes.right.pixels(), 0) < mean_channel(eyes.left.pixels(), 0));
}
/// The window is wider than it is high in the source, and is resampled to
/// the classifier's 40×24 without keeping that aspect — the red channel
/// spans `EYE_WINDOW.0` source pixels across 40 output columns.
#[test]
fn an_eye_patch_spans_the_window_it_was_asked_for() {
let (w, h) = (224, 224);
let rgb = coordinate_image(w, h);
let lm = shifted_scaled(1.0, 56.0, 56.0, 0.0);
let eyes = eye_patches(Pixels::RgbF32(&rgb), w, h, &lm).unwrap();
let px = eyes.right.pixels();
let row = |v: usize| &px[v * EYE_PATCH_WIDTH * 3..(v + 1) * EYE_PATCH_WIDTH * 3];
let first = row(0)[0];
let last = row(0)[(EYE_PATCH_WIDTH - 1) * 3];
let span = (last - first) * w as f32;
// 39 pixel-centre steps across a 20-unit window.
let want = EYE_WINDOW.0 * (EYE_PATCH_WIDTH as f32 - 1.0) / EYE_PATCH_WIDTH as f32;
assert!((span - want).abs() < 0.1, "span {span} vs {want}");
}
/// A tilted face yields upright eyes: the patch's rows run along the
/// interocular line, not along the image's x axis.
#[test]
fn eye_patches_follow_the_heads_tilt() {
let (w, h) = (300, 300);
let rgb = coordinate_image(w, h);
let rot = 0.5_f32;
let lm = shifted_scaled(1.0, 100.0, 60.0, rot);
let eyes = eye_patches(Pixels::RgbF32(&rgb), w, h, &lm).unwrap();
let px = eyes.left.pixels();
// Walking one output row moves along the rotated x axis, so both
// source coordinates change, in the ratio the rotation dictates.
let a = &px[0..3];
let b = &px[(EYE_PATCH_WIDTH - 1) * 3..EYE_PATCH_WIDTH * 3];
let dx = (b[0] - a[0]) * w as f32;
let dy = (b[1] - a[1]) * h as f32;
let angle = dy.atan2(dx);
assert!(
(angle - rot).abs() < 0.02,
"row runs at {angle}, want {rot}"
);
}
/// The second sunglasses framing takes in more than the face — it starts
/// above the template's top edge and ends below its bottom — and the
/// first is the aligned face itself.
#[test]
fn the_head_views_are_the_face_and_a_wider_framing_of_it() {
let (w, h) = (300, 300);
let rgb = coordinate_image(w, h);
let lm = shifted_scaled(1.0, 100.0, 100.0, 0.0);
let head = head_views(Pixels::RgbF32(&rgb), w, h, &lm).unwrap();
let views: Vec<&[f32]> = head.views().collect();
let face = warp(&rgb, w, h, &lm).unwrap();
assert_eq!(views.len(), SUNGLASSES_WINDOWS.len());
for v in &views {
assert_eq!(v.len(), SUNGLASSES_EDGE * SUNGLASSES_EDGE * 3);
}
// The face view samples the same region as the aligned crop.
assert!((mean_channel(views[0], 0) - mean_channel(face.pixels(), 0)).abs() < 0.01);
assert!((mean_channel(views[0], 1) - mean_channel(face.pixels(), 1)).abs() < 0.01);
let (x, y, ww, hh) = SUNGLASSES_WINDOWS[1];
assert!(
x < 0.0 && y < 0.0,
"the window starts outside the face crop"
);
assert!(x + ww > ALIGNED_EDGE as f32, "and is wider than it");
assert!(y + hh < ALIGNED_EDGE as f32, "but stops short of the chin");
// Centred horizontally on the face, so the two share a mean x.
assert!((mean_channel(views[1], 0) - mean_channel(face.pixels(), 0)).abs() < 0.01);
// Its first row lies above the face's first row.
assert!(views[1][1] < face.pixels()[1]);
}
#[test]
fn degenerate_landmarks_yield_no_eye_or_head_crop() {
let rgb = vec![0.5_f32; 64 * 64 * 3];
let degenerate = [(50.0, 50.0); 5];
assert!(eye_patches(Pixels::RgbF32(&rgb), 64, 64, &degenerate).is_none());
assert!(head_views(Pixels::RgbF32(&rgb), 64, 64, &degenerate).is_none());
// And a buffer that is not the size it claims.
let lm = shifted_scaled(1.0, 0.0, 0.0, 0.0);
assert!(eye_patches(Pixels::RgbF32(&rgb), 60, 60, &lm).is_none());
}
#[test]
fn out_of_bounds_samples_read_black_rather_than_wrapping() {
let rgb = vec![1.0_f32; 32 * 32 * 3];
// Face far outside the image: every sample is out of bounds.
let lm = shifted_scaled(1.0, 5000.0, 5000.0, 0.0);
let a = warp(&rgb, 32, 32, &lm).unwrap();
assert!(a.pixels().iter().all(|&v| v == 0.0));
}
// ── sharpness ─────────────────────────────────────────────────────────
/// An image of `edge` square, filled by `f(x, y) -> luma`.
fn image(edge: usize, f: impl Fn(usize, usize) -> f32) -> Vec<f32> {
let mut v = Vec::with_capacity(edge * edge * 3);
for y in 0..edge {
for x in 0..edge {
let l = f(x, y);
v.extend_from_slice(&[l, l, l]);
}
}
v
}
/// One box-blur pass, which is enough to move the metric a long way.
fn blur(rgb: &[f32], edge: usize) -> Vec<f32> {
let mut out = rgb.to_vec();
for y in 1..edge - 1 {
for x in 1..edge - 1 {
for c in 0..3 {
let mut sum = 0.0;
for dy in -1isize..=1 {
for dx in -1isize..=1 {
let i = (((y as isize + dy) as usize) * edge
+ ((x as isize + dx) as usize))
* 3
+ c;
sum += rgb[i];
}
}
out[(y * edge + x) * 3 + c] = sum / 9.0;
}
}
}
out
}
/// Landmarks placing the template into a larger image at scale 1, so the
/// warp resamples one-to-one and the metric sees the source detail.
fn centred(edge: usize) -> [(f32, f32); 5] {
let off = (edge as f32 - ALIGNED_EDGE as f32) / 2.0;
shifted_scaled(1.0, off, off, 0.0)
}
#[test]
fn a_blurred_face_scores_lower_than_a_sharp_one() {
let edge = 200;
let sharp = image(
edge,
|x, y| if (x / 3 + y / 3) % 2 == 0 { 0.9 } else { 0.1 },
);
let soft = blur(&blur(&sharp, edge), edge);
let a = warp(&sharp, edge, edge, &centred(edge))
.unwrap()
.sharpness();
let b = warp(&soft, edge, edge, &centred(edge)).unwrap().sharpness();
assert!(a > b * 2.0, "sharp {a} should clearly beat blurred {b}");
}
/// The reason for dividing by luma variance. A sharp face photographed
/// against a bright sky is low-contrast, and a raw Laplacian variance would
/// reject it as blurred — which would quietly throw away every backlit
/// portrait in the library.
#[test]
fn both_pixel_layouts_warp_to_the_same_crop() {
// The 8-bit path exists so a native render need not be converted to
// f32 whole; it has to agree with the path it replaces to within the
// quantisation it introduces.
let (w, h) = (64usize, 64usize);
let mut rgba = vec![0u8; w * h * 4];
let mut rgb = vec![0.0f32; w * h * 3];
for y in 0..h {
for x in 0..w {
let v = [
(x * 4 % 256) as u8,
(y * 4 % 256) as u8,
((x + y) % 256) as u8,
];
for c in 0..3 {
rgba[(y * w + x) * 4 + c] = v[c];
rgb[(y * w + x) * 3 + c] = v[c] as f32 / 255.0;
}
rgba[(y * w + x) * 4 + 3] = 255;
}
}
let lm = shifted_scaled(0.35, 32.0, 32.0, 0.2);
let a = warp_pixels(Pixels::RgbF32(&rgb), w, h, &lm).unwrap();
let b = warp_pixels(Pixels::Rgba8(&rgba), w, h, &lm).unwrap();
assert_eq!(a.source_px(), b.source_px());
for (x, y) in a.pixels().iter().zip(b.pixels()) {
assert!((x - y).abs() < 1e-6, "{x} vs {y}");
}
}
#[test]
fn sharpness_survives_the_contrast_being_halved() {
let edge = 200;
let full = image(
edge,
|x, y| if (x / 3 + y / 3) % 2 == 0 { 0.9 } else { 0.1 },
);
// Same detail, half the contrast, lifted so it does not clip.
let flat = image(
edge,
|x, y| {
if (x / 3 + y / 3) % 2 == 0 {
0.55
} else {
0.45
}
},
);
let a = warp(&full, edge, edge, &centred(edge)).unwrap().sharpness();
let b = warp(&flat, edge, edge, &centred(edge)).unwrap().sharpness();
let ratio = a / b;
assert!(
(0.5..2.0).contains(&ratio),
"contrast changed the score {ratio}x ({a} vs {b})"
);
}
#[test]
fn a_flat_crop_has_no_sharpness() {
let edge = 200;
let flat = image(edge, |_, _| 0.5);
assert_eq!(
warp(&flat, edge, edge, &centred(edge)).unwrap().sharpness(),
0.0
);
}
/// Upsampling invents no detail, so a face that had to be stretched to
/// reach the embedder scores lower than the same face at full size. That
/// overlap with the size floor is deliberate and documented; this pins it
/// so a future change cannot quietly remove it.
#[test]
fn an_upsampled_face_scores_lower_than_the_same_face_at_full_size() {
let edge = 200;
let src = image(
edge,
|x, y| if (x / 3 + y / 3) % 2 == 0 { 0.9 } else { 0.1 },
);
let full = warp(&src, edge, edge, &centred(edge)).unwrap();
// Half scale: the crop spans 56 source pixels and is stretched to 112.
let off = (edge as f32 - ALIGNED_EDGE as f32 / 2.0) / 2.0;
let small = warp(&src, edge, edge, &shifted_scaled(0.5, off, off, 0.0)).unwrap();
assert!(small.source_px() < full.source_px());
assert!(
small.sharpness() < full.sharpness(),
"upsampled {} should be softer than full {}",
small.sharpness(),
full.sharpness()
);
}
}