• Sep 8, 2026
  • 4 min read

Pure-Rust Face Detection Without OpenCV or ONNX Runtime

Adding face detection to a desktop camera application often means adding OpenCV, an ONNX runtime, Python, or a separate model file. For my Rust/Tauri DJI Tello controller, I wanted detection without turning a small native application into a large computer-vision distribution project.

The resulting edition uses a vendored pure-Rust CNN detector. The model is compiled into the executable, detections run on a bounded worker, and flight control never waits for inference.

Detection, not recognition

The feature finds faces and returns:

  • Bounding boxes
  • Confidence values
  • Five facial landmarks

It does not identify a person. Keeping that language precise matters both technically and socially: localization and identity matching are different systems.

Why I avoided OpenCV and ONNX

The core controller already delegated H.264 decoding to system FFmpeg. Adding another large native runtime would increase build complexity, bundle size, and platform-specific failure modes.

I used the Rust implementation from Shiqi Yu’s libfacedetection, vendored at a specific reviewed upstream commit with its BSD-3-Clause license. Vendoring was necessary because the required crate was not published through the normal Rust registry at the time.

The network weights are generated as static Rust data. There is no runtime model download and no Python, C++, OpenCV, or ONNX runtime dependency. The detector has a scalar implementation and selects AVX2 acceleration at runtime on supported x86/x86-64 processors.

Real-time software should prefer fresh work

A normal unbounded queue is wrong for visual overlays. If detection takes longer than frame production, the queue grows and boxes describe increasingly old images.

The controller uses a synchronous channel with capacity one:

decoded JPEG
  ├─→ Vue video immediately
  └─→ one-slot detector channel
         ├─ slot empty: analyze frame
         └─ slot occupied: drop new stale work

Inference is limited to about 5 FPS. The detector instance and workspace are reused rather than rebuilt for every frame. This creates intentional backpressure: the latest display continues, memory stays bounded, and flight commands remain independent.

Dropping frames is not a failure in this design. A face overlay benefits more from freshness than completeness.

Keeping inference outside the safety path

The RC loop, acknowledgements, telemetry, video receiving, and detection worker have separate responsibilities. Face results travel to Vue as events and are painted over the letterboxed video area.

The overlay does not alter:

  • Flight-control state
  • The photo bytes
  • The recorded H.264 stream
  • Command timing

If the detector cannot keep up, only optional boxes become less frequent.

Small model, small binary increase

The optimized face executable measured about 9.3 MB. The corresponding core controller was roughly 8.7 MB during development, so compiled detection added about 0.6 MB.

That does not mean the entire desktop stack is 9.3 MB. Linux still provides WebKitGTK/GTK and the app launches system FFmpeg. It does show that a compact fixed-purpose CNN and its Rust inference path need not add hundreds of megabytes to the native artifact.

Reproducibility and licensing

Vendored code should not become anonymous copied source. The project records:

  • Upstream repository
  • Exact commit
  • Included subfolder
  • License
  • Local changes, if any

Detector tests run separately from controller tests. This keeps the third-party snapshot inspectable and makes an upstream refresh a deliberate change.

What this pattern is good for

The architecture fits optional, lossy, CPU-bound enrichment:

  • Object or face detection overlays
  • OCR previews
  • Thumbnail generation
  • Audio visualization
  • Non-critical analytics

The pattern is simple: bound the queue, prefer recent data, reuse expensive state, and never make the primary real-time path wait for optional analysis.

Frequently asked questions

Is pure-Rust face detection the same as face recognition?

No. Detection locates faces. Recognition attempts to associate a detected face with an identity.

Why use a channel with capacity one?

It prevents an inference backlog. When processing is busy, obsolete frames are discarded instead of consuming memory and producing delayed overlays.

Does the application require a model download?

No. The model weights are compiled into the Rust executable.

Does it work without FFmpeg?

The detector itself does, but this Tello application uses FFmpeg to convert the drone’s H.264 stream into frames before detection.