A vision-based attendance system that recognizes enrolled people from a webcam, confirms they are live (real-person anti-spoofing), enforces a mask policy, and logs every attendance event to a local SQLite database.
Honest framing: Mask detection is a policy check (is this person covering their face?), not anti-spoofing. The real anti-spoofing layer in this project is blink-based liveness detection — a printed photo or a phone screen cannot blink. Do not advertise mask detection as spoof-proof.
webcam
│
├─ MediaPipe FaceMesh ── face box + eye landmarks (detection AND blink)
│ │
│ └─ FaceNet (keras-facenet) ── 512-d embedding → match vs faces/ db
│ (euclidean < 0.60)
│
├─ Blink liveness (EAR) ── real anti-spoofing layer (photos/screens can't blink)
│
├─ MobileNetV2 (fine-tuned) ── masked / unmasked classification (policy)
│
└─ SQLite (attendance.db) ── name, time, confidence, flags
| File | Purpose |
|---|---|
main.py |
Live attendance app (detect → identify → liveness → mask → log) |
collect_data.py |
Capture webcam cropped faces into dataset/{mask,no_mask} |
train_mask_model.py |
Fine-tune MobileNetV2, save models/mask_model.keras |
manage_db.py |
Enroll faces + inspect / export attendance |
# 1. Isolated environment (the stack pins numpy<2, conflict with global packages)
python -m venv .venv
.venv\Scripts\activate
# 2. Install
pip install -r requirements.txt
# 3. Enroll people (capture 3–4 shots: front, slight tilt, good lighting)
python manage_db.py init
python capture_selfie.py anas # SPACE = save, BACKSPACE = undo, ESC = quit
python manage_db.py list
# ...or enroll existing photos, one template per call:
python manage_db.py enroll anas C:\path\to\anas_front.jpg
python manage_db.py enroll anas C:\path\to\anas_side.jpg# Capture ~200 crops per class: press N (no mask) / M (mask), ESC to finish
python collect_data.py
# Train
python train_mask_model.pypython main.pyPoint the camera at your face, blink once — the screen badge turns LIVE, and if you're enrolled and unmasked, attendance is written to attendance.db.
python manage_db.py logs # recent entries
python manage_db.py export # attendance_export.csv| Constant | Default | Meaning |
|---|---|---|
MATCH_THRESHOLD |
0.60 |
Max embedding distance to accept identity |
REQUIRED_BLINKS |
1 |
Blinks within BLINK_WINDOW to pass liveness |
BLINK_WINDOW |
3.0 |
Rolling seconds window for blink counting |
EYE_CLOSE_EAR |
0.21 |
Eye Aspect Ratio below which an eye is "closed" |
MARK_COOLDOWN |
60.0 |
Min seconds between two marks of the same person |
ALLOW_MASKED_ATTENDANCE |
False |
Policy: allow attendance while masked? |
Tuning: if faces are mis-matched, lower MATCH_THRESHOLD (stricter). Always add several templates per person from different angles/lighting — matching uses the nearest template of each person, so more templates equals far better recognition. The on-screen % is a calibrated match probability derived from the FaceNet distance; same-person matches typically read 90%+.
- Python 3.11, TensorFlow 2.15 (Keras high-level API)
- keras-facenet (FaceNet embeddings + MTCNN detection)
- MediaPipe (FaceMesh eye landmarks)
- MobileNetV2 fine-tuning (transfer learning for mask classification)
- OpenCV, SQLite (stdlib)
- This is a learning-grade system, not production security. Blink liveness helps against static prints but not against pre-recorded video.
- FaceNet embeddings run in a background worker thread, so the camera loop never blocks; the loop itself is pure MediaPipe FaceMesh (fast) and blinks are captured reliably.
- Recognition quality depends heavily on enrollment photo quality and lighting.