Real-time CPU face recognition using classical ML (Haar Cascade + KNN). Designed for edge deployment on laptops, Raspberry Pi, and embedded devices without GPU acceleration.
Project description
Edge Face Recognition (CPU-Only)
Real-time face recognition system designed for CPU-only environments (laptops, embedded devices, Raspberry Pi)
A classical computer vision pipeline (Haar Cascade + KNN) delivering ~40 ms inference latency without GPUs or deep learning frameworks. Built for offline attendance systems and privacy-sensitive deployments where cloud inference isn't viable.
Install:
pip install edgeface-knn
Performance: ~40 ms per frame (~15 FPS effective throughput)
Problem & Motivation
Use case: Identity recognition for attendance systems, access control, or lab check-ins.
Constraints:
- No GPU access (laptops, Raspberry Pi, edge devices)
- No cloud connectivity (offline operation, privacy requirements)
- Real-time response needed (sub-100ms for good UX)
- Non-ML users (should "just work" without tuning)
Why existing solutions don't fit:
- CNN-based face recognition: ~300 ms inference on CPU, ~90 MB model size
- Cloud APIs: Require internet, data privacy concerns, per-call costs
- Research repos: Monolithic code, not installable, require ML expertise
This system prioritizes deployment viability over maximum accuracy — designed for environments where "good enough, deterministic, and always available" beats "state-of-the-art but requires infrastructure."
What This System Does
Workflow:
- Registration: Capture 100 face samples per person via webcam (automated, ~30 seconds)
- Recognition: Real-time identification with confidence scoring
- Logging: Optional attendance tracking with timestamps
Interface:
- Command-line tool (no GUI)
- Configuration-driven (YAML-based)
- Pip-installable package (no manual setup)
Performance:
- Latency: ~40 ms per processed frame
- Throughput: ~15 FPS effective (frame skipping for UX)
- Model size: <1 MB (vs ~90 MB for CNN alternatives)
- Accuracy: ~95% frontal face, ~90% with glasses, ~75% with mask
Engineering Decisions
1. Why Haar Cascade + KNN over CNNs?
Prototyped both classical CV and deep learning approaches.
| Factor | This System (Haar + KNN) | CNN Baseline |
|---|---|---|
| CPU inference | ~40 ms | ~300 ms |
| Model size | <1 MB | ~90 MB |
| Training data | ~100 samples/person | 1000+ samples/person |
| GPU required | No | Yes (for real-time) |
| Latency predictability | Deterministic | Variable (thermal throttling) |
Decision: Chose classical CV for deployment constraints.
Trade-off accepted: Lower angle robustness (±30° vs ±60° for CNNs) in exchange for guaranteed real-time performance on target hardware.
2. Unknown Face Handling Strategy
Problem: How to handle faces not in the training set?
Naive approach: Always return nearest neighbor (KNN default behavior).
- Risk: Logs the wrong person (critical failure in attendance systems)
This system's approach: Confidence thresholding with rejection.
| Confidence | Result |
|---|---|
| ≥ threshold | Person identified |
| < threshold | Marked "Unknown" |
Decision rationale:
- False negative (reject known person) → They try again
- False positive (log wrong person) → Permanent incorrect record
Bias toward precision over recall — better to ask someone to retry than log them as someone else.
3. Frame Skipping for Real-Time UX
Problem: Processing every frame creates visual lag (camera feed stutters).
Options:
- Process all frames → Stuttering, poor UX
- Async processing → Added complexity, race conditions
- Process every Nth frame → Simple, maintains smooth preview
Decision: Process every 2nd frame (skip odd frames).
- Rationale: Humans perceive <50ms lag as instantaneous; processing 15 FPS feels real-time
- Trade-off: Slight temporal jitter (detection updates every ~65ms instead of ~33ms)
4. Packaging as Reusable Tool
Problem: Initial prototype was monolithic scripts (hard to reuse, no version control).
Evolution:
- v1 (Sept 2024): Raspberry Pi prototype, single-file scripts
- v2 (Dec 2025): Modular Python package, pip-installable, configuration-driven
Decision to refactor:
- Makes it reusable across projects (attendance, access control, experiments)
- Demonstrates production packaging workflow (not just research code)
- Enables non-ML users to deploy (IT admins, not data scientists)
Packaging choices:
- CLI interface (not GUI) → Cross-platform, scriptable
- YAML configuration (not hardcoded params) → Customization without code changes
- Minimal dependencies → Reduces installation friction
Installation & Usage
Quick Install (Recommended)
Requires native OS execution for camera access (WSL users see notes below).
pip install edgeface-knn
edge-face --help # Verify installation
Development Setup
git clone https://github.com/SakshamBjj/edge-face-recognition-v2.git
cd edge-face-recognition-v2
pip install -e . # Editable install for development
Basic Workflow
1) Register People
edge-face collect --name Alice
edge-face collect --name Bob
Captures 100 samples per person automatically (~30 seconds per person).
What happens:
- Opens webcam
- Detects face using Haar Cascade (on grayscale frame)
- Saves 50×50 color (BGR) crops
- Stores in
data/raw/{name}/directory
2) Run Recognition
edge-face run
Controls:
| Key | Action |
|---|---|
o |
Log attendance (saves to CSV) |
q |
Quit |
Output:
- Real-time video feed with bounding boxes
- Name labels with confidence scores
- Attendance logs in
attendance/YYYY-MM-DD.csv
3) Optional: Custom Configuration
edge-face run --config configs/my_config.yaml
Configurable parameters:
- Detection confidence threshold
- Recognition threshold (unknown rejection)
- Frame skip interval
- Camera resolution
- Output paths
WSL Development Notes
Camera limitation: Webcam access requires native OS execution (WSL hardware virtualization limitation).
Recommended workflow:
| Task | Environment |
|---|---|
| Code editing, packaging | WSL |
| Face collection | Windows (native) |
| Real-time recognition | Windows (native) |
Testing from WSL:
# In WSL: Install editable package
pip install -e .
# In Windows terminal (same project directory):
edge-face collect --name TestUser
edge-face run
The package itself is OS-independent — only camera I/O requires native execution.
Technical Implementation
Runtime Pipeline
Camera (30 FPS)
↓
Grayscale conversion (for detection only)
↓
Haar Cascade detection (OpenCV) — runs on grayscale
↓
Face crop from color (BGR) frame + resize (50×50 pixels)
↓
Flatten to 1D vector (7,500 dimensions — 50×50×3 color channels)
↓
KNN classification (k=5, Euclidean distance)
↓
Confidence scoring: 100 × exp(−mean_dist / 4500) — heuristic, not calibrated probability
↓
Unknown rejection (if confidence < 40)
↓
Overlay labels + bounding boxes
↓
Display frame
Latency breakdown:
| Stage | Time |
|---|---|
| Detection (Haar) | ~20 ms |
| Preprocessing | ~5 ms |
| KNN search | ~15 ms |
| Total | ~40 ms |
Model Details
Detection: OpenCV Haar Cascade (frontalface_default.xml)
- Pre-trained on ~10K faces
- Detects faces at multiple scales
- Trade-off: Fast but angle-sensitive (±30° max)
- Runs on grayscale frame for speed
Feature representation: Raw pixel values (50×50 color BGR crop)
- Vector dimension: 7,500 (50 × 50 × 3 channels)
- No feature extraction (HOG, LBP, etc.)
- Simple = less overhead, more interpretable
Classification: K-Nearest Neighbors (k=5)
- Distance metric: Euclidean (L2 norm)
- No training step (lazy learning)
- O(n) search complexity (acceptable for <50 identities)
Unknown detection: Confidence scoring via exponential decay
score = 100.0 * np.exp(-mean_dist / 4500.0)
- Threshold: 40 (percent, configurable)
- Below threshold → "Unknown"
- Decay constant 4500 calibrated empirically — heuristic, not derived
Performance Analysis
Accuracy (Typical Indoor Lighting)
| Condition | Accuracy | Notes |
|---|---|---|
| Frontal face | ~95% | Optimal scenario |
| With glasses | ~90% | Slight reflection artifacts |
| With mask | ~75% | Reduced feature area |
| ±30° angle | ~70% | Haar detection limit |
| >45° angle | <50% | Face often undetected |
Error modes:
- Side profiles not detected (Haar limitation)
- Poor lighting → false negatives (miss detection)
- Multiple faces → processes only largest face
Latency Consistency
| Metric | Value |
|---|---|
| Mean latency | 40 ms |
| Std deviation | 3 ms |
| 99th percentile | 47 ms |
Why consistent:
- No GPU thermal throttling
- Deterministic CPU execution
- No network dependency
Scaling Limits
| # Identities | KNN Search Time | Total Latency | Acceptable? |
|---|---|---|---|
| 10 | 5 ms | 30 ms | ✓ |
| 50 | 15 ms | 40 ms | ✓ |
| 100 | 60 ms | 85 ms | ✗ (sub-real-time) |
| 500 | 300 ms | 325 ms | ✗ (unusable) |
Recommendation: <50 identities for smooth real-time performance.
Why KNN doesn't scale:
- Brute-force search is O(n)
- No indexing structure (KD-tree doesn't work well in high dimensions)
If you need >50 people: Consider approximate nearest neighbors (FAISS, Annoy) or switch to CNN embeddings with vector databases.
Limitations & Alternatives
Known Limitations
1. Angle sensitivity: Haar Cascade only detects frontal faces (±30°)
- Side profiles often missed
- Alternative: Multi-view Haar or CNN detector (MTCNN)
2. Lighting dependency: Poor lighting reduces detection rate
- Dark environments: <60% detection rate
- Alternative: Infrared camera + illuminator
3. Scaling ceiling: >50 identities degrades to sub-real-time
- KNN search becomes bottleneck
- Alternative: Approximate NN (FAISS) or CNN embeddings
4. Spoof vulnerability: Photo attacks possible (not liveness-aware)
- Printed photos can fool system
- Alternative: Depth cameras (RealSense) or liveness detection
5. No re-identification: Doesn't track individuals across frames
- Each frame is independent classification
- Alternative: Add object tracking (SORT, DeepSORT)
When This System is NOT Appropriate
Don't use this if you need:
- Security-critical authentication (use liveness detection + CNNs)
-
50 identities (use ANN-indexed embeddings)
- Angle robustness (use multi-view or CNN detectors)
- High accuracy requirements (use state-of-the-art CNNs)
Do use this if you need:
- Offline/edge deployment
- Predictable CPU latency
- Simple deployment (pip install)
- Small model footprint
- Fast prototyping
Repository Structure
edge-face-recognition-v2/
├── src/edge_face/
│ ├── __init__.py
│ ├── cli.py # Command-line interface
│ ├── config.py # Configuration management
│ ├── detector.py # Haar Cascade wrapper
│ ├── dataset.py # Data collection & loading
│ ├── model.py # KNN classifier
│ ├── pipeline.py # End-to-end inference
│ ├── camera.py # Cross-platform camera initialization
│ └── default.yaml # Default configuration
├── scripts/
│ └── collect_faces.py # Legacy collection script
├── data/ # Generated during collection
│ └── raw/{name}/ # Face samples per person
├── attendance/ # Generated during recognition
│ └── YYYY-MM-DD.csv # Daily attendance logs
├── pyproject.toml # Package metadata
└── README.md
Project Evolution
Version History
| Version | Focus | Date |
|---|---|---|
| v1 | Raspberry Pi embedded prototype | Sept 2024 |
| v2 | Pip-installable reusable package | Feb 2025 |
Key improvements in v2:
- Modular architecture (single file → package structure)
- Configuration-driven runtime (hardcoded → YAML)
- Cross-platform execution (RPi-only → Windows/Linux/macOS)
- Reusable CLI (monolithic script → installable tool)
- Calibrated confidence scoring with unknown rejection (broken formula → exponential decay)
What This Demonstrates
System design:
- Constraint-driven architecture (latency budget drives model choice)
- Trade-off analysis (accuracy vs deployment viability)
- User-centric design (non-ML users as target)
Software engineering:
- Packaging for distribution (PyPI-ready)
- CLI design (configuration, error handling, user feedback)
- Cross-platform compatibility (native vs WSL execution)
Production ML:
- Deployment constraints over benchmark chasing
- Unknown handling (precision > recall for attendance use case)
- Honest limitation documentation (when not to use this system)
Iteration velocity:
- Prototype → production evolution
- Monolithic code → modular package
- Single-use → reusable tool
References
Technical:
- Viola-Jones Face Detection (Haar Cascade): OpenCV Docs
- K-Nearest Neighbors: Scikit-learn KNN
Related work:
- FaceNet (CNN embeddings): For higher accuracy, GPU-available scenarios
- MTCNN (Multi-task CNN): For angle-robust detection
- ArcFace: State-of-the-art face recognition (requires GPU)
Author: Saksham Bajaj
Contact: LinkedIn | GitHub
License: MIT
Last Updated: February 2026
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file edgeface_knn-2.0.5.tar.gz.
File metadata
- Download URL: edgeface_knn-2.0.5.tar.gz
- Upload date:
- Size: 22.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
278655ce61a3d017084a937b6a27906d5f9284e86fd4be8a5224a00e024886e7
|
|
| MD5 |
9469ded61ab07cf65ecb947fb618f8c0
|
|
| BLAKE2b-256 |
5e11807f62b844eb34b179a2bc3267ac9b3d91c10150d12dc76d3118d5a90ece
|
File details
Details for the file edgeface_knn-2.0.5-py3-none-any.whl.
File metadata
- Download URL: edgeface_knn-2.0.5-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7871adbe44d6f1051472eb0d03a962eb6f21d6a8e261667fe5d1cac2c87eea47
|
|
| MD5 |
b9022a81205c816a0926ffeac3addb53
|
|
| BLAKE2b-256 |
7ccacdce6567f8dd167c435bcb3be637f77a64d76596f3323f36f32e27c1384b
|