Spaces:
Sleeping
Sleeping
| # FaceDetection System β Architecture Documentation | |
| > **Last Updated:** June 9, 2026 | |
| > **Stack:** Python Β· FastAPI Β· OpenCV Β· ONNX Β· SQLite Β· React | |
| --- | |
| ## Table of Contents | |
| 1. [Current Architecture](#1-current-architecture) | |
| 2. [Logic Flow (Step-by-Step)](#2-logic-flow-step-by-step) | |
| 3. [Core Models](#3-core-models) | |
| 4. [Liveness Detection](#4-liveness-detection) | |
| 5. [Attendance Logging](#5-attendance-logging) | |
| 6. [Proposed Architecture (Alternative Plan)](#6-proposed-architecture-alternative-plan) | |
| 7. [Side-by-Side Comparison](#7-side-by-side-comparison) | |
| 8. [What's Already Done vs. What Can Be Improved](#8-whats-already-done-vs-what-can-be-improved) | |
| 9. [Recommended Next Steps](#9-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](https://huggingface.co/opencv/face_detection_yunet) | [HuggingFace: opencv/face_recognition_sface](https://huggingface.co/opencv/face_recognition_sface) | | |
| --- | |
| ## 4. Liveness Detection | |
| **Goal:** Prevent "proxy attendance" using a printed photo or screen-displayed image. | |
| **Method: Landmark Standard-Deviation Analysis** | |
| ```python | |
| # 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_in` based on the last event stored in the DB for that employee on the current day. | |
| - **Debounce:** A 2-minute in-memory cooldown per `employee_id` prevents duplicate logs from continuous recognition. | |
| - **Real-Time Alerts:** Every attendance event is pushed into a thread-safe `queue.Queue` which 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.7` is designed for FaceNet's cosine similarity scale. SFace cosine scores use a different calibration where `0.363` is the verified boundary. Applying `0.7` to 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 `.onnx` format. The quantization recommendation is already fulfilled. | |
| --- | |
| ## 8. What's Already Done vs. What Can Be Improved | |
| ### β Already Implemented | |
| - [x] ONNX model inference (YuNet + SFace) | |
| - [x] Real-time face detection at 640Γ480 | |
| - [x] 128-d embedding extraction with face alignment | |
| - [x] Cosine similarity matching with calibrated threshold | |
| - [x] In-memory embedding cache (reload on new employee registration) | |
| - [x] Liveness detection (static photo / spoof guard) | |
| - [x] Attendance toggle logic (check-in / check-out) | |
| - [x] 2-minute debounce cooldown per employee | |
| - [x] Real-time SSE alerts to frontend | |
| - [x] Spoof alerts with 10-second debounce | |
| - [x] Premium HUD overlay (neon bounding boxes, corner ticks, scan-line animation) | |
| - [x] 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* | |