TL;DR: I built a face-based access control system that runs completely offline on a Raspberry Pi. It uses cosine similarity between face vectors to grant or deny folder access. The whole thing is under 300 lines of Python.
The Problem
Physical folder-level security is usually done with passwords or encryption. Both have a weakness: anyone with the password gets in. I wanted something that physically identifies the user — and runs on cheap hardware with no cloud dependency.
So I set up a Raspberry Pi with a camera module and built a facial authentication loop.
How It Works
The flow is straightforward:
Registration phase: The camera captures the authorized user's face. The system converts it to grayscale, extracts facial landmarks using
face_recognition, and stores the resulting 128-dimensional face vector locally.Verification phase: When someone opens the protected folder, the camera activates, captures the current face, extracts its vector, and compares it to the stored vector using cosine similarity.
Decision: If similarity exceeds a configurable threshold — access granted. If not — the folder closes immediately.
The Tech
Hardware: Raspberry Pi 3B+ + USB Camera
Software: Python 3, OpenCV, face_recognition (dlib), NumPy
Metric: Cosine similarity via scikit-learn
The key decision was using cosine similarity over Euclidean distance. Face vectors are direction-sensitive — two images of the same person under different lighting produce vectors pointing in roughly the same direction but with different magnitudes. Cosine similarity ignores magnitude and compares direction, which makes it much more robust to lighting changes.
Edge Case That Took the Longest to Solve
The hardest bug wasn't the ML — it was the camera initialization race condition.
When the folder-trigger script launches the camera, there's a brief window where the first frame is black (auto-exposure hasn't settled). If the system captures that black frame as the test face, the feature extraction returns an all-zero vector — and cosine similarity between zero vectors is undefined (division by zero).
Fix: I added a 3-frame warmup that discards the first captures and only processes once pixel variance stabilizes above a threshold. Simple, effective, and took way too long to figure out.
def warmup_camera(cap, warmup_frames=3):
"""Discard initial dark frames until exposure stabilizes."""
for _ in range(warmup_frames):
ret, frame = cap.read()
if not ret:
continue
return cap
Why This Matters
A Raspberry Pi costs $35. A camera module costs $15. For $50, you get biometric folder security that works entirely offline — no cloud API calls, no subscription fees, no internet required.
This approach — edge AI with cheap hardware — is massively underused. Most "AI security" products are cloud-dependent, which means latency, privacy risks, and recurring costs. Running the inference locally on a $35 computer changes the economics.
Key Takeaways
- Face recognition on a Raspberry Pi is entirely feasible with Python + OpenCV + dlib
- Cosine similarity handles lighting variation better than Euclidean distance for face vectors
- Camera initialization race conditions are a real edge case — always warm up
- Edge AI deployment doesn't need expensive hardware; it needs the right tradeoffs
What's Next
I'm exploring two improvements:
- Adding liveness detection (blink detection) to prevent photo-based spoofing
- Converting the model to TensorFlow Lite for faster inference on the Pi
The full source is on GitHub: github.com/HENI-MOHAMED/FaceRegnition
Have you run face recognition on edge hardware? What issues did you run into?
Top comments (0)