A modernized rebuild of the "Parallel Computer For Face Recognition Using Artificial Intelligence" capstone (IEEE, 14th ICCES, Bronze Medal) — same premise (recognize household members vs. intruders in real time), rebuilt with 2026-era tools:
- VGGFace → InsightFace (SCRFD detection + ArcFace embeddings)
- TensorFlow core / PyMP → ONNX Runtime, with the actual parallelism question moved from "how many CPU cores" to "CPU vs GPU vs quantized model"
- tkinter → a FastAPI dashboard you view in a browser
- Ad hoc benchmarking → a real eval harness (ROC/EER, not just wall-clock)
- Phone camera over WiFi, same as the original — via an IP-camera app (Option A below), no custom streaming code required
Everything runs locally — face detection/recognition happens on your own machine, no cloud AI APIs involved anywhere in the pipeline.
All commands below assume you're in the project root (safehome-v2/) — the
gallery/eval paths in config.yaml are resolved relative to wherever you run
the command from.
python3 -m venv venv
source venv/bin/activate # venv\Scripts\activate on Windows
pip install -r requirements.txtThe first time you run anything, insightface will download its buffalo_l
model pack (detection + recognition models) from its own hosted storage —
this is the one point where the app needs internet access. After that first
download it's cached locally (~/.insightface/models/) and everything runs
offline.
If you have an NVIDIA GPU and want to use it, also install onnxruntime-gpu
instead of the CPU-only onnxruntime, and set
recognition.providers: ["CUDAExecutionProvider", "CPUExecutionProvider"]
in config.yaml. CPU-only INT8 inference is genuinely fine for this use
case — see the note in the benchmarks section.
Install an IP-camera app on your phone and make sure phone + PC are on the same WiFi network (this is entirely local — no internet needed for the stream itself):
- Android: IP Webcam
— open the app, tap "Start server", note the URL it shows (something like
http://192.168.1.50:8080). - iOS: any "IP camera" app that exposes an MJPEG or RTSP URL works the
same way (e.g. an app that shows a
rtsp://orhttp://.../videoURL on its home screen).
Put that URL in config.yaml:
camera:
source: "http://192.168.1.50:8080/video" # your phone's stream URLTo use your laptop's built-in webcam instead (for development, before you
bother with the phone at all), set source: 0.
python scripts/enroll.py --name "Dipen" --webcam 0
# or, from an existing photo:
python scripts/enroll.py --name "Dipen" --image path/to/photo.jpgEnroll each person 2–4 times from slightly different angles/lighting — the matcher checks against every stored embedding for a person and takes the best score, so a bit of variety makes it more robust than one photo.
python scripts/run_demo.pyThen open http://localhost:8000 (or http://<pc-ip>:8000 from another
device on the LAN) for the live dashboard.
Don't eyeball this. Put a handful of photos per person under data/eval/<name>/
(reuse enrollment photos plus a few more) and run:
python benchmarks/eval_harness.py --eval-dir data/evalThis builds every same-person pair (positive) and cross-person pair
(negative) from those photos, computes an ROC curve and equal-error-rate,
and suggests a match_threshold value to put in config.yaml. Whether you
actually want the EER threshold or something stricter/looser depends on
whether a missed family member or a false "known" match is more costly for
your use case — the script gives you the FAR/FRR table so you can decide,
it doesn't decide for you.
python benchmarks/latency_bench.py --images data/eval/<any-person> --runs 20Reports mean/p95 detect+embed latency per available ONNX Runtime provider on
your machine. If you want the quantized-vs-full-precision comparison too,
see server/models/export_quantize.py — it's a manual, documented step
rather than a one-click toggle, since swapping a quantized model back into
InsightFace's bundled pipeline means loading it as a raw ONNX Runtime
session rather than through FaceAnalysis.
server/
camera_stream.py — threaded reader, keeps only the latest frame
face_engine.py — wraps InsightFace (SCRFD + ArcFace)
gallery.py — enrolled-face storage (embeddings on disk)
matcher.py — cosine similarity matching
pipeline.py — detect → embed → match → decide, in a background thread
notifier.py — local desktop notification, cooldown per person
main.py — FastAPI app: MJPEG stream, events, dashboard
dashboard/index.html — live view + recent events
benchmarks/
latency_bench.py — per-provider latency
eval_harness.py — ROC/EER, suggests a match threshold
scripts/
enroll.py — CLI face enrollment
run_demo.py — start the server
The original paper's core finding was that recognition time scaled with CPU
cores only up to a point (~6 cores), after which a fixed serial portion —
model initialization — dominated, per Amdahl's law. That bottleneck hasn't
gone away; it's just moved. The equivalent question now is: does moving
inference to a GPU or a quantized model actually reduce the fixed per-frame
cost, or does something else (frame capture, JPEG decode, network I/O from
the phone) become the new floor? latency_bench.py is set up so you can
actually answer that with numbers instead of a Task Manager screenshot.