Two-stage face recognition library: detection (SCRFD) + identification (ArcFace).
Offline inference via ONNX Runtime, no cloud required.
- Python 3.12+
- uv
- (optional) NVIDIA drivers + CUDA 12.x for GPU inference
uv sync --extra dev
make setup # configure git hooksCUDA (Linux only):
# 1. Install CUDA toolkit + cuDNN (NVIDIA repo required)
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
sudo apt install cuda-toolkit-12-8 libcudnn9-cuda-12
# 2. Install onnxruntime-gpu
uv sync --extra dev --extra cudaNote: the NVIDIA driver alone is not enough. ONNX Runtime requires
libcublasLt.so.12andlibcudnn*.so.9at runtime. Without themCUDAExecutionProvidersilently falls back to CPU — benchmarks will show identical latency for[cuda]and[cpu]params with no error.
from py_vision import FacePipeline
pipeline = FacePipeline()
pipeline.load_faces() # index known faces from faces_db/
import cv2
frame = cv2.imread("photo.jpg")
for result in pipeline.recognize(frame):
print(result.name, f"{result.confidence:.2f}", result.bbox)GPU:
from py_vision import FacePipeline, VisionConfig, ExecutionBackend
pipeline = FacePipeline(VisionConfig(backend=ExecutionBackend.CUDA))Anti-spoofing uses Silent-FAS ONNX
models (Apache 2.0, Minivision) bundled in py_vision/data/ — no extra download required.
See docs/silent-fas.md for license details and conversion process.
Use with --liveness passive:
uv run main.py --recognize --liveness passiveOr in code:
pipeline = FacePipeline(liveness=True)
for result in pipeline.recognize(frame):
print(result.liveness_score) # float [0, 1] or None if disableduv run main.py # detection only
uv run main.py --help # all options| Key | Mode | Action |
|---|---|---|
q |
any | quit |
s |
--enroll |
save face (single face on screen) |
1-9 |
--enroll |
save numbered face (multiple faces) |
e |
--recognize |
quick-enroll an __unknown__ face |
- Run
--enroll <name>and point the camera at the person - Press
s(or a digit) to capture 3-5 photos from different angles - Press
q— the database reindexes automatically - Run
--recognizeto verify
Place photos in the faces_db/ directory, one subdirectory per person:
faces_db/
alice/
photo1.jpg
photo2.png
bob/
bob.jpg
Multiple photos per person improve recognition across different poses. Images with zero faces or multiple faces are silently skipped (a warning is logged) — only images containing exactly one face are indexed.
| Class | Purpose |
|---|---|
FacePipeline |
Main entry point: detect(), recognize(), load_faces() |
FaceDetector |
SCRFD face detection, returns bboxes + 5-point landmarks |
FaceRecognizer |
ArcFace embedding extraction, 512-d L2-normalised vectors |
FaceDatabase |
Filesystem-based vector index with cosine similarity matching |
FaceLivenessChecker |
Passive anti-spoofing via Silent-FAS (single-frame) |
LivenessSession |
Active liveness via head-pose variation (multi-frame) |
VisionConfig |
Backend selection (CPU / CUDA / TensorRT / CoreML), thresholds |
# all benchmarks
uv run pytest benchmarks/ --benchmark-enable -v
# single layer
uv run pytest benchmarks/bench_preprocess.py --benchmark-enable -v
uv run pytest benchmarks/bench_inference.py --benchmark-enable -v
uv run pytest benchmarks/bench_db.py --benchmark-enable -v
uv run pytest benchmarks/bench_pipeline.py --benchmark-enable -v
# save baseline and compare after changes
uv run pytest benchmarks/ --benchmark-enable --benchmark-save=baseline
uv run pytest benchmarks/ --benchmark-enable --benchmark-compare=0001Benchmarks are disabled during normal pytest runs. See ADR-0013 for design decisions.
Generate a JSON report then open a notebook to visualise:
uv sync --extra report # one-time: install pandas/seaborn/ipykernel
make bench-report # writes .benchmarks/benchmark_report_<machine>.json
jupyter notebook notebooks/benchmark_report.ipynb # single-machine report
jupyter notebook notebooks/benchmark_comparison.ipynb # cross-machine comparisonReport JSONs are committed to the repo under .benchmarks/ so the notebooks
can be rendered without re-running the benchmarks.
make test # run tests
uv run ruff check . # lint
uv run ruff format . # formatThis project is built on top of excellent open-source work:
| Project | Role | License |
|---|---|---|
| InsightFace | SCRFD detection + ArcFace recognition models (buffalo_l) | MIT |
| ONNX Runtime | Cross-platform inference engine | MIT |
| OpenCV | Image processing and affine transforms | Apache 2.0 |
| NumPy | Array operations and vector math | BSD 3-Clause |
| Pillow | Image loading with EXIF support | MIT-CMU |
| Silent-Face-Anti-Spoofing | Passive liveness models (bundled ONNX) | Apache 2.0 |
| FAISS | Optional vector similarity search | MIT |
Key papers:
- Guo et al., Sample and Computation Redistribution for Efficient Face Detection (ICLR 2022) — SCRFD
- Deng et al., ArcFace: Additive Angular Margin Loss for Deep Face Recognition (CVPR 2019) — ArcFace
Unlicense — public domain.
Bundled Silent-FAS models in py_vision/data/ are licensed under Apache 2.0 by Minivision Technology;
see docs/silent-fas.md for details.