Spaces:
Sleeping
FaceDetection System β Architecture Documentation
Last Updated: June 9, 2026
Stack: Python Β· FastAPI Β· OpenCV Β· ONNX Β· SQLite Β· React
Table of Contents
- Current Architecture
- Logic Flow (Step-by-Step)
- Core Models
- Liveness Detection
- Attendance Logging
- Proposed Architecture (Alternative Plan)
- Side-by-Side Comparison
- What's Already Done vs. What Can Be Improved
- Recommended Next Steps
1. Current Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CAPTURE LAYER β
β VideoCamera (Singleton) Β· 640Γ480 Β· ~30 FPS cap β
β OpenCV VideoCapture β Horizontal Flip (mirror mode) β
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DETECTION LAYER β
β Model : YuNet (face_detection_yunet_2023mar.onnx) β
β Format: ONNX Β· Input: dynamic (set to frame size per call) β
β Output: bounding box [x, y, w, h] + 5-point facial landmarks β
β + detection confidence score β
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LIVENESS LAYER β
β Method : Landmark Standard-Deviation Analysis β
β Buffer : Last 15 frames of 5-point landmark coordinates β
β Logic : If avg. std-dev of all coordinates < 0.7 px β SPOOF β
β (A real face has micro-tremors; a printed photo is rigid) β
β Output : is_live = True / False + liveness label β
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β (only if is_live == True)
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EMBEDDING LAYER β
β Model : SFace (face_recognition_sface_2021dec.onnx) β
β Format: ONNX Β· ~38 MB on disk β
β Steps : 1. alignCrop(frame, face_landmarks) β aligned 112Γ112 β
β 2. recognizer.feature(aligned_face) β 128-dim vector β
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VERIFICATION LAYER β
β Method : Cosine Similarity (cv2.FaceRecognizerSF_FR_COSINE) β
β Threshold : 0.363 (calibrated by OpenCV SFace research paper) β
β Strategy : Best-of-N β takes highest score per employee across β
β all stored face templates β
β Output : (employee_id, employee_name, similarity_score) | None β
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ATTENDANCE LOGGER β
β Debounce : 2-minute cooldown per employee_id (in-memory dict) β
β Logic : check_in β check_out β check_in (toggle per day) β
β DB : SQLite (attendance.db) β
β Alerts : Thread-safe Queue β SSE stream β React frontend β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Logic Flow (Step-by-Step)
Camera Frame
β
βββΊ [YuNet Detector]
β β
β βββ No faces found βββΊ Clear liveness buffer, draw scan-line animation
β β
β βββ Faces found (1..N) βββΊ For each face:
β β
β βββΊ Parse bbox [x, y, w, h] + 5 landmarks + score
β β
β βββΊ Update landmark buffer (last 15 frames)
β β
β βββΊ [Liveness Check]
β β βββ Buffer < 8 frames βββΊ "PENDING..."
β β βββ std_dev < 0.7 px βββΊ "SPOOF: STATIC PHOTO" β push SSE alert
β β βββ std_dev β₯ 0.7 px βββΊ "LIVENESS: VERIFIED"
β β
β βββΊ [SFace Embedding] (only if live)
β β βββ alignCrop β feature() β 128-d vector
β β
β βββΊ [Cosine Match]
β β βββ score β₯ 0.363 βββΊ (emp_id, name, score)
β β βββ score < 0.363 βββΊ Unknown
β β
β βββΊ [Attendance Trigger] (only if emp_id found)
β βββ In cooldown? βββΊ Skip
β βββ Determine event_type (check_in / check_out)
β βββ Log to SQLite
β βββ Push notification to SSE Queue
β
βββΊ [HUD Renderer]
βββ Neon bounding box (color: green=match, red=unknown, crimson=spoof)
βββ 5-point landmark dots
βββ Label banner (name, score %, liveness status)
βββ Corner tick HUD overlay + scan-line animation
3. Core Models
| Property | YuNet (Detector) | SFace (Recognizer) |
|---|---|---|
| File | face_detection_yunet_2023mar.onnx |
face_recognition_sface_2021dec.onnx |
| Size | ~232 KB | ~38 MB |
| Format | ONNX | ONNX |
| Input | Dynamic (frame resolution) | 112Γ112 aligned crop |
| Output | bbox + 5 landmarks + score | 128-dimensional embedding vector |
| Runtime | OpenCV DNN (cv2.FaceDetectorYN) |
OpenCV DNN (cv2.FaceRecognizerSF) |
| Source | HuggingFace: opencv/face_detection_yunet | HuggingFace: opencv/face_recognition_sface |
4. Liveness Detection
Goal: Prevent "proxy attendance" using a printed photo or screen-displayed image.
Method: Landmark Standard-Deviation Analysis
# Collect last 15 frames of 5-point landmarks
pts = np.array(landmark_buffer) # shape: (15, 5, 2)
std_coords = np.std(pts, axis=0) # shape: (5, 2)
avg_std = np.mean(std_coords)
if avg_std < 0.7:
is_live = False # Coordinates are rigid β static photo / spoof
else:
is_live = True # Natural micro-tremors detected β real face
Why it works: A real human face held in front of a camera has natural micro-tremors, breathing movements, and small head shifts. A photo or screen holds perfectly still. The 0.7-pixel threshold captures this difference reliably.
Current Limitation: This method only catches perfectly static spoofs. It won't catch a video of a face played on a phone, or a photo shaken manually.
5. Attendance Logging
- Toggle Logic: The system alternates
check_inβcheck_outβcheck_inbased on the last event stored in the DB for that employee on the current day. - Debounce: A 2-minute in-memory cooldown per
employee_idprevents duplicate logs from continuous recognition. - Real-Time Alerts: Every attendance event is pushed into a thread-safe
queue.Queuewhich is streamed to the React frontend via Server-Sent Events (SSE). - Spoof Alerts: Spoof detection events also push to the SSE queue with a 10-second debounce to avoid flooding the UI.
6. Proposed Architecture (Alternative Plan)
The following is an alternative architecture plan that was evaluated for this system:
Capture Layer
βββΊ BlazeFace (initial detection)
βββΊ Correlation Tracker (tracks bounding box between detections)
βββΊ FaceNet (128-d or 512-d embeddings)
βββΊ Cosine Similarity (threshold: 0.7)
βββΊ Attendance Logger
Additional tips proposed:
- Model Quantization (ONNX / TFLite)
- Frame Skipping (process every other frame)
- Lower detection resolution (320Γ320)
- Liveness via blink detection / MediaPipe landmarks
7. Side-by-Side Comparison
| Dimension | Current Implementation | Proposed Plan |
|---|---|---|
| Detector | YuNet (ONNX, ~232 KB) | BlazeFace |
| Tracking | Per-frame YuNet detection | BlazeFace β Correlation Tracker handoff |
| Recognizer | SFace (ONNX, 128-d) via OpenCV | FaceNet (PyTorch / TFLite, 128-d or 512-d) |
| Similarity | Cosine via cv2.FaceRecognizerSF |
Manual cosine similarity |
| Threshold | 0.363 (SFace-calibrated) | 0.7 (incompatible with SFace scale) |
| Model Format | β Already ONNX | Needs conversion to ONNX/TFLite |
| Liveness | β Landmark std-dev (static photo guard) | Blink detection (EAR via MediaPipe) |
| Frame Rate | ~30 FPS cap (sleep 0.033s) | 15β20 FPS target |
| Complexity | Low β single pipeline | Higher β 2-stage detect+track pipeline |
| Dependencies | OpenCV only (no heavy ML framework) | Requires PyTorch or TFLite runtime |
| Status | β Production-ready | π§ Would require significant re-engineering |
Key Conflicts to Note
β οΈ Threshold Incompatibility: The proposed threshold of
0.7is designed for FaceNet's cosine similarity scale. SFace cosine scores use a different calibration where0.363is the verified boundary. Applying0.7to the current SFace engine would reject almost all legitimate matches.
βΉοΈ Detector Redundancy: YuNet already operates at a low-resolution input size and is fast enough for real-time CPU use. Adding a Correlation Tracker on top introduces state complexity with marginal speed gain for an attendance use case (stationary camera, controlled environment).
β ONNX is already done: Both YuNet and SFace are
.onnxformat. The quantization recommendation is already fulfilled.
8. What's Already Done vs. What Can Be Improved
β Already Implemented
- ONNX model inference (YuNet + SFace)
- Real-time face detection at 640Γ480
- 128-d embedding extraction with face alignment
- Cosine similarity matching with calibrated threshold
- In-memory embedding cache (reload on new employee registration)
- Liveness detection (static photo / spoof guard)
- Attendance toggle logic (check-in / check-out)
- 2-minute debounce cooldown per employee
- Real-time SSE alerts to frontend
- Spoof alerts with 10-second debounce
- Premium HUD overlay (neon bounding boxes, corner ticks, scan-line animation)
- Thread-safe singleton camera instance
π§ Improvements Worth Adding
| Priority | Improvement | Effort | Impact |
|---|---|---|---|
| β‘ High | Frame Skipping β process every 2nd frame | Low | 30β50% CPU reduction |
| β‘ High | Downscale for detection β run YuNet on 320Γ240 copy, scale landmarks back to 640Γ480 for display | Low | Faster detection pass |
| ποΈ Medium | Blink Detection (EAR) β upgrade to MediaPipe 468-point face mesh for eye-aspect-ratio liveness | Medium | Catches video/moving-photo spoofs |
| π Medium | Multi-angle enrollment β store 3β5 embeddings per employee from different angles | Low | Improves recognition rate |
| π Low | Async embedding extraction β move embedding+matching to a worker thread, keep capture loop purely for frames | Medium | Eliminates blocking in capture loop |
9. Recommended Next Steps
Priority 1 (Quick Wins β 1-2 hours):
βββ Add frame_count % 2 skip in _capture_loop()
βββ Add downscale copy for YuNet detect call
Priority 2 (Accuracy β 1 day):
βββ Multi-angle face enrollment UI (capture 3-5 samples per person)
Priority 3 (Liveness Upgrade β 2-3 days):
βββ Integrate MediaPipe FaceMesh for blink-based EAR liveness
Generated by Antigravity AI Β· FaceDetection Project Β· June 2026