Skip to content

danilkiff/py-vision

Repository files navigation

py-vision

Two-stage face recognition library: detection (SCRFD) + identification (ArcFace).

Offline inference via ONNX Runtime, no cloud required.

Requirements

  • Python 3.12+
  • uv
  • (optional) NVIDIA drivers + CUDA 12.x for GPU inference

Setup

uv sync --extra dev
make setup          # configure git hooks

CUDA (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 cuda

Note: the NVIDIA driver alone is not enough. ONNX Runtime requires libcublasLt.so.12 and libcudnn*.so.9 at runtime. Without them CUDAExecutionProvider silently falls back to CPU — benchmarks will show identical latency for [cuda] and [cpu] params with no error.

Usage

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))

Liveness detection (optional)

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 passive

Or in code:

pipeline = FacePipeline(liveness=True)
for result in pipeline.recognize(frame):
    print(result.liveness_score)  # float [0, 1] or None if disabled

Camera demo

uv run main.py              # detection only
uv run main.py --help       # all options

Controls

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

Enrollment workflow

  1. Run --enroll <name> and point the camera at the person
  2. Press s (or a digit) to capture 3-5 photos from different angles
  3. Press q — the database reindexes automatically
  4. Run --recognize to verify

Face database

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.

API

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

Benchmarks

# 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=0001

Benchmarks are disabled during normal pytest runs. See ADR-0013 for design decisions.

Benchmark report (optional)

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 comparison

Report JSONs are committed to the repo under .benchmarks/ so the notebooks can be rendered without re-running the benchmarks.

Development

make test           # run tests
uv run ruff check . # lint
uv run ruff format . # format

Acknowledgements

This 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:

License

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.

About

Real-time face detection and identification library built on InsightFace and ONNX

Resources

License

Stars

Watchers

Forks

Contributors

Languages