Skip to content

About

GPU-accelerated face detection, cropping, enhancement, and batch export tools in Rust.

Topics

Resources

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

Face Crop Studio

GPU accelerated face detection and cropping software built in Rust.

🌐 Official Website: https://facecropstudio.com

Rust WESL CUDA SCRFD-80k License Models CI Release

Face Crop Studio is a Rust workspace that wraps a face detector with deterministic cropping, quality analysis, enhancement, and export tooling. The project ships both a command-line workflow and an egui desktop application, backed by shared utilities for image processing, metadata handling, and configuration. Preprocessing runs on the CPU; inference and enhancement support both built-in CPU paths and GPU acceleration via wgpu/WGSL compute shaders.

The detector is SCRFD-80k, trained by this project on 80,000 CC BY 2.0 Open Images photographs, so the shipped weights carry no non-commercial restriction. It runs on WGSL compute shaders, falling back to a built-in Rust CPU graph. Neither detection nor eye refinement loads an ONNX Runtime library.

Model comparison

A small detector, with a separate eye refiner for precise alignment. On 1,859 held-out Open Images photographs containing 4,597 annotated face boxes, SCRFD-80k was competitive with the RetinaFace ResNet-34 weights found in FaceCropJet, at a fraction of the model size and CPU inference cost.

Detection recall across annotation-mismatch budgets, and paired eye-line error for SCRFD-80k, public SCRFD-500M and RetinaFace R34

At an equal budget of 0.1 annotation-unmatched detections per image:

Detector Annotated face recall Median CPU time / image Detector weights
Face Crop Studio SCRFD-80k 76.9% 12.2 ms 2.55 MB
RetinaFace R34 (FaceCropJet weights) 73.9% 115.6 ms 88.50 MB
Public SCRFD-500M 68.8% 11.2 ms 2.52 MB

On the same 1,318 faces found by all three detectors, SCRFD-80k plus the eye refiner achieved 1.18° median eye-line error, versus 1.81° for RetinaFace and 2.02° for public SCRFD-500M. 94.1% of refined eye lines were within 5° of the hand-clicked labels. Refinement added a median 1.49 ms per face in this benchmark.

On COCO 2017, which none of the three models trained on (23,883 hand-labelled faces, with the same thresholds), SCRFD-80k found the most faces with eyes at least 8 px apart: 84.8%, against 78.3% for RetinaFace. But it also fired about five times as often on photos with no people in them, mostly on animals and round objects like clocks. At equal false-positive rates, RetinaFace found the most faces and SCRFD-80k the fewest. With the eye refiner, SCRFD-80k still had the lowest eye-line error, at 2.03° median.

Scope: These are model benchmarks using ONNX Runtime on the same CPU at 640 × 640, not timings of either finished application or the built-in Rust/WGSL engines. Unmatched detections include real faces missing from the annotations, so the Open Images ranking is provisional. Those held-out images share the Open Images domain used for SCRFD-80k training, and on COCO the ranking at equal false-positive rates reverses.

Full results, hardware, methodology and model hashes.

Crates

  • fcs-core – Loads the detector, decodes its outputs, and implements the crop calculation logic. Holds the WGSL and pure-Rust CPU inference engines (see ARCHITECTURE.md for details).
  • fcs-ort – Development-only ONNX Runtime oracle for parity tests and benchmarks; excluded from application dependencies and release packages.
  • fcs-utils – Shared helpers: configuration structs, Laplacian-variance quality scoring, enhancement pipeline (CPU and GPU paths with 7 WGSL compute shaders), and output encoders with metadata support.
  • fcs-mapping – Tabular ingestion for the batch mapping workflow: reads CSV, Excel, Parquet, and SQLite tables into source-image/output-name pairs. Standalone — it depends on no other workspace crate.
  • fcs-cli – Command-line frontend aimed at batch processing and automation with optional GPU acceleration (auto-detected, with fallback to CPU). Shares one GPU context across enhancement and shape-mask work in a batch. Example invocations are documented in docs/cli_recipes.md.
  • fcs-gui – eframe/egui desktop experience with live preview, crop adjustments, enhancements, history/undo, and batch export. Shares GPU context with eframe's wgpu backend for efficient rendering and compute. A user guide lives in docs/gui_crop_guide.md.

Architecture Diagrams

High-level views of how the workspace fits together. Each image links to its editable .drawio source for editing in draw.io.

Workspace architecture: CLI and GUI entry points, shared detection, mapping and image-processing crates, development-only ONNX Runtime oracle, and external dependencies
Workspace architecture - crate dependency layers and the key modules in each crate.

Per-image detection and crop pipeline: load and orient the image, letterbox it, run SCRFD on WGSL or the built-in CPU graph, decode and suppress detections, optionally refine the eyes on the CPU, and crop faces
Detection pipeline - per-image flow from image load through preprocessing, inference, postprocessing, and per-face cropping.

Crop Features Overview

  • Preset sizing – LinkedIn, Passport, Instagram, ID Card, Avatar, Headshot, plus an explicit “Custom” mode.
  • Face height targeting – Configure how large the face should appear in the final crop (10–100%). The cropper preserves the requested aspect ratio and records any padding needed so crops can extend beyond the source image without distortion.
  • Padding colour control – Empty pixels introduced by out-of-bounds crops, and the area outside a crop shape, are filled with a configurable colour (zero alpha keeps them transparent) (CLI --crop-fill-color, GUI color picker/hex/RGB/HSV inputs). Black remains the default.
  • Positioning modes – Center, Rule of Thirds, or fully custom offsets with keyboard nudges and undo/redo support.
  • Eye-line alignment – Optionally rotates each crop so the subject's eyes sit level (CLI --eye-line-align, GUI toggle). A bundled 1.5 M-parameter refiner runs on the built-in Rust CPU engine. In the paired model comparison, SCRFD-80k plus the refiner achieved 1.18° median eye-line error, with 94.1% of faces within 5° of hand-clicked ground truth. No ONNX Runtime library is needed; if the model is missing or incompatible, the detector's own points are used.
  • Quality automation – Laplacian-variance scoring categorises crops into Low/Medium/High. Filters can auto-select the sharpest face, skip soft captures, and append quality suffixes.
  • Enhancement pipeline – Optional post-crop adjustments (auto colour, exposure, brightness, contrast, saturation, sharpening, skin smoothing, red-eye removal, and portrait background blur) with both CPU (pure Rust) and GPU (WGSL compute shaders) implementations.
  • Camera RAW input – DNG, CR2, CR3, NEF, ARW, RW2, ORF, RAF, SRW, and PEF decode via a pure-Rust pipeline in both CLI and GUI, alongside the standard formats (PNG, JPEG, WebP, TIFF, BMP, AVIF, HEIC). Unsupported DNG variants are skipped rather than crashing a batch.
  • Metadata & export – Preserve, strip, or customise metadata. Exports support PNG, JPEG (with quality controls), and WebP.
  • Batch processing – Both CLI and GUI support multi-image workflows with status tracking, filenames derived from templates, and quality-aware selection.
  • Clipboard & drag-and-drop import – Drop/paste a single image to preview instantly, drop a folder/clipboard path list to enqueue its supported images for batch export, or drop CSV/XLSX/Parquet/SQLite tables to seed the mapping workflow.

GPU Acceleration

The project includes comprehensive GPU acceleration via wgpu and WGSL compute shaders:

  • Enhancement shaders – 7 WGSL compute pipelines: pixel adjustments (exposure/brightness/contrast/saturation), histogram equalization, Gaussian blur, bilateral filter (skin smoothing), red-eye removal, background blur, and shape masking. Both front-ends call the same runtime (fcs_utils::EnhancementRuntime), which falls back to the identical CPU pipeline when no GPU is available.
  • Custom inference – Full GPU implementation of the detector in WGSL (Conv2D, pooling, add, upsample, activations), measured within about 1e-05 of ONNX Runtime on one input; the parity tests gate at 2e-3 (CPU graph) and 5e-3 (GPU). It exists so detection works with no external runtime, not because it is faster: measured, the two are a tie (docs/ENGINE_SPEED.md).
  • GPU contexts – The CLI opens one context for enhancement; the GUI shares eframe's. The detector opens its own, under the same settings.
  • Auto-detection – Both CLI and GUI automatically detect GPU availability and fall back to CPU when necessary. --no-gpu (or the GUI's GPU toggle) keeps enhancement and detection on the CPU.

Experimental CUDA Rust: on compatible NVIDIA GPUs on Windows and Linux, enable Settings → GPU → CUDA Rust (experimental) and confirm the warning, or pass --experimental-cuda to the CLI. It is off by default, requires compute capability 8.0+ and a compatible NVIDIA driver, and accelerates detection, enhancement and crop masking. Unsupported hardware cannot enable it; CUDA initialization errors are reported explicitly. macOS and other GPU vendors continue using the standard WESL/CPU paths. See the CUDA experiment notes for driver requirements, testing, and rebuilding the Rust kernels.

Batch worker threads

The timings below are historical measurements. The SCRFD sweep used the ONNX Runtime application backend in 1.9.0, before it became a development-only oracle; it does not measure the current built-in CPU and WGSL batch paths.

Batch detection runs through Rayon, which defaults to one worker per logical processor. On the GPU path that overshoots: only the GPU dispatches serialise, but the work around them — JPEG decode, RGBA conversion, PNG encode — is CPU-bound, and past a point the extra workers contend rather than help.

Measured on 968 images (Ryzen 9 7950X, 16 cores / 32 threads, RTX 4090):

Rayon threads 4 8 12 16 20 24 32
Wall clock 15.6 s 11.5 s 11.4 s 11.6 s 12.8 s 13.0 s 14.0 s

Throughput was flat from 8 to 16 and fell away above the physical core count, so Rayon's default of 32 cost about 17% on this machine.

That stopped being true once the batch path got about 2.4x faster, and experiment 60 measured the default winning every alternated pair against 16 by about 12% (32 threads at 7.85 s against 8.9 s).

It has now inverted a second time. Re-measured on 2026-09-21 with SCRFD in place of YuNet, same 1239 images, warm, order alternated:

Rayon threads 1 4 8 16 32 (default)
Wall clock 54.8 s 17.0 s 11.5 s 10.14 / 10.55 s 10.87–11.05 s

16 now wins both alternated pairs by 5–8%. It is not the atomic write added in 2.0: with nothing written at all, 16 threads take 8.79 s against 32 at 9.73 s, so the change is in the detection path — which is precisely the part that was replaced.

Two measurements in a row have reversed this ranking as soon as the work around it changed, which is the real lesson: treat any number here as describing the build it was taken on. The wider finding is that batch scaling is poor whichever you pick — 5.4x on 16 physical cores, about 34% efficiency — and docs/ENGINE_SPEED.md records what has been ruled out as the cause.

RAYON_NUM_THREADS still overrides the default if a particular machine wants something else. No automatic cap is applied, and the case for one is weaker than it was: the figure that looked stable across two measurements moved as soon as the surrounding work changed, and "physical cores" is not a meaningful number on hybrid CPUs, where performance and efficiency cores are not interchangeable.

For comparison, the same sweep on the CPU path: 40.4 s at 4 threads, 18.4 s at 8, 14.6 s at 16, 14.4 s at 32 — the GPU path is ahead at every thread count, by the widest margin (2.5x) on the fewest cores.

Requirements

  • Hardware: Vulkan 1.2+, DirectX 12, or Metal-capable GPU.
  • Drivers: Up-to-date graphics drivers (NVIDIA 530+, AMD 23.x+, Intel 31.x+).
  • Fallback: System automatically falls back to CPU if no compatible adapter is found.

Mapping-driven Workflows

  • Source->Output mappings - Import CSV/TSV, Excel (XLS/XLSX), Parquet, or SQLite datasets via the CLI with --mapping-file, column selectors, and header/sheet/delimiter/query options.
  • Live preview in the GUI - Use the Mapping Import panel to choose a file, pick the source/output columns, and inspect a truncated preview before queueing rows.
  • Batch-aware overrides - Batch exports respect mapping-provided output names (including nested folders) while still falling back to the existing naming template when no mapping is configured.

Supported Architectures

Official release binaries are built for:

Platform Architecture Artifacts
Windows x86_64 MSI, EXE installer, portable ZIP, Microsoft Store
macOS aarch64 (Apple Silicon) DMG (.app bundle, GUI only)
Linux x86_64 AppImage, .deb
Linux arm64 AppImage, .deb

Every architecture is built natively — there is no cross-compilation in the release pipeline.

Windows on Arm uses the x86_64 build under emulation; there is no native Windows arm64 binary yet. The former tract-linalg build blocker was removed with that dependency, but native Windows arm64 builds, dependencies, and installers still need validation. Linux arm64 is built natively.

All x86_64 binaries are compiled for the x86-64-v3 microarchitecture level (AVX2/FMA — Intel Haswell 2013+ or AMD Excavator 2015+). Older CPUs need a local build with -C target-cpu=x86-64 (edit .cargo/config.toml). arm64 binaries use the base AArch64 target, which already includes NEON and FMA. Intel Macs (x86_64) are not shipped as binaries but build from source.

The Windows MSI, NSIS installer, and Store package currently ship x86_64 binaries only.

Prerequisites

  • Rust 1.99.0: Matches the workspace minimum Rust version and the CI/release toolchain.
  • NASM: You must have NASM installed and added to your system PATH to build the project.

Installation (Windows)

The easiest option is the Microsoft Store listing, which keeps itself updated. To install manually instead:

  1. Download either:

    • face-crop-studio-windows-x86_64.msi (enterprise installer), or
    • face-crop-studio-windows-x86_64-setup.exe (installer), or
    • face-crop-studio-windows-x86_64.zip (portable package), plus SHA256SUMS-windows-x86_64.txt from the GitHub Release page.

    Windows-on-Arm devices use these same x86_64 downloads and run them under emulation.

  2. Verify checksum in PowerShell:

    Get-FileHash .\face-crop-studio-windows-x86_64.msi -Algorithm SHA256
    Get-FileHash .\face-crop-studio-windows-x86_64.zip -Algorithm SHA256
  3. Confirm it matches the hash in SHA256SUMS-windows-x86_64.txt.

  4. If you downloaded the MSI, install with msiexec /i face-crop-studio-windows-x86_64.msi.

  5. If you downloaded the EXE installer, run face-crop-studio-windows-x86_64-setup.exe.

  6. If you downloaded the zip, extract it to a folder of your choice.

  7. Run:

    • fcs-gui.exe for the desktop app
    • fcs-cli.exe --help for CLI usage

The release package includes models/scrfd80k_500m_640.onnx by default, so detection works out-of-the-box without manually selecting a model path.

Microsoft Store

The Store listing ships an x86_64 .msixbundle (see the Windows-on-Arm note above). It carries the same two binaries as the other packages — the GUI plus fcs-cli, which the package registers as an execution alias so it works from any terminal without a PATH edit.

Store builds are packaged as full-trust desktop apps, so file access behaves exactly like the MSI and ZIP installs: batch mode can walk any directory you point it at, with no MSIX container restrictions.

Screenshots

Face Crop Studio desktop workspace with a loaded portrait, face detections, crop preview, queue, and export controls
Desktop workspace - loaded image, detection overlays, crop preview, queue, and export controls.

Crop configuration panel showing presets, sizing, shape, padding, and positioning controls
Crop setup
Presets, target sizing, shape, padding, and positioning.
Enhancement controls for exposure, brightness, contrast, saturation, sharpening, smoothing, and background blur
Enhancement controls
Post-crop colour, sharpness, smoothing, and blur tuning.
Batch processing queue with image status, queue controls, and export buttons
Batch queue
Multi-image queue management and export progress.
CLI automation example

Terminal run showing Face Crop Studio CLI output, detection metadata, crop coordinates, quality score, and summary
Batch-friendly CLI output with detection metadata, crop coordinates, quality scoring, and processing summary.

Workflow recordings

Animated walkthroughs of the main workflows. See screenshots/README.md for the recording checklist and filenames; once the GIFs are added, uncomment the block below to publish them.

Development Tasks

  • cargo check --workspace – Fast type checking across all crates.
  • cargo test --workspace --all-features – Run the full test suite (requires models/scrfd80k_500m_640.onnx; set FCS_STRICT_TESTS=1 to fail rather than skip when it is missing).
  • cargo test -p fcs-core --test detection_accuracy -- --nocapture – Score the shipped detector against human-drawn boxes on 60 held-out Open Images photographs (fixtures/images/). Fails if recall drops or unmatched detections rise, which the engine-parity tests cannot see.
  • cargo test -p fcs-core --test python_parity – Compare the Rust detector against the Python implementation it was ported from, on committed CC BY 2.0 fixtures. The only test here that checks this project against something it did not write; see fixtures/oracle/ATTRIBUTION.md.
  • cargo run -p fcs-cli -- --help – View CLI options.
  • cargo run --release -p fcs-core --example engine_speed -- <image> – What a detection costs, per engine (see docs/ENGINE_SPEED.md).
  • cargo run -p fcs-cli -- --input fixtures/ --gpu – Run with explicit GPU acceleration.
  • cargo run -p fcs-cli -- --input fixtures/ --no-gpu – Run with CPU-only mode.
  • cargo run -p fcs-cli -- --watch inbox/ --crop --output-dir out/ – Watch a folder and crop each image as it lands. Files already in inbox/ are left alone; run the same command with --input inbox/ to process those.
  • cargo run -p fcs-cli -- --input portraits/ --crop --eye-line-align --output-dir out/ – Crop with the eyes levelled in every output.
  • cargo run -p fcs-gui – Launch the GUI with default settings (auto-detects GPU).
  • cargo bench -p fcs-core crop_enhance – Measure the crop + enhancement micro-benchmark.
  • cargo fmt --all && cargo clippy --workspace -- -D warnings – Formatting and linting hygiene. Note this only lints the platform you are on: code behind #[cfg(target_os = ...)] is invisible to the compiler everywhere else, which is why CI runs clippy on all three legs rather than just one. See Cross-platform linting.

Cross-platform linting

A local cargo clippy only sees the cfg blocks that your own platform compiles. To lint another platform's branches without waiting for CI:

rustup target add x86_64-unknown-linux-gnu
cargo clippy --workspace --target x86_64-unknown-linux-gnu -- -D warnings

Build scripts are the exception — build.rs always compiles for the host, so --target does not change which cfg(windows) branch it sees. A build script can only be linted for another platform by invoking the driver on it directly:

clippy-driver --edition 2024 --target x86_64-unknown-linux-gnu \
    --crate-type bin --emit=metadata -o /tmp/probe.rmeta \
    -D warnings fcs-gui/build.rs

That will not resolve winresource, so it is only useful for the non-Windows branches — which are exactly the ones a Windows developer cannot otherwise see.

Test tooling

The extra runners are optional for day-to-day work but match what CI does: cargo install cargo-nextest cargo-llvm-cov cargo-mutants.

  • cargo nextest run --workspace --all-features – The suite as CI runs it. Add --profile ci for no fail-fast, one retry on flaky GPU adapter acquisition, and a five-minute hang timeout.
  • cargo nextest-fast (or cargo test-fast) – The same suite without compiling fcs-core's examples, roughly halving the build after an fcs-core change. Aliases in .cargo/config.toml; CI still builds the examples, and doctests need cargo test --workspace --doc.
  • FCS_STRICT_TESTS=1 cargo nextest run --workspace --all-features – Turn "model/fixture missing, skipping" into a hard failure. CI sets this so a broken asset path cannot masquerade as a green run; leave it unset on a fresh clone, where skipping is the correct behaviour.
  • cargo llvm-cov nextest --workspace --all-features --summary-only – Line and region coverage. CI fails below 72% line coverage; pass --fail-under-lines 72 to check the gate locally. Note that the nextest profile must travel via NEXTEST_PROFILE, because --profile on the command line belongs to cargo-llvm-cov.
  • cargo mutants -p fcs-core -j 4 --file postprocess.rs --file cropper.rs – Mutation testing: mutates the source and reports which changes no test caught, which is a sharper signal than coverage about where tests are missing. Results land in mutants.out/missed.txt (the gaps), caught.txt, and unviable.txt (mutants that did not compile). Feature selection and exclusions come from .cargo/mutants.toml. Don't use --test-tool nextest here: it runs each test in its own process, which defeats the shared GPU test context.

Mutation testing

Most recent full run: 2026-10-14, all six crates, from commit 2a8c25e.

Crate Mutants Caught Missed Timed out Score
fcs-utils 2,262 2,230 24 8 98.6%
fcs-core 1,311 1,250 17 44 95.3%
fcs-ort 47 45 2 0 95.7%
fcs-cli 208 208 0 0 100%
fcs-mapping 71 71 0 0 100%
fcs-gui 48 48 0 0 100%
Workspace 3,947 3,852 43 52 97.6%

Score is caught ÷ (caught + missed + timed out). A timed-out mutant made the suite hang, so a test did notice it. Counting those as detected gives 98.9%. Mutants that did not compile are left out.

The run's real gaps were fixed afterwards (#7). What is still missed is equivalent: a guard that only skips a no-op, OR vs XOR on disjoint flag bits, a symmetric kernel, or a performance-only code path that produces the same output. The rest is code that only compiles on another platform: the two fcs-ort mutants are Unix-only and are caught in WSL, and three fcs-utils mutants are macOS-only.

A full run is not in CI: every mutant is a fresh incremental rebuild, and the whole workspace takes about 32 hours at -j 4, or 13 at -j 8 on an idle 16-core machine. Instead, every pull request that touches Rust runs cargo mutants --in-diff on just the lines it changed (.github/workflows/mutants.yml), and a missed mutant fails it. Above -j 4, check unviable.txt against the logs: a build that runs out of memory is recorded as unviable, not missed. For day-to-day work, run it against a couple of files at a time.

Configuration

  • Settings persist to config/gui_settings.json. The GUI saves changes automatically, and the CLI now reads the same file by default when --config is omitted, keeping detection, crop, and enhancement settings in sync across surfaces.

Diagnostics

  • CLI: pass --telemetry (optionally --telemetry-level trace) to log scoped timings under the fcs::lemetry target. Use --telemetry-level off to disable timing logs for that run.
  • GUI: toggle the Diagnostics → Telemetry logging checkbox in the settings panel to emit the same timing traces.

Documentation

  • Architecture overview – how the six workspace crates collaborate, including the GPU inference graph and development-only oracle.
  • Architecture diagrams – editable .drawio sources and SVG exports for the workspace and detection-pipeline diagrams shown above.
  • Contributing guide – workspace layout and how to get from clone to a working build.
  • Changelog – release history and notable changes.
  • CLI recipes – common fcs-cli invocations for detection, cropping, filtering, and enhancement.
  • GUI crop guide – walkthrough of the desktop crop workflow and keyboard shortcuts.
  • GPU research notes – design and implementation of the custom WGPU inference graph.
  • ONNX runtime options – inference backend trade-offs and the chosen approach.
  • Model comparison – SCRFD-80k and the eye refiner against public SCRFD-500M and the RetinaFace R34 weights found in FaceCropJet.
  • Performance guide – performance profile and optimization notes.
  • OpenCV parity snapshot – detection/crop parity against the OpenCV reference.
  • Release runbook – the Windows-first release process.

Licence

The code is dual-licensed under MIT or Apache-2.0, at your option. The two trained models in models/ are licensed separately under CC BY 4.0: reuse them freely, including commercially, as long as you credit Face Crop Studio. See models/README.md for the credit line.

About

GPU-accelerated face detection, cropping, enhancement, and batch export tools in Rust.

Topics

Resources

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages