Version: v1.0 | Date: 2026-06-20 | Course project
A fully offline classroom face-attendance system built on YOLOv8-Face + InsightFace ArcFace. Upload a classroom panorama photo — faces are detected, identities recognized, and attendance completed automatically, all on a local GPU.
# 1. Install dependencies (see Deployment for CUDA/CPU variants)
pip install -r requirements.txt
# 2. Initialize the database
python scripts/init_db.py
# 3. Download model weights (NOT included in this repo)
python scripts/download_weights.py
# 4. Start the service
python run.py
# 5. Open http://localhost:5000First-use flow:
- Open
http://localhost:5000/register→ enroll student faces - Open
http://localhost:5000→ upload a classroom panorama photo → attendance done
| Module | Description | Technology |
|---|---|---|
| 🔢 Face detection & counting | Detect all faces & count in an image | YOLOv8n-Face |
| 👤 Face recognition attendance | Identify each student, output present/absent lists | ArcFace (ResNet50) + cosine similarity |
| 📝 Student management | Enroll / query / delete students & face features | SQLite + Flask API |
| 📊 Attendance history | Query attendance records by date | SQLite persistence |
| 📈 System analytics | 6 academic-style charts (ROC/PR, t-SNE, FPS…) | Matplotlib + scikit-learn |
Core highlights:
- ✅ Fully offline — all model weights stored locally, no network dependency
- ✅ GPU accelerated — YOLOv8 + ONNX Runtime CUDA inference, ~50 FPS on RTX 4060
- ✅ Automatic fallback — switches to CPU when VRAM runs out, without interrupting service
- ✅ Annotated visualization — results with face boxes + name labels
face_attendance_system/
├── app/ # Flask web application
│ ├── __init__.py # Flask app factory
│ ├── routes.py # API + page routes (12 endpoints)
│ ├── errors.py # 7-level exception hierarchy + global error handling
│ ├── utils.py # Image validation, file saving
│ ├── analysis.py # System analytics (6 charts)
│ ├── templates/ # Jinja2 templates (7 pages)
│ │ ├── base.html # Base layout
│ │ ├── index.html # Home dashboard
│ │ ├── register.html # Student enrollment
│ │ ├── attendance.html # Classroom attendance
│ │ ├── count.html # Face counting
│ │ ├── students.html # Student management
│ │ ├── history.html # Attendance history
│ │ └── system.html # System analytics
│ └── static/css/style.css # Global styles
├── engine/ # AI inference engine
│ ├── detector.py # YOLOv8-Face detector
│ ├── recognizer.py # ArcFace feature extractor
│ ├── matcher.py # Cosine-similarity batch matching
│ └── pipeline.py # Full inference pipeline (singleton)
├── database/ # Persistence
│ ├── schema.sql # Table creation (3 tables)
│ ├── db.py # DAO layer (Student/Attendance/UploadLog)
│ └── attendance.db # SQLite database (created at runtime)
├── train/ # Model training
│ ├── train.py # YOLOv8-Face WIDERFace fine-tuning
│ ├── evaluate.py # mAP/PR/R evaluation
│ ├── export_onnx.py # ONNX export
│ └── config_widerface.yaml # WIDERFace training config
├── scripts/ # Operations
│ ├── download_weights.py # Model weight download
│ ├── download_datasets.py # Dataset download
│ ├── benchmark_gpu.py # GPU benchmark
│ ├── clean_temp.py # Temp file cleanup
│ └── init_db.py # Database initialization
├── weights/ # Model weights (NOT in repo — run scripts/download_weights.py)
├── data/ # Runtime data (auto-created, NOT in repo)
├── logs/ # Application logs (runtime)
├── config.py # Global configuration
├── run.py # Entry point
├── requirements.txt # Python dependencies
└── README.md / README_zh.md # This documentation
Classroom Panorama Photo
│
▼
[1] YOLOv8-Face Detection → N cropped faces
│
▼
[2] ArcFace 512-D Embedding → N feature vectors
│
▼
[3] Cosine Similarity Matching → present + absent lists
│
▼
[4] Annotated image + DB write → JSON response
┌──────────────┐ ┌──────────────────────┐ ┌──────────────┐
│ students │ │ attendance_records │ │ upload_logs │
├──────────────┤ ├──────────────────────┤ ├──────────────┤
│ id (PK) │→1:N→│ id (PK) │ │ id (PK) │
│ name │ │ student_id (FK) │ │ image_path │
│ student_no │ │ confidence │ │ total_faces │
│ face_feature │ │ created_at │ │ matched_count│
│ created_at │ └──────────────────────┘ │ created_at │
└──────────────┘ └──────────────┘
students.face_feature: 512-D float32 vector stored as BLOB (2048 bytes)- Serialization:
np.ndarray → .tobytes()/np.frombuffer() - Cascade delete: removing a student deletes all their attendance records
| Component | Minimum | Recommended |
|---|---|---|
| OS | Windows 10+ / Ubuntu 20.04+ / macOS 12+ | Windows 11 / Ubuntu 22.04 |
| Python | 3.8+ | 3.10 |
| GPU (optional) | NVIDIA GTX 1050 (4GB) | NVIDIA RTX 4060+ (8GB+) |
| CUDA | 11.8+ | 12.1 |
| RAM | 8 GB | 16 GB+ |
| Disk | 2 GB (with models) | 5 GB+ (with datasets) |
nvidia-smi # verify GPU driver
nvcc --version # verify CUDA (expect release 12.1)python -m venv venv
# Windows: venv\Scripts\activate | Linux/macOS: source venv/bin/activate
# CUDA 12.1 (default)
pip install -r requirements.txt
# CUDA 11.8
pip install torch==2.3.1+cu118 torchvision==0.18.1+cu118 --index-url https://download.pytorch.org/whl/cu118
pip install ultralytics==8.2.0 insightface==1.0.1 onnxruntime-gpu==1.18.0 flask==3.0.3 opencv-python==4.9.0.80 pillow==10.3.0 numpy==1.26.4 scipy==1.13.0 tqdm==4.66.4 pyyaml==6.0.1
# CPU mode (no NVIDIA GPU)
pip install torch==2.3.1 torchvision==0.18.1 --index-url https://download.pytorch.org/whl/cpu
pip install ultralytics==8.2.0 insightface==1.0.1 onnxruntime==1.18.0 flask==3.0.3 opencv-python==4.9.0.80 pillow==10.3.0 numpy==1.26.4 scipy==1.13.0 tqdm==4.66.4 pyyaml==6.0.1python -c "import torch; print('CUDA:', torch.cuda.is_available())"
python -c "import insightface; print(insightface.__version__)"
python -c "from ultralytics import YOLO; print('YOLOv8 OK')"
python -c "import onnxruntime; print(onnxruntime.get_available_providers())"Expected GPU output: CUDA: True + providers: ['CUDAExecutionProvider', 'CPUExecutionProvider']
All weights are stored locally; the app never downloads at runtime. Weights are NOT included in this repository (single files exceed GitHub's 100 MB limit).
python scripts/download_weights.py # download all (YOLOv8-Face + buffalo_l)
python scripts/download_weights.py --detector # YOLOv8-Face only
python scripts/download_weights.py --recognizer # InsightFace onlyExpected layout after download:
weights/
├── detection/
│ └── yolov8n-face.pt # 6.1 MB — YOLOv8 Nano Face
└── recognition/models/buffalo_l/
├── det_10g.onnx # 17 MB — SCRFD-10GF detector
├── w600k_r50.onnx # 174 MB — ResNet50 ArcFace
├── 2d106det.onnx # 5 MB — 106-point landmark
└── genderage.onnx # 1.3 MB — gender & age
⚠️ buffalo_lis licensed for non-commercial research use only.
Key parameters in config.py:
DEVICE = 'cuda' # 'cuda' | 'cpu'
USE_FP16 = True # FP16 mixed precision (auto for VRAM ≤ 4GB)
MATCH_THRESHOLD = 0.48 # cosine similarity ≥ this → same person
DUPLICATE_THRESHOLD = 0.85 # duplicate check on enrollment
DET_CONF_THRESHOLD = 0.25 # YOLO confidence
DET_IOU_THRESHOLD = 0.45 # NMS IoU
DET_IMAGE_SIZE = 640 # inference resolution
MAX_IMAGE_SIZE_MB = 10 # upload limitTuning tips:
| Symptom | Adjustment |
|---|---|
Too many Unknown matches |
Lower MATCH_THRESHOLD (e.g. 0.40) |
| Confusing different people | Raise MATCH_THRESHOLD (e.g. 0.55) |
Startup:
python run.py # default localhost:5000
python run.py --port 8080 # custom port
python run.py --host 0.0.0.0 # LAN access
python run.py --no-gpu # CPU mode
python run.py --debug # debug mode| Method | Path | Description |
|---|---|---|
POST |
/api/register |
Enroll student (multipart: name + image) |
POST |
/api/attendance |
Classroom attendance (multipart: image) |
POST |
/api/count |
Face counting (multipart: image) |
GET |
/api/students |
List all students |
GET |
/api/students/<id> |
Student detail |
DELETE |
/api/students/<id> |
Delete student (cascade) |
GET |
/api/history?date=YYYY-MM-DD |
Attendance history |
GET |
/api/stats |
Dashboard statistics |
GET |
/api/status |
System status (GPU/models) |
GET |
/api/system/analysis |
Analytics chart data |
- Architecture: CSPDarknet Backbone + PAN-FPN Neck + Decoupled Head (anchor-free)
- Model: YOLOv8n-Face — 3.2M params, 8.7G FLOPs, WIDERFace Hard 79.6%
- Loss: CIoU + Distribution Focal Loss + BCE
-
Core: Additive Angular Margin Loss (
$s=64, m=0.5$ ) - Backbone: ResNet50 → 512-D L2-normalized embedding
- Training data: WebFace600K (600K identities, 40M images)
- All features L2-normalized to the unit hypersphere → dot product = cosine similarity
- Batch matrix multiply:
$S_{(N \times M)} = F_{(N \times 512)} @ D^T_{(512 \times M)}$ - Complexity:
$O(N \times M \times 512)$ ; on GPU with N=35, M=40 → < 1 ms - Dedup: multiple faces of one student → keep the highest confidence
| Scenario | Faces | Detection | Recognition | Total | Effective FPS |
|---|---|---|---|---|---|
| Small classroom | 10 | 45 ms | 180 ms | 230 ms | ~43 |
| Medium classroom | 25 | 52 ms | 420 ms | 480 ms | ~52 |
| Large classroom | 50 | 60 ms | 850 ms | 920 ms | ~54 |
Figures and tables from the course report — privacy-filtered, no personal photos.

Fig. 1 — ArcFace cosine similarity distribution. Blue: genuine pairs; red: impostor pairs. Bimodal separation reflects discriminative power; dashed line = match threshold (0.48).

Fig. 2 — Face recognition performance: (a) ROC curve (AUC → 1.0 better); (b) PR curve (AP → 1.0 better).

Fig. 3 — Pairwise cosine similarity matrix between students (diagonal = 1.0; red dashed line = match threshold).

Fig. 4 — t-SNE projection of 512-D ArcFace features (colors: K-Means clusters, numbers: student IDs).

Fig. 5 — YOLOv8-Face + ArcFace throughput: (a) faces/sec by batch size (GPU vs CPU); (b) GPU speedup ratio.

Fig. 6 — mAP@0.5 of face detection models across scenarios (WIDER Face validation set).

Fig. 7 — Collaboration of YOLOv8-Face (detection & localization) and InsightFace/ArcFace (feature extraction & identification).

Fig. 8 — YOLOv8-Face network architecture (CSPDarknet backbone + PAN-FPN neck + decoupled head).

Fig. 9 — Core business workflow of the attendance system.
| Variant | Easy | Medium | Hard |
|---|---|---|---|
| yolov8-lite-t | 90.4 | 87.7 | 73.3 |
| yolov8n (selected) | 94.6 | 92.3 | 79.6 |
| yolov8s | 96.1 | 94.2 | 83.1 |
| yolov8m | 96.6 | 95.0 | 84.7 |
The Nano variant runs at 8–12 ms/frame on GPU; switch to yolov8s for higher accuracy (Hard 83.1%, ~50% slower).
| Package | Detector | Backbone | Size | LFW (%) |
|---|---|---|---|---|
| buffalo_l (selected) | SCRFD-10GF | ResNet50 | 326 MB | 99.83 |
| buffalo_m | SCRFD-2.5GF | ResNet50 | 313 MB | 99.80 |
| buffalo_s | SCRFD-500MF | MobileFaceNet | 159 MB | 99.70 |
| antelopev2 | SCRFD-10GF | ResNet100 | 407 MB | 99.85 |
Cross-race accuracy of buffalo_l (MR-ALL): 91.25% — White 94.70%, African 90.29%, South Asian 93.16%, East Asian 74.96% (East Asian precision is a known open challenge).
The system follows a five-layer layered architecture: frontend (Jinja2 + vanilla JS) → web service (Flask 3.0, Blueprint) → inference pipeline (detector/recognizer/matcher singleton) → DL frameworks (PyTorch 2.3 + ONNX Runtime CUDA) → infrastructure (SQLite WAL + filesystem + logging).
- Small faces (< 30×30 px, e.g. back-row students) hurt Nano recall — combine with person-counting models for a head-up-rate metric
- Matching cost grows linearly with registered students (M=40→400 is 10×); large-scale deployments need approximate nearest-neighbor search
- Static images only — real-time video attendance not yet supported
- Robustness to extreme backlighting, side shadows, masks and head-down postures needs targeted handling
CUDA out of memory?
# 1. Enable FP16
USE_FP16 = True
# 2. Lower inference resolution
DET_IMAGE_SIZE = 320
# 3. Fall back to CPU
DEVICE = 'cpu' # or: python run.py --no-gpuModel loading on CPU (GPU unavailable)?
python -c "import torch; print(torch.version.cuda, torch.__version__)"
pip uninstall onnxruntime onnxruntime-gpu && pip install onnxruntime-gpu==1.18.0Poor recognition accuracy?
- Same person → Unknown: lower
MATCH_THRESHOLDto 0.40 - Different people confused: raise
MATCH_THRESHOLDto 0.55 - Poor enrollment photo (profile/backlit/blurry): retake a clear frontal photo
Missing model weights?
python scripts/download_weights.pyInsightFace trying to download online?
config.py forces local paths via INSIGHTFACE_HOME, so it never touches the network.
For privacy reasons, this repository ships code only. The following are not included:
weights/— model weights (downloaded viascripts/download_weights.py)data/— enrolled face photos and uploaded classroom images (real personal data)database/attendance.db— runtime database with real attendance recordslogs/— runtime logs- Course reports (PDF/DOCX)
MIT © 2026 Ke Yang (杨珂)
