Runs on CPU FastAPI · DeepFace · ONNX · MediaPipe

Face Verification, Recognition & Liveness — in one toolkit.

Face ID Pro bundles eight face-analysis modules into a single FastAPI service. Register a face, verify it 1:1, search it 1:N, estimate age & gender, classify expression, map 3D topology, and gate access with passive anti-spoof and active blink liveness — all behind JWT authentication and running on CPU.

Self-hosted No GPU required SQLite or PostgreSQL JWT-secured
The modules

Eight face-analysis endpoints, one process.

Each module is a discrete FastAPI route backed by a specific model, sharing the same auth, storage layer, and rate-limit budget. You can call them independently or chain them through /api/analyze-face/, which fans out to four models in parallel.

1:1 Face Verification
POST /api/verify/

Upload two images. The server detects every face in each, computes Facenet512 embeddings, and finds the optimal pairing between them using the Hungarian algorithm on cosine similarity. Returns per-pair similarity, distance, and an overall verified verdict (all face pairs must clear the threshold). Both annotated images come back with corner brackets drawn on the matched faces.

1:N Face Recognition
POST /api/recognize/

Compares every detected face in the upload against the embeddings you own. A high-trust band (> 0.90 cosine) short-circuits; an ambiguous band (0.65 – 0.90) triggers a cross-check against the original stored image before accepting. Results include per-face name, confidence, and the method that produced the match.

Register Face
POST /api/register-face/

Adds a person to your private face database. Each upload is written to uploads/known_faces/<sanitized_name>/ with a UUID filename, then cropped, embedded, and stored as a JSON vector. Names are Unicode NFKD-folded and path-traversal-safe. Multiple photos per person are supported and improve recognition.

Full Face Analysis
POST /api/analyze-face/

One upload fans out to four models in parallel — analysis_type can be all, age_gender, expression, or topology. Age & gender come from MiVOLO, the six-expression classifier from DFER, and the 3D mesh from 3DDFA (with MediaPipe FaceMesh as fallback). Each stage is thread-isolated so TensorFlow and PyTorch don't thrash.

Anti-Spoofing
POST /api/anti-spoofing/

Hybrid liveness scorer. Six heuristic signals — Laplacian texture, Shannon entropy, Sobel sharpness, histogram spread, color variance, and FFT frequency content — combine with a 600 KB quantized ONNX model. The final gate uses a frequency-confidence rule tuned for the MiniFASNetV2-SE output distribution. Per-face results are returned with all component scores.

Wink & Blink Detection
POST /api/detect-wink/

MediaPipe FaceMesh eye-aspect-ratio over a frame sequence. The blink detector requires both eyes to close then reopen, with a bounded closure length so a squint or a still photo can't pass. The single-image wink endpoint is included as a demonstration and is documented as such in the API.

Biometric Login
POST /api/biometric/login-blink/

Password + face + blink, all in one flow. The password layer runs first, so anonymous face spam can't exhaust the embedding model. A face match alone with strong confidence and separation passes even if the liveness signal is weak; otherwise liveness must clear the threshold. Rate-limited with lockout after repeated failures.

History & Audit
GET /api/history/

Every verify, recognize, and analysis call writes a log row — username, module, summary, timestamp. The combined history endpoint merges RecognitionLog and AnalysisLog results into one chronological feed, filterable by free-text query. Admin users can also list accounts and toggle active state.

Request lifecycle

Every heavy endpoint follows the same shape.

Validation, detection, and response happen in a fixed order. Understanding the order matters because rate limits, concurrency slots, and storage guarantees are tied to it.

Step 01

Upload & Validate

The multipart upload is read, magic bytes are checked for JPEG/PNG/WebP/BMP, and image dimensions are decoded from the header before the body is decoded. Size is capped at 10 MB, dimensions at 4096×4096, total pixels at ~16.7M. A disk-free guard aborts at 500 MB remaining. Filenames are sanitized before any path is constructed.

Step 02

Detect & Score

Face detection runs first, then the crop is passed to the module-specific model. For anti-spoofing, the crop is scored by both the six-feature heuristic and the ONNX model; the final gate uses a frequency-confidence rule. Heavy endpoints run under bounded concurrency — a request waits up to 30 seconds for a slot before returning 503.

Step 03

Log & Respond

A log row is written with the module name, result summary, and timestamp. The annotated image is encoded back to base64 and returned alongside the structured JSON payload. Optional PDF/image export is available from the client via /api/export/, which re-renders the result at print resolution.

Verifiable numbers

What's actually running under the hood.

These counts come directly from the codebase, not from a spec sheet. They're the numbers you can grep for.

single process
0
Analysis modules

Verify · Recognize · Register · Age & Gender · Expression · Topology · Anti-Spoof · Wink. Each is a discrete FastAPI route sharing one auth context.

passive + active
0
Liveness paths

Passive hybrid anti-spoof (heuristic + ONNX) for single frames, plus active blink coordination across a frame sequence via MediaPipe FaceMesh eye-aspect-ratio.

cosine
0
Embedding dimensions

Facenet512 vectors stored per registered face. Similarity is cosine; verify pairs via Hungarian assignment over the full face matrix, with a cross-check against the original image in the ambiguous band.

3DDFA + MediaPipe
0
Topology landmarks

MediaPipe FaceMesh provides 478 landmarks and drives the blink detector; 3DDFA produces a dense 3D mesh with 38365 landmarks when the ONNX backend is available, with an automatic fallback to FaceMesh.

The stack

Six models, each with one job.

All inference runs on CPU. TensorFlow and PyTorch threads are pinned to one so the server stays responsive under load, and GPU access is explicitly disabled at process start.

Model Role Runtime Used in
Facenet512 Face embeddings DeepFace · TF Verify · Recognize · Register · Biometric
MiVOLO Age & gender PyTorch Analyze (age_gender)
DFER 6-class expression PyTorch Analyze (expression)
3DDFA (ONNX) Dense 3D mesh ONNX Runtime Analyze (topology)
MediaPipe FaceMesh 478 landmarks · EAR MediaPipe Blink liveness · Topology fallback
MiniFASNetV2-SE Anti-spoof deep model ONNX Runtime Anti-spoof · Biometric login
Technical FAQ

Honest answers about what this stack does.

Including the parts that are works-in-progress. If something isn't production ready, this list says so.

Anti-spoof How does the anti-spoof decide real vs. spoof?
Two independent signals. First, a six-feature heuristic scores texture (Laplacian variance), Shannon entropy, Sobel sharpness, histogram spread, color variance, and FFT frequency content — weighted 0.30 / 0.20 / 0.15 / 0.15 / 0.10 / 0.10. Second, a 600 KB quantized ONNX model returns a probability. The final decision uses a frequency-confidence gate: (dl_prob > 0.15 AND freq_score > 80) or (dl_prob > 1e-4 AND freq_score > 85.35). This rule is tuned for the MiniFASNetV2-SE output distribution.
Hardware Do I need a GPU?
No. The server forces CUDA_VISIBLE_DEVICES=-1 and TF_METAL_DEVICE_COUNT=0, pins OMP/MKL/OpenBLAS/NumExpr thread counts to 1, and sets torch.set_num_threads(1). Everything runs on CPU. Cold-start latency is a few seconds per model; warm requests are fast.
Storage Where is user data stored?
Raw uploads live under uploads/known_faces/<sanitized_name>/<uuid>.jpg. Face embeddings are stored as JSON arrays in the face_embeddings table (plaintext, not encrypted). Recognition and analysis events are logged to recognition_logs and analysis_logs. Uploads older than 7 days are purged by a background task that runs every 6 hours. Embeddings are not encrypted at rest — this is a known limitation of the current implementation.
Auth How do sessions work?
JWT HS256. Payload includes sub, role, iat, exp, ver (token version), and epoch (server boot id). Web sessions expire in 15 minutes; mobile in 24 hours. Changing your password bumps token_version, invalidating old tokens. Restarting the server changes epoch, so all previously issued tokens are rejected. The frontend watchdog checks every 30 seconds and warns at 2 minutes before expiry.
Wink Is wink detection production-ready?
No. The /api/detect-wink/ endpoint is documented in the codebase itself as "for demonstration only". It works, but its thresholds (EAR_CLOSED = 0.24, EAR_OPEN = 0.27, and a 0.55 ratio rule) are tuned for a demo, not a regulated environment. Blink liveness — which requires both eyes to close and reopen across a frame sequence — is the mechanism used for biometric login.
Deployment What do I need to run it locally?
Python 3.10+, the project's requirements.txt, and a set JWT_SECRET environment variable. Start with uvicorn app:app --reload --port 8000. The database tables are created automatically on first boot. The default admin account is seeded during startup — change its password immediately.
Ready when you are

Start registering and verifying faces in a few minutes.

One FastAPI process gives you 1:1 verify, 1:N recognize, register face, age & gender, expression, 3D topology, anti-spoof, blink liveness, and biometric login — all behind JWT auth and running on CPU.