Skip to content
Document DB v12 - Improved Interceptors with Soft Delete Integration, AI protections, & Admin UI with Aspire Integration! How!?

Getting Started

GitHubGitHub stars for shinyorg/recogintelligence
DownloadsNuGet downloads for Shiny.FaceIntelligence
Frameworks
.NET
.NET MAUI
Operating Systems
Android
iOS
macOS
Windows
Linux

Shiny.FaceIntelligence does face enrollment and recognition entirely on-device. You store one or more face embeddings per person, and recognition is a nearest-neighbour vector lookup with a cosine-distance threshold. Nothing leaves the device, and there is no cloud API to pay for.

The pipeline is split so you only pull what you use. The core has no ONNX and no database dependency.

PackageRole
Shiny.FaceIntelligenceCore. IFaceIntelligence, the contracts (IFaceEmbedder, IFaceDetector, IFaceStore), data types, and the registration builder. Depends on SkiaSharp + DI abstractions only.
Shiny.FaceIntelligence.OnnxONNX ArcFace embedder + UltraFace detector (UseOnnxEmbedder / UseOnnxDetector). Also ships the iOS/Mac Catalyst linker fix.
Shiny.FaceIntelligence.DocumentDbProvider-agnostic vector store over Shiny.DocumentDb (UseDocumentDbStore).
Shiny.FaceIntelligence.DocumentDb.SqliteTurnkey SQLite + sqlite-vec store (UseSqliteStore).
Shiny.FaceIntelligence.MauiLive camera controls — FaceRecognitionView and the guided FaceEnrollmentView.

A typical mobile app installs Shiny.FaceIntelligence.Onnx, Shiny.FaceIntelligence.DocumentDb.Sqlite, and Shiny.FaceIntelligence.Maui (each pulls the core transitively). A server-side enrollment service installs the first two and skips MAUI entirely.

Terminal window
dotnet add package Shiny.FaceIntelligence.Onnx
dotnet add package Shiny.FaceIntelligence.DocumentDb.Sqlite
dotnet add package Shiny.FaceIntelligence.Maui

Everything is composed through one builder. AddFaceIntelligence validates the result and throws at startup if an embedder or a store is missing, naming the method you forgot to call.

using Shiny.FaceIntelligence;
using Shiny.FaceIntelligence.Onnx;
using Shiny.FaceIntelligence.DocumentDb.Sqlite;
builder.Services.AddFaceIntelligence(face =>
{
face.Options.MaxDistance = 0.6f; // cosine distance — lower is stricter
// Image → 512-d vector. Loaded lazily on the first enroll/recognize.
face.UseOnnxEmbedder(o => o.ModelBytesProvider = () => LoadBundledModel("arcface.onnx"));
// Finds the face box. Required for the live controls and the no-box overloads.
face.UseOnnxDetector(o => o.ModelBytesProvider = () => LoadBundledModel("face_detector.onnx"));
// Vector storage + nearest-neighbour search via sqlite-vec.
face.UseSqliteStore(o => o.ConnectionString = $"Data Source={Path.Combine(FileSystem.AppDataDirectory, "faces.db")}");
});
// Only if you use the MAUI controls — it holds per-camera state, so it must be transient.
builder.Services.AddTransient<FaceRecognitionAnalyzer>();

The camera is only used by Shiny.FaceIntelligence.Maui, and it needs the usual platform entries: iOS and Mac Catalyst want NSCameraUsageDescription in Info.plist, Android wants the CAMERA permission. See MAUI Controls.

Or generate the full set of files — packages, registration, manifests and permissions — with the builder below:

Shiny.FaceIntelligence.DocumentDb.SqliteNuGet package Shiny.FaceIntelligence.DocumentDb.Sqlite
Shiny.FaceIntelligence.MauiNuGet package Shiny.FaceIntelligence.Maui
Shiny.FaceIntelligence.OnnxNuGet package Shiny.FaceIntelligence.Onnx
public class CheckInService(IFaceIntelligence faces)
{
public Task Enroll(string employeeId, byte[] photo, FaceBox box)
=> faces.Enroll(employeeId, photo, box);
public async Task<string?> WhoIsThis(byte[] photo, FaceBox box)
{
var result = await faces.Recognize(photo, box);
return result.IsMatch ? result.PersonIdentifier : null;
}
}

IFaceIntelligence is the whole consumer-facing surface:

MemberWhat it does
Enroll(id, imageData, faceBox)Embed the face at that box and store it under id.
Enroll(id, imageData, allowDuplicate)Detect the face first, apply the quality gates, then store. Needs a detector.
Recognize(imageData, faceBox)Nearest enrolled identity within the threshold, or RecognitionResult.NoMatch.
Recognize(imageData)Detect then recognize. Needs a detector.
GetAll()Every stored shot, most recent first.
Forget(id)Delete every shot for an identity; returns how many were removed.

You rarely call Recognize per frame yourself. Shiny.FaceIntelligence.Maui wraps the camera, permissions, lifecycle and analyzer into two drop-in controls:

xmlns:fi="clr-namespace:Shiny.FaceIntelligence.Maui;assembly=Shiny.FaceIntelligence.Maui"
<fi:FaceRecognitionView FaceRecognized="OnFaceRecognized" />
<fi:FaceEnrollmentView PersonIdentifier="{Binding EmployeeId}" Completed="OnEnrolled" />

See MAUI Controls.

PersonIdentifier is an opaque string you pick — a user id, an employee number, a GUID. The library stores no display name and does no identity resolution of its own.

  • Architecture — how the pieces fit, and which ones you can swap
  • Enrollment — quality gates, duplicate detection, building a good gallery
  • Recognition — thresholds, tuning, and what the numbers mean
  • MAUI Controls — live recognition and the guided enrollment wizard
  • Models — picking, bundling and sizing the ONNX models
  • Stores — sqlite-vec, other DocumentDb providers, or your own