DeepFace Access Control System
Note: This is a documented technical plan and Product Requirements Document (PRD) - not an implemented project. It serves as a comprehensive blueprint for anyone looking to build a DIY facial recognition access control system.
Product Requirements Document
Facial Recognition Access Control System
DIY Implementation for 50-1000 Users
| Field | Value | |-------|-------| | Document Version | 1.0 | | Date | January 2026 | | Status | Draft | | Target Deployment | Q1-Q2 2026 |
Table of Contents
- Executive Summary
- Problem Statement
- Goals and Objectives
- User Stories and Use Cases
- Functional Requirements
- Non-Functional Requirements
- System Architecture
- Hardware Requirements
- Software Stack
- Implementation Plan
- Sample Code
- Project Directory Structure
- Project Timeline
- Budget Summary
- Risks and Mitigations
- Success Metrics
- Future Enhancements
1. Executive Summary
This document outlines the product requirements for a DIY facial recognition access control system designed for small to medium deployments (50-1,000 users). The system prioritizes cost-effectiveness, customizability, privacy (all processing on-premises), and integration flexibility.
1.1 Key Highlights
| Metric | Value | |--------|-------| | Hardware Cost | $150-400 total (one-time) | | Software Cost | $0 (open-source libraries) | | Recurring Cost | $0 (no cloud APIs) | | Recognition Accuracy | 97-99.8% (depending on model choice) | | Recognition Speed | 50-500ms per face | | Privacy | 100% on-premises, no data leaves the device |
2. Problem Statement
Organizations need secure, touchless access control that is affordable, customizable, and respects user privacy. Existing solutions present significant challenges:
2.1 Current Market Gaps
- Commercial systems are expensive: $1,000-2,500 per door plus ongoing fees
- Cloud-based APIs create dependencies: Recurring costs ($30-300/month), latency issues, internet dependency
- Privacy concerns: Biometric data sent to third-party servers
- Limited customization: Vendor lock-in, inability to integrate with custom workflows
- Physical credentials are insecure: Keys, cards, and PINs can be lost, stolen, or shared
3. Goals and Objectives
3.1 Primary Goals
- Deploy a functional facial recognition access control system for under $400
- Support 50-1,000 registered users with sub-second recognition
- Achieve 97%+ recognition accuracy under normal conditions
- Maintain 100% on-premises data processing
- Provide full customization through open-source software
3.2 Success Criteria
- System operational within 2 weeks of project start
- False acceptance rate (FAR) less than 0.1%
- False rejection rate (FRR) less than 3%
- System uptime greater than 99% during business hours
- User enrollment time less than 30 seconds per person
4. User Stories and Use Cases
4.1 Primary Users
End Users (Employees/Members)
- As an employee, I want to unlock the door by looking at the camera so that I don't need to carry keys or cards
- As a member, I want the system to recognize me quickly so that I'm not delayed at entry points
- As a user, I want a backup entry method (PIN/card) in case facial recognition fails
Administrators
- As an admin, I want to enroll new users by capturing their face photo so that they can access the facility
- As an admin, I want to revoke access instantly so that terminated employees cannot enter
- As an admin, I want to view access logs so that I can audit who entered and when
- As an admin, I want to set access schedules so that users can only enter during authorized hours
4.2 Use Cases
| Use Case | Description | Priority | |----------|-------------|----------| | UC-001 | User approaches door, face is recognized, door unlocks | P0 - Critical | | UC-002 | Admin enrolls new user with photo capture | P0 - Critical | | UC-003 | Unknown face detected, access denied, event logged | P0 - Critical | | UC-004 | Admin views access logs and generates reports | P1 - High | | UC-005 | User uses backup PIN when face recognition fails | P1 - High | | UC-006 | System sends alert on repeated failed attempts | P2 - Medium |
5. Functional Requirements
5.1 Face Detection and Recognition
- System shall detect faces in real-time video stream (minimum 15 FPS)
- System shall generate 128-512 dimensional face embeddings for each detected face
- System shall compare face embeddings against enrolled database using cosine similarity
- System shall support configurable similarity threshold (default: 0.6)
- System shall handle multiple detection backends: OpenCV, MTCNN, RetinaFace
- System shall support multiple recognition models: ArcFace, FaceNet, VGG-Face
5.2 User Management
- Enroll users via photo capture (webcam) or image upload
- Support multiple photos per user for improved accuracy
- Store user metadata: name, employee ID, department, access level
- Enable/disable user access without deleting profile
- Bulk import users via CSV
- Set access schedules per user or group
5.3 Access Control
- Control door lock via GPIO relay module
- Configurable unlock duration (default: 5 seconds)
- Support backup authentication: PIN code, RFID card
- Anti-passback protection (optional)
- Manual override for emergency situations
5.4 Logging and Monitoring
- Log all access attempts with timestamp, user ID, result
- Store snapshots of unrecognized faces for review
- Generate daily/weekly access reports
- Alert on suspicious activity (multiple failed attempts)
- Export logs to CSV/JSON
5.5 Administration Interface
- Web-based dashboard accessible on local network
- User management: add, edit, delete, search
- Real-time recognition feed preview
- System configuration: thresholds, timing, models
- Access log viewer with filtering
6. Non-Functional Requirements
6.1 Performance
| Metric | Target | |--------|--------| | Face detection | Less than 100ms per frame | | Face recognition | Less than 500ms total (detection + embedding + matching) | | Database search | Less than 50ms for 1,000 users | | Door unlock response | Less than 1 second from face detection | | Web interface response | Less than 2 seconds |
6.2 Reliability
- System uptime: greater than 99% during operating hours
- Automatic recovery after power failure
- Graceful degradation if camera fails (allow PIN/card fallback)
- Database backup: daily automatic backup
6.3 Security
- All data stored locally (no cloud transmission)
- Face embeddings stored, not raw images (optional)
- Admin interface requires authentication
- HTTPS for web interface
- Anti-spoofing: liveness detection (Phase 2)
6.4 Scalability
- Support 50-1,000 users on single device
- Support multiple doors with networked devices
- Centralized user database for multi-door deployments
6.5 Environmental
- Operating temperature: 0C to 40C
- Indoor use only (or with weatherproof enclosure)
- Adequate lighting: 200-1000 lux recommended
7. System Architecture
7.1 High-Level Architecture
+----------------------------------------------------------------+
| HARDWARE LAYER |
+---------------+---------------+---------------+-----------------+
| Camera | Raspberry | Relay | Solenoid Lock |
| Module | Pi 5 | Module | (12V DC) |
+---------------+---------------+---------------+-----------------+
|
+----------------------------------------------------------------+
| SOFTWARE LAYER |
+----------------------------------------------------------------+
| +--------------+ +--------------+ +------------------------+ |
| | OpenCV | | DeepFace | | Flask/FastAPI | |
| | (Capture) | | (Recognition)| | (Web Interface) | |
| +--------------+ +--------------+ +------------------------+ |
+----------------------------------------------------------------+
| +--------------+ +--------------+ +------------------------+ |
| | SQLite | | Pickle | | RPi.GPIO | |
| | (Metadata) | | (Embeddings) | | (Lock Control) | |
| +--------------+ +--------------+ +------------------------+ |
+----------------------------------------------------------------+7.2 Data Flow
- Step 1: Camera captures video frame
- Step 2: Face detection identifies face region
- Step 3: Face alignment normalizes face orientation
- Step 4: Recognition model generates embedding vector
- Step 5: Embedding compared against database
- Step 6: If match found and authorized, GPIO triggers relay
- Step 7: Relay activates solenoid lock
- Step 8: Event logged to database
7.3 Database Schema
Users Table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
employee_id VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
department VARCHAR(100),
access_level INTEGER DEFAULT 1,
is_active BOOLEAN DEFAULT TRUE,
pin_code VARCHAR(6),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);Access Logs Table
CREATE TABLE access_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id),
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
door_id VARCHAR(50),
access_granted BOOLEAN,
method VARCHAR(20), -- 'face', 'pin', 'card', 'manual'
confidence FLOAT,
snapshot_path VARCHAR(255)
);Access Schedules Table
CREATE TABLE access_schedules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id),
day_of_week INTEGER, -- 0=Monday, 6=Sunday
start_time TIME,
end_time TIME
);8. Hardware Requirements
8.1 Bill of Materials
| Component | Model | Est. Cost | Notes | |-----------|-------|-----------|-------| | Single Board Computer | Raspberry Pi 5 (8GB) | $80 | 4GB also works | | Camera Module | RPi Camera V3 | $25 | Or USB webcam | | MicroSD Card | 64GB Class 10 | $12 | 32GB minimum | | Relay Module | 5V Single Channel | $5 | Optocoupler isolated | | Solenoid Lock | 12V DC Electric | $15 | Or magnetic lock | | 12V Power Supply | 12V 2A Adapter | $10 | For solenoid lock | | 5V USB-C Power Supply | 5V 5A USB-C | $15 | For Raspberry Pi | | Enclosure + Misc | Case, wires, etc. | $30 | Jumper wires, box | | TOTAL | | $192 | |
8.2 Optional Components
| Component | Model | Est. Cost | Purpose | |-----------|-------|-----------|---------| | IR Illuminator | 850nm LED Array | $15 | Low-light operation | | Touchscreen | 7" HDMI Display | $50 | Local UI/enrollment | | RFID Reader | RC522 Module | $5 | Backup authentication | | Keypad | 4x4 Matrix | $5 | PIN backup | | UPS | Mini UPS for Pi | $25 | Power backup |
8.3 Wiring Diagram
Raspberry Pi GPIO Connections:
-----------------------------------------
Pi GPIO 17 (Pin 11) ------> Relay IN
Pi 5V (Pin 2) ------> Relay VCC
Pi GND (Pin 6) ------> Relay GND
Relay to Solenoid Lock:
-----------------------------------------
12V Adapter (+) ----------> Solenoid (+)
12V Adapter (-) ----------> Relay COM
Relay NO ----------> Solenoid (-)
Camera Connection:
-----------------------------------------
Camera ribbon cable ------> Pi CSI port
(or USB webcam to USB port)8.4 GPIO Pin Reference
| GPIO | Pin | Function | |------|-----|----------| | GPIO 17 | 11 | Relay Control (Lock) | | GPIO 27 | 13 | Status LED (Green) | | GPIO 22 | 15 | Status LED (Red) | | GPIO 23 | 16 | Door Sensor (Optional) | | GPIO 24 | 18 | Exit Button (Optional) |
9. Software Stack
9.1 Operating System
- Raspberry Pi OS (64-bit) - Debian Bookworm based
- Python 3.11+
9.2 Core Libraries
| Library | Version | Purpose | |---------|---------|---------| | deepface | 0.0.93+ | Face detection, recognition, embedding | | opencv-python | 4.9+ | Video capture, image processing | | Flask / FastAPI | 3.0+ / 0.110+ | Web interface and REST API | | RPi.GPIO / gpiozero | Latest | GPIO control for relay | | SQLite3 | Built-in | User metadata and access logs | | numpy | 1.26+ | Numerical operations, embedding storage | | pandas | 2.0+ | Data manipulation, log analysis | | Pillow | 10.0+ | Image processing |
9.3 Recommended Recognition Models
| Model | Accuracy (LFW) | Speed | Embedding Size | Recommendation | |-------|----------------|-------|----------------|----------------| | ArcFace | 99.41% | Medium | 512 | Best overall | | FaceNet512 | 99.65% | Slower | 512 | Highest accuracy | | FaceNet | 99.20% | Medium | 128 | Good balance | | VGG-Face | 98.78% | Fast | 4096 | Good for low-power | | Dlib | 99.38% | Fast | 128 | Good balance | | SFace | 99.60% | Fast | 128 | Lightweight |
9.4 Detection Backends
| Backend | Speed | Accuracy | Notes | |---------|-------|----------|-------| | RetinaFace | Slower | Excellent | Best for accuracy | | MTCNN | Medium | Very Good | Good balance | | OpenCV | Fast | Good | Best for speed | | SSD | Fast | Good | Lightweight | | Dlib | Medium | Good | CPU-friendly |
10. Implementation Plan
10.1 Phase 1: Hardware Setup (Days 1-2)
- Flash Raspberry Pi OS (64-bit) to SD card using Raspberry Pi Imager
- Enable SSH and configure WiFi in imager settings
- Boot Raspberry Pi and perform initial setup (updates, locale)
- Connect and test camera module (libcamera-hello)
- Wire relay module to GPIO (Pin 17 to IN, 5V to VCC, GND to GND)
- Connect solenoid lock through relay (12V supply to COM, solenoid to NO)
- Test relay switching with simple GPIO script
10.2 Phase 2: Software Environment (Days 2-3)
- Create Python virtual environment
- Install system dependencies (cmake, libatlas, libhdf5)
- Install Python packages: deepface, opencv-python, flask, RPi.GPIO
- Verify DeepFace installation by running test recognition
- Create project directory structure
- Initialize SQLite database with schema
10.3 Phase 3: Core Recognition System (Days 4-7)
- Implement video capture module (OpenCV VideoCapture)
- Implement face detection with configurable backend
- Implement face embedding generation using DeepFace.represent()
- Implement database search using DeepFace.find()
- Implement access decision logic with configurable threshold
- Implement GPIO lock control with timing
- Implement access logging to SQLite
- Create main recognition loop with threading
10.4 Phase 4: Administration Interface (Days 8-10)
- Set up Flask application structure
- Implement user enrollment endpoint (photo capture/upload)
- Implement user management CRUD endpoints
- Implement access log viewing endpoint
- Implement system configuration endpoint
- Build HTML templates for dashboard
- Add basic authentication for admin interface
- Implement live video feed preview
10.5 Phase 5: Testing and Deployment (Days 11-14)
- Enroll test users (minimum 10)
- Test recognition accuracy under various lighting conditions
- Test false rejection rate with enrolled users
- Test false acceptance rate with unknown faces
- Configure system as systemd service for auto-start
- Set up log rotation
- Configure automatic database backup
- Install and secure enclosure at entry point
- Document system and create user guide
11. Sample Code
11.1 Main Recognition Loop
# main.py - Core facial recognition access control
import cv2
import time
import sqlite3
from deepface import DeepFace
from gpiozero import OutputDevice
from datetime import datetime
import threading
# Configuration
CONFIG = {
"database_path": "./face_database",
"model_name": "ArcFace",
"detector_backend": "retinaface",
"distance_metric": "cosine",
"threshold": 0.6,
"unlock_duration": 5,
"relay_pin": 17,
"camera_id": 0
}
# Initialize GPIO
relay = OutputDevice(CONFIG["relay_pin"], active_high=False)
def unlock_door(duration=5):
"""Activate relay to unlock door for specified duration."""
relay.on()
time.sleep(duration)
relay.off()
def log_access(user_id, granted, confidence, method="face"):
"""Log access attempt to SQLite database."""
conn = sqlite3.connect("access_control.db")
cursor = conn.cursor()
cursor.execute("""
INSERT INTO access_logs
(user_id, timestamp, access_granted, method, confidence)
VALUES (?, ?, ?, ?, ?)
""", (user_id, datetime.now(), granted, method, confidence))
conn.commit()
conn.close()
def recognize_face(frame):
"""Attempt to recognize face in frame against database."""
try:
results = DeepFace.find(
img_path=frame,
db_path=CONFIG["database_path"],
model_name=CONFIG["model_name"],
detector_backend=CONFIG["detector_backend"],
distance_metric=CONFIG["distance_metric"],
enforce_detection=True,
silent=True
)
if len(results) > 0 and len(results[0]) > 0:
best_match = results[0].iloc[0]
distance = best_match["distance"]
identity = best_match["identity"]
if distance < CONFIG["threshold"]:
confidence = 1 - distance
return True, identity, confidence
return False, None, 0.0
except Exception as e:
print(f"Recognition error: {e}")
return False, None, 0.0
def main_loop():
"""Main recognition loop."""
cap = cv2.VideoCapture(CONFIG["camera_id"])
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
last_recognition_time = 0
cooldown_period = 3 # Prevent rapid re-recognition
print("Facial Recognition Access Control Started")
print("Press Ctrl+C to exit")
try:
while True:
ret, frame = cap.read()
if not ret:
continue
current_time = time.time()
# Only attempt recognition after cooldown
if current_time - last_recognition_time > cooldown_period:
recognized, identity, confidence = recognize_face(frame)
if recognized:
print(f"Access GRANTED: {identity} ({confidence:.2%})")
log_access(identity, True, confidence)
# Unlock door in separate thread
unlock_thread = threading.Thread(
target=unlock_door,
args=(CONFIG["unlock_duration"],)
)
unlock_thread.start()
last_recognition_time = current_time
# Display frame (optional, for debugging)
cv2.imshow("Access Control", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except KeyboardInterrupt:
print("\nShutting down...")
finally:
cap.release()
cv2.destroyAllWindows()
relay.close()
if __name__ == "__main__":
main_loop()11.2 User Enrollment Script
# enroll.py - User enrollment script
import cv2
import os
import sqlite3
from datetime import datetime
DATABASE_PATH = "./face_database"
DB_FILE = "access_control.db"
def init_database():
"""Initialize SQLite database with schema."""
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
employee_id VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
department VARCHAR(100),
access_level INTEGER DEFAULT 1,
is_active BOOLEAN DEFAULT TRUE,
pin_code VARCHAR(6),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS access_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id),
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
door_id VARCHAR(50),
access_granted BOOLEAN,
method VARCHAR(20),
confidence FLOAT,
snapshot_path VARCHAR(255)
)
""")
conn.commit()
conn.close()
def enroll_user(employee_id, name, department="General"):
"""Capture photos and enroll a new user."""
# Create user directory
user_dir = f"{DATABASE_PATH}/{employee_id}_{name.replace(' ', '_')}"
os.makedirs(user_dir, exist_ok=True)
# Add to database
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
try:
cursor.execute("""
INSERT INTO users (employee_id, name, department)
VALUES (?, ?, ?)
""", (employee_id, name, department))
conn.commit()
except sqlite3.IntegrityError:
print(f"User {employee_id} already exists!")
conn.close()
return False
conn.close()
# Capture photos
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
photo_count = 0
print(f"\n{'='*50}")
print(f"Enrolling: {name} ({employee_id})")
print(f"{'='*50}")
print("Instructions:")
print(" - Press SPACE to capture a photo")
print(" - Capture 3-5 photos from different angles")
print(" - Press Q when done")
print(f"{'='*50}\n")
while True:
ret, frame = cap.read()
if not ret:
continue
# Draw instructions on frame
display_frame = frame.copy()
cv2.putText(display_frame, f"Photos: {photo_count}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(display_frame, "SPACE=Capture, Q=Done", (10, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
cv2.imshow("Enrollment", display_frame)
key = cv2.waitKey(1) & 0xFF
if key == ord(' '): # Space to capture
photo_path = f"{user_dir}/{name.replace(' ', '_')}_{photo_count}.jpg"
cv2.imwrite(photo_path, frame)
photo_count += 1
print(f"Captured photo {photo_count}: {photo_path}")
elif key == ord('q'): # Q to quit
break
cap.release()
cv2.destroyAllWindows()
if photo_count > 0:
print(f"\nSuccessfully enrolled {name} with {photo_count} photos")
print(f" Directory: {user_dir}")
return True
else:
print(f"\nNo photos captured for {name}")
# Remove empty directory and database entry
os.rmdir(user_dir)
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute("DELETE FROM users WHERE employee_id = ?", (employee_id,))
conn.commit()
conn.close()
return False
def list_users():
"""List all enrolled users."""
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute("SELECT employee_id, name, department, is_active FROM users")
users = cursor.fetchall()
conn.close()
print(f"\n{'='*60}")
print(f"{'Employee ID':<15} {'Name':<20} {'Department':<15} {'Active'}")
print(f"{'='*60}")
for user in users:
status = "Yes" if user[3] else "No"
print(f"{user[0]:<15} {user[1]:<20} {user[2]:<15} {status}")
print(f"{'='*60}")
print(f"Total users: {len(users)}\n")
def delete_user(employee_id):
"""Delete a user and their photos."""
import shutil
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
# Get user info
cursor.execute("SELECT name FROM users WHERE employee_id = ?", (employee_id,))
result = cursor.fetchone()
if not result:
print(f"User {employee_id} not found!")
conn.close()
return False
name = result[0]
user_dir = f"{DATABASE_PATH}/{employee_id}_{name.replace(' ', '_')}"
# Delete from database
cursor.execute("DELETE FROM users WHERE employee_id = ?", (employee_id,))
conn.commit()
conn.close()
# Delete photos directory
if os.path.exists(user_dir):
shutil.rmtree(user_dir)
print(f"Deleted user {name} ({employee_id})")
return True
# Main CLI
if __name__ == "__main__":
import sys
init_database()
os.makedirs(DATABASE_PATH, exist_ok=True)
if len(sys.argv) < 2:
print("Usage:")
print(" python enroll.py add <employee_id> <name> [department]")
print(" python enroll.py list")
print(" python enroll.py delete <employee_id>")
sys.exit(1)
command = sys.argv[1]
if command == "add" and len(sys.argv) >= 4:
emp_id = sys.argv[2]
name = sys.argv[3]
dept = sys.argv[4] if len(sys.argv) > 4 else "General"
enroll_user(emp_id, name, dept)
elif command == "list":
list_users()
elif command == "delete" and len(sys.argv) >= 3:
delete_user(sys.argv[2])
else:
print("Invalid command or missing arguments")11.3 Flask Web Interface
# web/app.py - Flask admin interface
from flask import Flask, render_template, request, jsonify, Response
import cv2
import sqlite3
import os
from datetime import datetime, timedelta
app = Flask(__name__)
DATABASE = "../access_control.db"
FACE_DB = "../face_database"
def get_db():
conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row
return conn
@app.route('/')
def dashboard():
"""Main dashboard with statistics."""
conn = get_db()
# Get stats
total_users = conn.execute("SELECT COUNT(*) FROM users WHERE is_active=1").fetchone()[0]
today = datetime.now().date()
today_access = conn.execute(
"SELECT COUNT(*) FROM access_logs WHERE DATE(timestamp) = ?",
(today,)
).fetchone()[0]
# Recent access logs
recent_logs = conn.execute("""
SELECT al.*, u.name
FROM access_logs al
LEFT JOIN users u ON al.user_id = u.employee_id
ORDER BY al.timestamp DESC
LIMIT 10
""").fetchall()
conn.close()
return render_template('dashboard.html',
total_users=total_users,
today_access=today_access,
recent_logs=recent_logs)
@app.route('/users')
def users():
"""User management page."""
conn = get_db()
users = conn.execute("SELECT * FROM users ORDER BY name").fetchall()
conn.close()
return render_template('users.html', users=users)
@app.route('/api/users', methods=['GET', 'POST'])
def api_users():
"""REST API for user management."""
conn = get_db()
if request.method == 'GET':
users = conn.execute("SELECT * FROM users").fetchall()
conn.close()
return jsonify([dict(u) for u in users])
elif request.method == 'POST':
data = request.json
try:
conn.execute(
"INSERT INTO users (employee_id, name, department) VALUES (?, ?, ?)",
(data['employee_id'], data['name'], data.get('department', 'General'))
)
conn.commit()
conn.close()
return jsonify({"status": "success"})
except Exception as e:
conn.close()
return jsonify({"status": "error", "message": str(e)}), 400
@app.route('/api/users/<employee_id>', methods=['DELETE'])
def api_delete_user(employee_id):
"""Delete a user."""
conn = get_db()
conn.execute("DELETE FROM users WHERE employee_id = ?", (employee_id,))
conn.commit()
conn.close()
return jsonify({"status": "success"})
@app.route('/logs')
def logs():
"""Access logs page."""
conn = get_db()
logs = conn.execute("""
SELECT al.*, u.name
FROM access_logs al
LEFT JOIN users u ON al.user_id = u.employee_id
ORDER BY al.timestamp DESC
LIMIT 100
""").fetchall()
conn.close()
return render_template('logs.html', logs=logs)
@app.route('/api/logs')
def api_logs():
"""REST API for access logs."""
conn = get_db()
# Get query parameters
start_date = request.args.get('start')
end_date = request.args.get('end')
user_id = request.args.get('user_id')
query = """
SELECT al.*, u.name
FROM access_logs al
LEFT JOIN users u ON al.user_id = u.employee_id
WHERE 1=1
"""
params = []
if start_date:
query += " AND DATE(al.timestamp) >= ?"
params.append(start_date)
if end_date:
query += " AND DATE(al.timestamp) <= ?"
params.append(end_date)
if user_id:
query += " AND al.user_id = ?"
params.append(user_id)
query += " ORDER BY al.timestamp DESC LIMIT 1000"
logs = conn.execute(query, params).fetchall()
conn.close()
return jsonify([dict(log) for log in logs])
def generate_frames():
"""Generator for video streaming."""
camera = cv2.VideoCapture(0)
while True:
success, frame = camera.read()
if not success:
break
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.route('/video_feed')
def video_feed():
"""Video streaming route."""
return Response(generate_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)12. Project Directory Structure
face-access-control/
+-- main.py # Main recognition loop
+-- enroll.py # User enrollment script
+-- config.py # Configuration settings
+-- requirements.txt # Python dependencies
+-- README.md # Project documentation
|
+-- face_database/ # Face embeddings storage
| +-- EMP001_John_Smith/
| | +-- John_0.jpg
| | +-- John_1.jpg
| | +-- John_2.jpg
| +-- EMP002_Jane_Doe/
| | +-- Jane_0.jpg
| +-- representations_arcface.pkl # Cached embeddings
|
+-- web/ # Flask web interface
| +-- app.py # Flask application
| +-- templates/
| | +-- base.html
| | +-- dashboard.html
| | +-- users.html
| | +-- enroll.html
| | +-- logs.html
| +-- static/
| +-- css/
| | +-- style.css
| +-- js/
| +-- app.js
|
+-- database/
| +-- schema.sql # Database schema
| +-- access_control.db # SQLite database
|
+-- logs/
| +-- access.log # Access log file
| +-- error.log # Error log file
| +-- snapshots/ # Unrecognized face images
|
+-- scripts/
| +-- setup.sh # Initial setup script
| +-- backup.sh # Database backup script
| +-- test_hardware.py # Hardware testing
| +-- test_recognition.py # Recognition testing
|
+-- systemd/
| +-- face-access.service # Systemd service file
|
+-- docs/
+-- user_guide.md # End user documentation
+-- admin_guide.md # Administrator guide
+-- troubleshooting.md # Common issues and solutions13. Project Timeline
| Phase | Tasks | Duration | Deliverable | |-------|-------|----------|-------------| | 1 | Hardware setup, OS install, wiring | Days 1-2 | Working Pi + relay | | 2 | Software environment, dependencies | Day 3 | DeepFace verified | | 3 | Core recognition system | Days 4-7 | Working recognition | | 4 | Admin web interface | Days 8-10 | Web dashboard | | 5 | Testing, optimization, deployment | Days 11-14 | Production ready | | Total | | 2 Weeks | |
14. Budget Summary
| Category | One-Time Cost | Recurring (Monthly) | |----------|---------------|---------------------| | Hardware (BOM) | $192 | $0 | | Software (Open Source) | $0 | $0 | | Cloud/API Fees | $0 | $0 | | Electricity (~10W device) | $0 | ~$1 | | TOTAL | $192 | ~$1 |
Cost Comparison with Alternatives
| Solution | One-Time | Monthly | 3-Year Total | |----------|----------|---------|--------------| | This DIY Solution | $192 | $1 | $228 | | Commercial System | $1,500 | $50 | $3,300 | | Cloud API (AWS/Google) | $100 | $100 | $3,700 |
Savings: $3,000+ over 3 years
15. Risks and Mitigations
| Risk | Severity | Mitigation | |------|----------|------------| | Poor lighting affects accuracy | High | Add IR illumination, use RetinaFace detector, adjust threshold | | Photo spoofing attacks | High | Implement liveness detection (Phase 2), require blink/head movement | | System downtime | Medium | Provide PIN/card backup, auto-restart with systemd, UPS for power | | Data breach | Medium | Store embeddings only (not photos), local network only, encryption | | Hardware failure | Low | Keep spare components, daily database backup, manual override | | Slow recognition | Low | Use faster model (VGG-Face), enable embedding cache, optimize code | | User resistance | Low | Provide training, backup entry methods, clear communication |
16. Success Metrics
| Metric | Target | Measurement Method | |--------|--------|-------------------| | Recognition Accuracy | >97% | Test with enrolled users | | False Acceptance Rate | less than 0.1% | Test with unknown faces | | False Rejection Rate | less than 3% | Track failed attempts for enrolled users | | Recognition Speed | less than 1 second | Measure door unlock latency | | System Uptime | >99% | Monitor with systemd | | User Enrollment Time | less than 30 seconds | Time new enrollments | | Total Cost | less than $250/door | Track actual expenses |
17. Future Enhancements (Phase 2)
Priority 1: Security Enhancements
- Liveness Detection: Require blink or head movement to prevent photo spoofing
- Anti-Spoofing: Detect printed photos, screens, and masks
- Encryption: Encrypt stored embeddings and database
Priority 2: Scalability
- Multi-Door Support: Centralized database with networked devices
- Load Balancing: Distribute recognition across multiple processors
- Cloud Backup: Optional encrypted backup to cloud storage
Priority 3: User Experience
- Mobile App: Remote access log viewing, user enrollment via phone
- Voice Feedback: Audio confirmation of access granted/denied
- Touchscreen UI: Local enrollment and status display
Priority 4: Integration
- Visitor Management: Temporary access codes, visitor photo capture
- HR System Integration: Sync users with HR database
- Time and Attendance: Track work hours based on access logs
- Security Alerts: Integration with alarm systems
Priority 5: Analytics
- Traffic Analytics: Peak hours, busiest entry points
- Attendance Reports: Daily/weekly/monthly summaries
- Anomaly Detection: Alert on unusual access patterns
Appendix A: Installation Commands
System Setup
# System Updates
sudo apt update && sudo apt upgrade -y
# Install System Dependencies
sudo apt install -y python3-pip python3-venv cmake libatlas-base-dev \
libhdf5-dev libhdf5-serial-dev libopenblas-dev libjpeg-dev \
libpng-dev libtiff-dev libavcodec-dev libavformat-dev \
libswscale-dev libv4l-dev libxvidcore-dev libx264-dev \
libgtk-3-dev libcanberra-gtk3-module
# Create Project Directory
mkdir -p ~/face-access-control
cd ~/face-access-control
# Create Virtual Environment
python3 -m venv venv
source venv/bin/activate
# Install Python Packages
pip install --upgrade pip
pip install deepface opencv-python flask numpy pandas pillow
pip install RPi.GPIO gpiozero # For Raspberry Pi
# Verify Installation
python -c "from deepface import DeepFace; print('DeepFace OK')"
python -c "import cv2; print(f'OpenCV {cv2.__version__} OK')"Database Initialization
# Create database directory
mkdir -p database
# Initialize database
sqlite3 database/access_control.db < database/schema.sqlCamera Testing
# Test Pi Camera
libcamera-hello
# Test USB Camera
python -c "import cv2; cap=cv2.VideoCapture(0); print('Camera:', cap.isOpened())"GPIO Testing
# Test relay (will click on/off)
python -c "
from gpiozero import OutputDevice
import time
relay = OutputDevice(17)
print('Relay ON')
relay.on()
time.sleep(1)
print('Relay OFF')
relay.off()
relay.close()
"Appendix B: Systemd Service File
Service Configuration
# /etc/systemd/system/face-access.service
[Unit]
Description=Facial Recognition Access Control
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/face-access-control
Environment=PATH=/home/pi/face-access-control/venv/bin
ExecStart=/home/pi/face-access-control/venv/bin/python main.py
Restart=always
RestartSec=10
StandardOutput=append:/home/pi/face-access-control/logs/access.log
StandardError=append:/home/pi/face-access-control/logs/error.log
[Install]
WantedBy=multi-user.targetService Management
# Copy service file
sudo cp systemd/face-access.service /etc/systemd/system/
# Reload systemd
sudo systemctl daemon-reload
# Enable service (start on boot)
sudo systemctl enable face-access
# Start service
sudo systemctl start face-access
# Check status
sudo systemctl status face-access
# View logs
journalctl -u face-access -f
# Stop service
sudo systemctl stop face-access
# Restart service
sudo systemctl restart face-accessAppendix C: Troubleshooting
Common Issues
| Issue | Possible Cause | Solution | |-------|---------------|----------| | Camera not detected | Wrong device ID | Try camera_id: 1 or check /dev/video* | | Low accuracy | Poor lighting | Add lighting, use RetinaFace backend | | Slow recognition | Large model | Switch to VGG-Face or enable caching | | Relay not clicking | Wrong GPIO pin | Verify wiring, test with simple script | | Permission denied | GPIO access | Add user to gpio group | | Import errors | Missing dependencies | Reinstall in virtual environment |
Debug Mode
# Add to main.py for debugging
import logging
logging.basicConfig(level=logging.DEBUG)
# Enable DeepFace verbose output
results = DeepFace.find(..., silent=False)Performance Optimization
# Use embedding cache (auto-generated on first run)
# Delete to rebuild: rm face_database/representations_*.pkl
# Use faster detector for real-time
CONFIG["detector_backend"] = "opencv" # Fastest
CONFIG["detector_backend"] = "ssd" # Fast + accurate
# Reduce frame size
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)Appendix D: requirements.txt
# Core dependencies
deepface>=0.0.93
opencv-python>=4.9.0
numpy>=1.26.0
pandas>=2.0.0
Pillow>=10.0.0
# Web interface
Flask>=3.0.0
gunicorn>=21.0.0
# Raspberry Pi GPIO (install only on Pi)
# RPi.GPIO>=0.7.0
# gpiozero>=2.0.0
# Optional: Better face detection
# tensorflow>=2.15.0 # For RetinaFace
# torch>=2.0.0 # Alternative backend
# Development
pytest>=7.0.0
black>=23.0.0
flake8>=6.0.0