From a6a1d2ed2221e3bfa82d055b35d93b482fae22d8 Mon Sep 17 00:00:00 2001 From: Gert Pellin - GPE Date: Sat, 17 Oct 2026 02:31:02 +0200 Subject: [PATCH] feat(algo): face detection with libfacedetection's CNN in pure Rust, and image.detectFaces (part of #3603) (#4513) photocraft_algo::faces::detect finds faces in an RGBA image with a score, a box and five landmarks, through a pure-Rust port of libfacedetection's YuNet network (Shiqi Yu, BSD-3-Clause; licence, NOTICE and ATTRIBUTION rows included). The weights are the library's, converted to half precision (107 KB), parsed defensively. image.detectFaces reports the document's faces to scripts and agents; it is read-only with no history step and no menu entry. The network is compiled out of the browser build, where the command reports itself unavailable. Head outputs are pinned against the C++ reference. Part of #3603. --- ATTRIBUTION.md | 1 + NOTICE | 1 + crates/algo/data/LICENSE-libfacedetection.txt | 35 ++ crates/algo/data/libfacedetection.bin | Bin 0 -> 106643 bytes crates/algo/src/faces.rs | 90 +++++ crates/algo/src/faces/cnn.rs | 323 ++++++++++++++++++ crates/algo/src/lib.rs | 1 + crates/engine/src/commands.rs | 1 + crates/engine/src/face_cmds.rs | 115 +++++++ crates/engine/src/lib.rs | 1 + 10 files changed, 568 insertions(+) create mode 100644 crates/algo/data/LICENSE-libfacedetection.txt create mode 100644 crates/algo/data/libfacedetection.bin create mode 100644 crates/algo/src/faces.rs create mode 100644 crates/algo/src/faces/cnn.rs create mode 100644 crates/engine/src/face_cmds.rs diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index 26eca62464..1ae19efa66 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -57,6 +57,7 @@ next to them. | `crates/ui-egui/src/theme.rs` (Solarized Dark palette) | Solarized colour scheme used by the Solarized Dark theme | Ethan Schoonover | | MIT, [`LICENSE-solarized.txt`](crates/ui-egui/src/LICENSE-solarized.txt) | | `crates/ui-egui/src/theme.rs` (Breeze Light and Breeze Dark themes) | The Breeze Light and Breeze Dark themes are original colour values on PhotoCraft's Studio layout, chosen to look close to KDE Plasma's Breeze (cool blue-greys, blue accent, 5 px frame radius) so the app blends into a Plasma desktop. None of Breeze's colour values, files or code are copied; the values are a few levels away from its schemes and tuned for text contrast | PhotoCraft contributors | Inspired by the look of KDE Breeze, (its colour schemes are LGPL-2.0-or-later) | Original work | | `crates/ui-egui/src/theme.rs` (Soft Dark and Soft Light themes) | Original colour values and radii for two low-contrast themes on PhotoCraft's Studio layout; no third-party palette, file or code is used | PhotoCraft contributors | Original | Original work | +| `crates/algo/data/libfacedetection.bin` (face detection weights, read by `crates/algo/src/faces.rs`, whose network code is a port of the library's) | libfacedetection CNN (YuNet design) weights and inference code, weights converted to half precision | Shiqi Yu | [ShiqiYu/libfacedetection](https://github.com/ShiqiYu/libfacedetection) (`src/facedetectcnn-data.cpp`, `facedetectcnn.cpp`, `facedetectcnn-model.cpp`) | BSD-3-Clause, [`LICENSE-libfacedetection.txt`](crates/algo/data/LICENSE-libfacedetection.txt) | ### Optional build input: craft-fonts (not files in this repo) diff --git a/NOTICE b/NOTICE index 887010d4a6..a7302a940c 100644 --- a/NOTICE +++ b/NOTICE @@ -16,6 +16,7 @@ Inter font (assets/fonts/Inter-*.ttf) — SIL Open Font License 1.1. See assets/ JetBrains Mono font (assets/fonts/JetBrainsMono-Regular.ttf) — SIL Open Font License 1.1. See assets/fonts/OFL-JetBrainsMono.txt. Lucide icons (assets/icons/*.svg, except slice-knife.svg) — ISC; icons derived from Feather also MIT. See assets/icons/LICENSE-lucide.txt. SCOWL English word list (assets/dict/en_US-scowl-50.txt.gz) — SCOWL license. See assets/dict/LICENSE-SCOWL.txt. +libfacedetection face detection weights and network (crates/algo/data/libfacedetection.bin, crates/algo/src/faces/cnn.rs) — BSD-3-Clause, Copyright (c) 2015-2019, Shiqi Yu, all rights reserved. See crates/algo/data/LICENSE-libfacedetection.txt. Builds made with craft-fonts (CRAFT_FONTS_DIR; all official releases) also embed the fonts listed in https://github.com/storytold/craft-fonts/blob/main/ATTRIBUTION.md (today BIZ UDPGothic, Shippori Mincho and diff --git a/crates/algo/data/LICENSE-libfacedetection.txt b/crates/algo/data/LICENSE-libfacedetection.txt new file mode 100644 index 0000000000..33abd64f06 --- /dev/null +++ b/crates/algo/data/LICENSE-libfacedetection.txt @@ -0,0 +1,35 @@ +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement For libfacedetection + (3-clause BSD License) + +Copyright (c) 2015-2019, Shiqi Yu, all rights reserved. +shiqi.yu@gmail.com + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. \ No newline at end of file diff --git a/crates/algo/data/libfacedetection.bin b/crates/algo/data/libfacedetection.bin new file mode 100644 index 0000000000000000000000000000000000000000..0941c4f689dae8012c98b16bbc530f2d59173a92 GIT binary patch literal 106643 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%0AH+ z^#ekmewvCHLavK=f_b!idWjMGZ62;z6wPda&fZz-MBoXz*s(Hs-CF;H_MGZM^IBRFIDph;n zXK-H0_WrSRga1~iQ+7C{vlT-Qc$A+tP}(zFh1r;@O%9Wv;C<0Qh3V|;6AT_vZu64Ijz+r9I>*;X z5e|I@@Kcs-#xI0^$@v@g5XU1&4dY^F=&+&;{G%z-V)uTQ&O>xp@#6i*5x)?ecHE}O z7yY-fM@W|Ht;~%eUm68E-}CK1^vI;+P82Xz@+T^i)%=`wC_};-Tg)sB9Ga^ojOp|)w zL{Km5#!&BGX|j8?d%nMlUBa&QG6ZSnDM7W^8~JC?YtU=Gpql0hz{bAG-h`%jjgoJJ z^XZ6quJ5k3&43qqf#_59@MJ}!0j^=JQk`|&IBn96)77$p}DO2JhCUU zcF?IQ9}Fs0;qBq;d9>=SfjOaIIH=w|a8r>R$_-pqoL3$V9S(JdQRVfJPV@P|(a4Fs zX02f0@L)ltNxL%gwR~mZm%tt6de!;hc(6XWMa>Kq(-f&w)H@R}rp)L{1~XQc>i+@U C?{l>P literal 0 HcmV?d00001 diff --git a/crates/algo/src/faces.rs b/crates/algo/src/faces.rs new file mode 100644 index 0000000000..aa246fcaf8 --- /dev/null +++ b/crates/algo/src/faces.rs @@ -0,0 +1,90 @@ +//! Face detection: a port of libfacedetection's CNN (Shiqi Yu, BSD-3; `data/LICENSE-libfacedetection.txt`). +//! +//! The network is the YuNet design: a backbone of 1×1 pointwise and 3×3 depthwise convolutions with +//! 2×2 max pooling, a feature pyramid at strides 8, 16 and 32, and per-cell heads for the face score, +//! the box and five landmarks (eyes, nose tip, mouth corners). The weights (`data/libfacedetection.bin`, +//! converted to half precision from the library's `facedetectcnn-data.cpp`) are about 100 KB, and +//! inference is plain Rust: no runtime, so it works offline. +//! +//! [`detect`] takes straight RGBA in `0..=1` and returns faces in pixel coordinates of that image. +//! +//! The browser build (wasm32) leaves the network and its weights out, as `photocraft-raw` does with +//! its JPEG XL decoder: they would push the wasm over the per-file cap of static hosts +//! (packaging/web/package.sh). There [`AVAILABLE`] is false and [`detect`] finds nothing. + +#[cfg(not(target_arch = "wasm32"))] +mod cnn; + +/// Whether this build can detect faces (not the browser build: see the module docs). +pub const AVAILABLE: bool = cfg!(not(target_arch = "wasm32")); + +/// Longest side the network looks at; larger images are scaled down first (faces smaller than +/// about 10 px at this scale are missed). +pub const MAX_SIDE: usize = 1280; + +/// One detected face, in pixels of the image passed to [`detect`]. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Face { + /// Confidence `0..=1` (the library reports faces above 0.5 as reliable). + pub score: f32, + /// Left, top, width and height. + pub rect: [f32; 4], + /// Right eye, left eye (as seen in the image: the first is on the image's left), nose tip, + /// right mouth corner, left mouth corner. + pub landmarks: [[f32; 2]; 5], +} + +/// Faces in `px` (straight RGBA `0..=1`, `w × h`, row-major) scoring at least `min_score`, best +/// first. Transparent pixels count as white. `None` when `px` doesn't match the size. +#[cfg(not(target_arch = "wasm32"))] +pub fn detect(px: &[[f32; 4]], w: usize, h: usize, min_score: f32) -> Option> { + if w == 0 || h == 0 || px.len() != w.checked_mul(h)? { + return None; + } + // Scale down (box filter) so the longer side is at most MAX_SIDE. + let step = w.max(h).div_ceil(MAX_SIDE).max(1); + let (sw, sh) = (w.div_ceil(step), h.div_ceil(step)); + let mut bgr = vec![[0.0f32; 3]; sw * sh]; + for y in 0..sh { + for x in 0..sw { + let (mut acc, mut n) = ([0.0f32; 3], 0.0f32); + for yy in y * step..((y + 1) * step).min(h) { + for xx in x * step..((x + 1) * step).min(w) { + let [r, g, b, a] = px[yy * w + xx]; + let a = a.clamp(0.0, 1.0); + for (k, v) in [b, g, r].into_iter().enumerate() { + acc[k] += v.clamp(0.0, 1.0) * a + (1.0 - a); + } + n += 1.0; + } + } + bgr[y * sw + x] = acc.map(|v| (v / n.max(1.0) * 255.0).round()); + } + } + let s = step as f32; + let faces = cnn::network(&bgr, sw, sh)? + .into_iter() + .filter(|f| f.score >= min_score) + .map(|f| Face { score: f.score, rect: f.rect.map(|v| v * s), landmarks: f.landmarks.map(|[x, y]| [x * s, y * s]) }) + .collect(); + Some(faces) +} + +/// The browser build has no detector (see the module docs): always `None`. +#[cfg(target_arch = "wasm32")] +pub fn detect(_px: &[[f32; 4]], _w: usize, _h: usize, _min_score: f32) -> Option> { + None +} + +#[cfg(all(test, not(target_arch = "wasm32")))] +mod tests { + use super::*; + + #[test] + fn no_faces_in_flat_or_tiny_images_and_bad_sizes_are_refused() { + assert_eq!(detect(&vec![[0.5, 0.5, 0.5, 1.0]; 64 * 48], 64, 48, 0.5), Some(vec![])); + assert_eq!(detect(&[[1.0, 1.0, 1.0, 1.0]], 1, 1, 0.5), Some(vec![])); + assert_eq!(detect(&[[1.0; 4]; 3], 2, 2, 0.5), None); + assert_eq!(detect(&[], 0, 0, 0.5), None); + } +} diff --git a/crates/algo/src/faces/cnn.rs b/crates/algo/src/faces/cnn.rs new file mode 100644 index 0000000000..b86cac7eb2 --- /dev/null +++ b/crates/algo/src/faces/cnn.rs @@ -0,0 +1,323 @@ +//! The network itself (native builds only: see the parent module's docs). + +use super::Face; +use crate::par; + +/// The weights: `PCFACE1\0`, the layer count (u16), then per layer its input channels (u16), filters +/// (u16) and kind (u8: 0 pointwise, 1 depthwise), the weights and the biases, all little-endian f16. +static WEIGHTS: &[u8] = include_bytes!("../../data/libfacedetection.bin"); + +const MAGIC: &[u8] = b"PCFACE1\0"; +const LAYERS: usize = 53; +/// Faces whose boxes overlap more than this (intersection over union) are one face. +const NMS_OVERLAP: f32 = 0.45; +/// Candidates kept before non-maximum suppression, as the library does. +const TOP_K: usize = 1000; + +struct Layer { + channels: usize, + filters: usize, + depthwise: bool, + weights: Vec, + biases: Vec, +} + +/// A feature map: `rows × cols` cells of `channels` values each. +#[derive(Clone)] +struct Blob { + rows: usize, + cols: usize, + channels: usize, + data: Vec, +} + +impl Blob { + fn new(rows: usize, cols: usize, channels: usize) -> Self { + Blob { rows, cols, channels, data: vec![0.0; rows * cols * channels] } + } +} + +fn f16_to_f32(h: u16) -> f32 { + let sign = if h & 0x8000 != 0 { -1.0 } else { 1.0 }; + let exp = i32::from((h >> 10) & 0x1f); + let frac = f32::from(h & 0x3ff); + match exp { + 0 => sign * frac * (2.0f32).powi(-24), + 31 => { + if frac == 0.0 { + sign * f32::INFINITY + } else { + f32::NAN + } + } + _ => sign * (1.0 + frac / 1024.0) * (2.0f32).powi(exp - 15), + } +} + +fn parse(bytes: &[u8]) -> Option> { + let rest = bytes.strip_prefix(MAGIC)?; + let mut at = 0usize; + let u16_at = |n: &mut usize| -> Option { + let b = rest.get(*n..*n + 2)?; + *n += 2; + Some(u16::from_le_bytes([b[0], b[1]])) + }; + let count = usize::from(u16_at(&mut at)?); + if count != LAYERS { + return None; + } + let mut layers = Vec::with_capacity(count); + for _ in 0..count { + let channels = usize::from(u16_at(&mut at)?); + let filters = usize::from(u16_at(&mut at)?); + let depthwise = *rest.get(at)? == 1; + at += 1; + let nw = if depthwise { 9 * channels } else { filters * channels }; + let mut read = |n: usize| -> Option> { (0..n).map(|_| u16_at(&mut at).map(f16_to_f32)).collect() }; + let weights = read(nw)?; + let biases = read(filters)?; + if depthwise && filters != channels { + return None; + } + layers.push(Layer { channels, filters, depthwise, weights, biases }); + } + (at == rest.len()).then_some(layers) +} + +/// A 1×1 or 3×3 depthwise convolution with bias, then ReLU when `relu`. +fn conv(x: &Blob, l: &Layer, relu: bool) -> Option { + if x.channels != l.channels { + return None; + } + let mut out = Blob::new(x.rows, x.cols, l.filters); + let (rows, cols, cin, cout) = (x.rows, x.cols, x.channels, l.filters); + par::chunks_mut(&mut out.data, cols * cout, rows * cols * cout > 1 << 16, |row, line| { + for col in 0..cols { + let o = &mut line[col * cout..(col + 1) * cout]; + if l.depthwise { + o.copy_from_slice(&l.biases); + for r in row.saturating_sub(1)..(row + 2).min(rows) { + for c in col.saturating_sub(1)..(col + 2).min(cols) { + let k = (r + 1 - row) * 3 + (c + 1 - col); + let input = &x.data[(r * cols + c) * cin..(r * cols + c + 1) * cin]; + let w = &l.weights[k * cin..(k + 1) * cin]; + for ((o, i), w) in o.iter_mut().zip(input).zip(w) { + *o += i * w; + } + } + } + } else { + let input = &x.data[(row * cols + col) * cin..(row * cols + col + 1) * cin]; + for (f, o) in o.iter_mut().enumerate() { + let w = &l.weights[f * cin..(f + 1) * cin]; + *o = l.biases[f] + input.iter().zip(w).map(|(i, w)| i * w).sum::(); + } + } + if relu { + for v in o.iter_mut() { + *v = v.max(0.0); + } + } + } + }); + Some(out) +} + +/// Pointwise then depthwise (ReLU after the depthwise one when `relu`). +fn conv_dp(x: &Blob, p: &Layer, d: &Layer, relu: bool) -> Option { + conv(&conv(x, p, false)?, d, relu) +} + +fn unit(x: &Blob, l: &[Layer]) -> Option { + let [p1, d1, p2, d2] = l else { return None }; + conv_dp(&conv_dp(x, p1, d1, true)?, p2, d2, true) +} + +fn max_pool(x: &Blob) -> Blob { + let rows = x.rows.saturating_sub(3).div_ceil(2) + 1; + let cols = x.cols.saturating_sub(3).div_ceil(2) + 1; + let mut out = Blob::new(rows, cols, x.channels); + for r in 0..rows { + for c in 0..cols { + let o = (r * cols + c) * x.channels; + for ch in 0..x.channels { + let mut m = f32::NEG_INFINITY; + for rr in r * 2..(r * 2 + 2).min(x.rows) { + for cc in c * 2..(c * 2 + 2).min(x.cols) { + m = m.max(x.data[(rr * x.cols + cc) * x.channels + ch]); + } + } + out.data[o + ch] = m; + } + } + } + out +} + +/// `small` upsampled ×2 (nearest) plus `big`. +fn up_add(small: &Blob, big: &Blob) -> Option { + if small.rows * 2 != big.rows || small.cols * 2 != big.cols || small.channels != big.channels { + return None; + } + let mut out = big.clone(); + let ch = big.channels; + for r in 0..big.rows { + for c in 0..big.cols { + let s = ((r / 2) * small.cols + c / 2) * ch; + let o = (r * big.cols + c) * ch; + for k in 0..ch { + out.data[o + k] += small.data[s + k]; + } + } + } + Some(out) +} + +/// The network's input: each cell holds the 3×3 neighbourhood (stride 2) of BGR values `0..=255`, +/// so the first, 3×3 stride-2 convolution is a pointwise one. The size is padded to 32. +fn input(bgr: &[[f32; 3]], w: usize, h: usize) -> Blob { + let rows = h.div_ceil(32) * 16; + let cols = w.div_ceil(32) * 16; + let mut b = Blob::new(rows, cols, 32); + for r in 0..rows { + for c in 0..cols { + let cell = (r * cols + c) * 32; + for fy in 0..3 { + let Some(y) = (r * 2 + fy).checked_sub(1).filter(|&y| y < h) else { continue }; + for fx in 0..3 { + let Some(x) = (c * 2 + fx).checked_sub(1).filter(|&x| x < w) else { continue }; + let o = cell + (fy * 3 + fx) * 3; + b.data[o..o + 3].copy_from_slice(&bgr[y * w + x]); + } + } + } + } + b +} + +/// The heads of one pyramid level, decoded to candidate faces (in network pixels). +fn candidates(feat: &Blob, l: &[Layer], heads: [usize; 4], stride: f32, out: &mut Vec) -> Option<()> { + let head = |i: usize| conv_dp(feat, l.get(i)?, l.get(i + 1)?, false); + let (cls, reg, obj, kps) = (head(heads[0])?, head(heads[1])?, head(heads[2])?, head(heads[3])?); + if cls.channels != 1 || obj.channels != 1 || reg.channels != 4 || kps.channels != 10 { + return None; + } + let sigmoid = |v: f32| 1.0 / (1.0 + (-v.clamp(-88.0, 88.0)).exp()); + for r in 0..feat.rows { + for c in 0..feat.cols { + let i = r * feat.cols + c; + let score = (sigmoid(cls.data[i]) * sigmoid(obj.data[i])).sqrt(); + if score < 0.2 { + continue; + } + let (px, py) = (c as f32 * stride, r as f32 * stride); + let b = ®.data[i * 4..i * 4 + 4]; + let (cx, cy) = (b[0] * stride + px, b[1] * stride + py); + let (bw, bh) = (b[2].exp() * stride, b[3].exp() * stride); + let k = &kps.data[i * 10..i * 10 + 10]; + let mut landmarks = [[0.0; 2]; 5]; + for (n, lm) in landmarks.iter_mut().enumerate() { + *lm = [k[2 * n] * stride + px, k[2 * n + 1] * stride + py]; + } + out.push(Face { score, rect: [cx - bw / 2.0, cy - bh / 2.0, bw, bh], landmarks }); + } + } + Some(()) +} + +fn iou(a: &[f32; 4], b: &[f32; 4]) -> f32 { + let w = ((a[0] + a[2]).min(b[0] + b[2]) - a[0].max(b[0])).max(0.0); + let h = ((a[1] + a[3]).min(b[1] + b[3]) - a[1].max(b[1])).max(0.0); + let i = w * h; + if i <= 0.0 { 0.0 } else { i / (a[2] * a[3] + b[2] * b[3] - i) } +} + +/// The heads (cls, reg, obj, kps: their first layer) of the stride 8, 16 and 32 levels. +const HEADS: [[usize; 4]; 3] = [[29, 35, 41, 47], [31, 37, 43, 49], [33, 39, 45, 51]]; + +/// The backbone and feature pyramid: the stride 8, 16 and 32 feature maps the heads read. +fn features(bgr: &[[f32; 3]], w: usize, h: usize, l: &[Layer]) -> Option<[Blob; 3]> { + let x = conv(&input(bgr, w, h), l.first()?, true)?; + let x = max_pool(&conv_dp(&x, l.get(1)?, l.get(2)?, true)?); + let x = unit(&unit(&x, l.get(3..7)?)?, l.get(7..11)?)?; + let f8 = unit(&max_pool(&x), l.get(11..15)?)?; + let f16 = unit(&max_pool(&f8), l.get(15..19)?)?; + let f32_ = unit(&max_pool(&f16), l.get(19..23)?)?; + let f32_ = conv_dp(&f32_, l.get(27)?, l.get(28)?, true)?; + let f16 = conv_dp(&up_add(&f32_, &f16)?, l.get(25)?, l.get(26)?, true)?; + let f8 = conv_dp(&up_add(&f16, &f8)?, l.get(23)?, l.get(24)?, true)?; + Some([f8, f16, f32_]) +} + +pub(super) fn network(bgr: &[[f32; 3]], w: usize, h: usize) -> Option> { + let l = parse(WEIGHTS)?; + let levels = features(bgr, w, h, &l)?; + let mut faces = Vec::new(); + // Heads (cls, reg, obj, kps) per level, as in the library's model. + for ((feat, heads), stride) in levels.iter().zip(HEADS).zip([8.0, 16.0, 32.0]) { + candidates(feat, &l, heads, stride, &mut faces)?; + } + faces.sort_by(|a, b| b.score.total_cmp(&a.score)); + faces.truncate(TOP_K); + let mut kept: Vec = Vec::new(); + for f in faces { + if kept.iter().all(|k| iou(&k.rect, &f.rect) <= NMS_OVERLAP) { + kept.push(f); + } + } + Some(kept) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn weights_parse() { + let l = parse(WEIGHTS).expect("bundled weights"); + assert_eq!(l.len(), LAYERS); + assert_eq!((l[0].channels, l[0].filters, l[0].depthwise), (32, 16, false)); + assert_eq!((l[2].channels, l[2].filters, l[2].depthwise), (16, 16, true)); + assert!(l.iter().all(|l| l.weights.iter().chain(&l.biases).all(|v| v.is_finite()))); + // Damaged weights are refused, not read past their end. + assert!(parse(&WEIGHTS[..WEIGHTS.len() - 1]).is_none()); + assert!(parse(b"nope").is_none()); + } + + /// The sum of every head's raw output on a fixed test image matches libfacedetection's own C++ + /// (computed with its f32 weights; ours are f16, hence the tolerance). + #[test] + fn heads_match_the_reference_implementation() { + let (w, h) = (96usize, 72usize); + let bgr: Vec<[f32; 3]> = (0..w * h) + .map(|i| { + let (x, y) = (i % w, i / w); + let (r, g, b) = ((x * 7 + y * 3) % 256, (x * x + y * 5) % 256, (x * y * 3 + 11) % 256); + [b as f32, g as f32, r as f32] + }) + .collect(); + let l = parse(WEIGHTS).unwrap(); + let levels = features(&bgr, w, h, &l).unwrap(); + let reference = + [27.647039, 549.579923, -1645.299678, 914.784787, -6.149318, 35.705980, -454.742148, 163.847111, 1.267630, 28.415509, -85.662257, 62.294208]; + let mut got = Vec::new(); + for (feat, heads) in levels.iter().zip(HEADS) { + for first in heads { + let o = conv_dp(feat, &l[first], &l[first + 1], false).unwrap(); + got.push(o.data.iter().map(|&v| f64::from(v)).sum::()); + } + } + for (g, r) in got.iter().zip(reference) { + assert!((g - r).abs() <= 0.01 * r.abs().max(10.0), "{got:?}"); + } + } + + #[test] + fn f16_decoding() { + assert_eq!(f16_to_f32(0x3c00), 1.0); + assert_eq!(f16_to_f32(0xc000), -2.0); + assert_eq!(f16_to_f32(0x0000), 0.0); + assert_eq!(f16_to_f32(0x3555), 0.333_251_95); + assert_eq!(f16_to_f32(0x0001), 2.0f32.powi(-24)); + } +} diff --git a/crates/algo/src/lib.rs b/crates/algo/src/lib.rs index 49ab15871c..c2ff6d2e6c 100644 --- a/crates/algo/src/lib.rs +++ b/crates/algo/src/lib.rs @@ -32,6 +32,7 @@ mod distort; mod distort2; pub mod erase; pub mod exif; +pub mod faces; pub mod features; mod fxutil; mod gallery; diff --git a/crates/engine/src/commands.rs b/crates/engine/src/commands.rs index 2f04187128..48b576dcb4 100644 --- a/crates/engine/src/commands.rs +++ b/crates/engine/src/commands.rs @@ -1422,6 +1422,7 @@ fn build() -> Vec { v.extend(crate::edit_menu_cmds::specs()); v.extend(crate::fill_key_cmds::specs()); v.extend(crate::sample_cmds::specs()); + v.extend(crate::face_cmds::specs()); v.extend(crate::sampler_cmds::specs()); v.extend(crate::brush_key_cmds::specs()); v.extend(crate::stamp_cmds::specs()); diff --git a/crates/engine/src/face_cmds.rs b/crates/engine/src/face_cmds.rs new file mode 100644 index 0000000000..d48df95b84 --- /dev/null +++ b/crates/engine/src/face_cmds.rs @@ -0,0 +1,115 @@ +//! Face detection (#3603): the faces in the document, with their box and five landmarks, from +//! [`photocraft_algo::faces`]. Select › Color Range's Detect Faces and Face-Aware Liquify read +//! faces through [`faces_in`]; `image.detectFaces` reports them to scripts and agents. + +use photocraft_algo::faces::{self, Face}; +use photocraft_doc::{Document, LayerId}; +use serde_json::{Value, json}; + +use crate::commands::CommandSpec; +use crate::{EngineError, Result, Session}; + +/// Faces scoring below this are left out unless asked for (libfacedetection's own threshold for a +/// reliable face). +pub const MIN_SCORE: f32 = 0.5; + +/// Why there are no faces in the browser build (see [`photocraft_algo::faces`]). +const UNAVAILABLE: &str = "face detection isn't available in the browser version of PhotoCraft; use the desktop app"; + +/// The faces in `px` (`w × h` straight RGBA), as [`faces::detect`] finds them. +pub fn detect(px: &[[f32; 4]], w: usize, h: usize, min_score: f32) -> Result> { + if !faces::AVAILABLE { + return Err(EngineError::Other(UNAVAILABLE.into())); + } + faces::detect(px, w, h, min_score).ok_or_else(|| EngineError::Other("face detection failed".into())) +} + +/// The faces in `doc`, best first: in the composite, or in the active layer's pixels when +/// `all_layers` is off. Coordinates are document pixels. +pub fn faces_in(doc: &Document, active: Option, all_layers: bool, min_score: f32) -> Result> { + let area = doc.bounds(); + let (w, h) = (area.width() as usize, area.height() as usize); + let surface = active.and_then(|id| doc.layer(id)).and_then(|l| l.surface()).filter(|_| !all_layers); + let px = match surface { + Some(surf) => { + let mut px = vec![[0.0f32; 4]; w * h]; + surf.read_rgba_into(area, &mut px); + px + } + None => photocraft_compose::render(doc, area).px, + }; + let found = detect(&px, w, h, min_score)?; + let (x0, y0) = (area.x0 as f32, area.y0 as f32); + Ok(found + .into_iter() + .map(|f| Face { rect: [f.rect[0] + x0, f.rect[1] + y0, f.rect[2], f.rect[3]], landmarks: f.landmarks.map(|[x, y]| [x + x0, y + y0]), ..f }) + .collect()) +} + +fn face_json(f: &Face) -> Value { + let pt = |i: usize| json!([f.landmarks[i][0], f.landmarks[i][1]]); + json!({ + "score": f.score, + "rect": f.rect, + "landmarks": {"rightEye": pt(0), "leftEye": pt(1), "nose": pt(2), "mouthRight": pt(3), "mouthLeft": pt(4)}, + }) +} + +fn detect_faces(s: &mut Session, p: &Value) -> Result { + const CMD: &str = "image.detectFaces"; + let bad = |msg: &str| EngineError::BadParams { cmd: CMD.into(), msg: msg.into() }; + let min_score = match p.get("minScore") { + None => MIN_SCORE, + Some(v) => v.as_f64().filter(|v| (0.0..=1.0).contains(v)).ok_or_else(|| bad("minScore must be a number in 0..1"))? as f32, + }; + let all_layers = match p.get("sampleAllLayers") { + None => true, + Some(v) => v.as_bool().ok_or_else(|| bad("sampleAllLayers must be true or false"))?, + }; + let d = s.active().ok_or(EngineError::NoDocument)?; + let found = faces_in(&d.doc, d.active_layer, all_layers, min_score)?; + Ok(json!({ "faces": found.iter().map(face_json).collect::>() })) +} + +pub fn specs() -> Vec { + vec![CommandSpec { + id: "image.detectFaces", + label: "Detect Faces", + menu: &[], + shortcut: None, + params: r##"{"minScore":0..1=0.5,"sampleAllLayers":bool=true} → {"faces":[{"score","rect":[x,y,w,h],"landmarks":{"rightEye","leftEye","nose","mouthRight","mouthLeft"}: [x,y] each},…]} (best first; "rightEye" is the one on the image's left)"##, + enabled: |s| { + if !faces::AVAILABLE { + return Err(UNAVAILABLE.into()); + } + s.active().map(|_| ()).ok_or_else(|| "no document open".into()) + }, + run: detect_faces, + journal: false, + }] +} + +#[cfg(test)] +mod tests { + use serde_json::json; + + use crate::Session; + + #[test] + fn detect_faces_reports_no_faces_on_a_plain_image_and_validates_params() { + let mut s = Session::new(); + assert!(s.execute("image.detectFaces", json!({})).is_err(), "no document"); + assert!(!s.is_enabled("image.detectFaces")); + s.execute("file.new", json!({"width": 64, "height": 48, "background": "white"})).unwrap(); + assert!(s.is_enabled("image.detectFaces")); + s.execute("select.all", json!({})).unwrap(); + assert_eq!(s.execute("image.detectFaces", json!({})).unwrap(), json!({"faces": []})); + assert_eq!(s.execute("image.detectFaces", json!({"minScore": 0, "sampleAllLayers": false})).unwrap(), json!({"faces": []})); + // No history step of its own: Undo undoes the Select All before it. + s.execute("edit.undo", json!({})).unwrap(); + assert!(s.active().unwrap().doc.selection.is_none()); + for bad in [json!({"minScore": 2}), json!({"minScore": "high"}), json!({"sampleAllLayers": 1})] { + assert!(s.execute("image.detectFaces", bad.clone()).is_err(), "{bad}"); + } + } +} diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index b8d6167c3d..0bf4d1b40f 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -41,6 +41,7 @@ pub mod eraser_cmds; pub mod export_preset_cmds; pub mod exr_cmds; pub mod extra_cmds; +pub mod face_cmds; pub mod file_cmds; pub mod fill_cmds; pub mod fill_key_cmds;