Metadata-Version: 2.4
Name: face-liveness-check
Version: 0.1.0
Summary: Composable face liveness and identity-verification orchestration for Python.
Project-URL: Homepage, https://github.com/AbdulazeezAde/face-liveness-check
Project-URL: Repository, https://github.com/AbdulazeezAde/face-liveness-check
Author: Face Liveness Check contributors
License: MIT
License-File: LICENSE
Keywords: anti-spoofing,biometrics,face-verification,liveness
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Requires-Dist: platformdirs>=4.0
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: twine>=6.0; extra == 'dev'
Provides-Extra: documents
Requires-Dist: pymupdf>=1.24; extra == 'documents'
Provides-Extra: evidence
Requires-Dist: boto3>=1.34; extra == 'evidence'
Requires-Dist: cryptography>=42; extra == 'evidence'
Provides-Extra: evidence-local
Requires-Dist: cryptography>=42; extra == 'evidence-local'
Provides-Extra: evidence-s3
Requires-Dist: boto3>=1.34; extra == 'evidence-s3'
Provides-Extra: full
Requires-Dist: mediapipe>=0.10; extra == 'full'
Requires-Dist: onnxruntime>=1.17; extra == 'full'
Requires-Dist: opencv-python>=4.8; extra == 'full'
Requires-Dist: pymupdf>=1.24; extra == 'full'
Provides-Extra: mediapipe
Requires-Dist: mediapipe>=0.10; extra == 'mediapipe'
Provides-Extra: onnx
Requires-Dist: onnxruntime>=1.17; extra == 'onnx'
Requires-Dist: opencv-python-headless>=4.8; extra == 'onnx'
Provides-Extra: opencv
Requires-Dist: opencv-python>=4.8; extra == 'opencv'
Provides-Extra: opencv-headless
Requires-Dist: opencv-python-headless>=4.8; extra == 'opencv-headless'
Description-Content-Type: text/markdown

# Face Liveness Check

`face-liveness-check` is a model-agnostic Python package for orchestrating face verification with active liveness challenges, passive anti-spoof scores, video-integrity checks, and embedding comparison.

> This project provides decision orchestration, not a guarantee that a person is live. Use a validated presentation-attack-detection (PAD) model, calibrate thresholds on representative data, and provide a safe fallback path for people who cannot complete a motion challenge.

## What it does

- Generates a cryptographically shuffled challenge sequence: blink, turn left, turn right, or nod.
- Verifies that activity occurs in the requested order.
- Requires stable single-face tracking, acceptable lighting/quality, passive anti-spoof scores, and non-duplicated video frames.
- Compares normalized face embeddings with cosine similarity only after liveness succeeds.
- Leaves model selection to the application: use licensed ONNX detector, landmark, embedding, and PAD models appropriate for your deployment.

## Install

```bash
pip install face-liveness-check
# Full default stack, webcam support, and PDF ID input
pip install 'face-liveness-check[full]'
```

## Quick start

Supply licensed detector, landmark, embedding, and PAD adapters, or use the
checksum-pinned default stack described below. `OnnxArcFaceEmbedder` and
`OnnxPassiveAntiSpoof` are ready for compatible ONNX models; detector and
landmark adapters remain application-owned where output contracts vary by model.

```python
from face_liveness_check import LivenessPolicy, LivenessVerifier
from face_liveness_check.pipeline import FrameEvidenceBuilder, ReferenceExtractor

reference_extractor = ReferenceExtractor(detector, embedder, aligner)
evidence_builder = FrameEvidenceBuilder(
    detector, embedder, pad_model,
    aligner=aligner,
    landmarks=landmark_model,
    activity=activity_detector,
)
verifier = LivenessVerifier(reference_extractor, evidence_builder, LivenessPolicy())

run = verifier.verify(id_portrait_bgr, live_frames())
print(run.challenges)  # display these randomized prompts to the person
print(run.result.matched, run.result.liveness.passed, run.result.similarity)
```

`live_frames()` yields `(timestamp_seconds, BGR_frame)` tuples, such as those
from `face_liveness_check.video.iter_frames(0)`. Use a short-lived session and
reject missing PAD evidence, multiple faces, subject switches, low quality, and
duplicate-frame replays.

## Model contracts

- **Detector:** returns `FaceDetection` objects. It must detect every face, not
  only the largest one.
- **Aligner:** aligns a detected face before embedding. ArcFace needs aligned
  faces; use an adapter matching your detector's landmark convention.
- **Landmark estimator:** returns normalized Face Mesh-compatible landmarks, or
  configure `LandmarkIndices` for another landmark model.
- **PAD model:** returns a bona-fide probability or logits. Set its live-class
  index explicitly and calibrate the threshold before deployment.

## Default model pack

Weights are not bundled into the PyPI wheel. The supported `opencv-default` pack
downloads four separately licensed models once to the operating-system cache
(normally `~/.cache/face-liveness-check/models/`), verifies every SHA-256 digest,
records the accepted licence notice, and reuses the files on later runs.

```python
from face_liveness_check import (
    LivenessVerifier, ModelPackManager, create_opencv_verifier_from_pack,
    default_registry,
)

manager = ModelPackManager(default_registry())
verifier = LivenessVerifier.from_model_pack(
    "opencv-default",
    manager=manager,
    factory=create_opencv_verifier_from_pack,
    download=True,
    accept_model_license=True,
)
```

Use `download=False` on a later startup to require a previously verified cache
without making a network request. The pack contains YuNet for detection, SFace
for alignment and identity embeddings, MiniFASNetV2 for passive live/print/replay
PAD evidence, and MediaPipe Face Landmarker for dense blink/nod landmarks.

`research-default` remains a smaller MiniFASNetV2-only pack for applications
providing their own detection, identity, and landmark models.

Dense landmarks are intentionally required for the active default policy; blink
and nod are not silently replaced with weak image-motion heuristics.

## PAD evaluation

Do not choose a passive PAD model from a single webcam session. The package
includes a score-only `PadEvaluator` for comparing candidates on the same
labelled samples. Its JSONL output contains only an opaque sample ID, consented
attack label, timestamp, face count, and scores—never frames, face crops,
embeddings, document paths, or identity data.

`pad-facenox-experimental` is an independently trained, Apache-2.0 binary PAD
candidate. It uses 128×128 RGB letterboxed inputs and is deliberately separate
from `opencv-default`: its reported benchmark must be reproduced on the target
cameras and attacks before it can become a default.

Evaluate genuine, print, screen-replay, and mask samples with a consistent
consented protocol. Select thresholds from the recorded genuine-accept and
attack-reject rates, not from one model's claimed accuracy.

Use the score-only webcam collector to create labelled records. The label must
describe what is physically presented to the camera: use `replay` only while a
portrait is displayed on a separate screen, or `print` for a printed portrait.

```bash
face-liveness-check evaluate-webcam --label genuine --output pad-scores.jsonl --download --accept-model-license
face-liveness-check evaluate-webcam --label replay --output pad-scores.jsonl --download --accept-model-license
```

## Command line and webcam demo

Install the model pack after reviewing its licence notice:

```bash
face-liveness-check models list
face-liveness-check models install opencv-default --accept-model-license
```

Extract the single portrait from an image or PDF identity document, then run a
short interactive camera session. The CLI prints the randomized challenge order
before capture and emits a JSON result. Press `q` to end capture early.

```bash
face-liveness-check extract id.pdf id-portrait.png
face-liveness-check webcam id-portrait.png --duration 15
```

For a first-time one-command flow, pass `--download --accept-model-license` to
`extract` or `webcam`. PDF reading is local: the tool rasterizes only the selected
page and does not upload or retain ID images.

```bash
face-liveness-check webcam id.pdf --download --accept-model-license
```

## Optional evidence storage

Evidence retention is disabled by default. To record only failed or suspicious
sessions, the integrator must explicitly enable a policy, provide a sink, and
pass explicit consent for each session. Reference images and embeddings are
never captured by this feature.

```python
from face_liveness_check import (
    EvidencePolicy, LocalEncryptedEvidenceSink, active_first_policy,
)

sink = LocalEncryptedEvidenceSink("./encrypted-evidence", key=key_from_a_secret_manager)
verifier = LivenessVerifier(
    reference_extractor,
    evidence_builder,
    active_first_policy(),
    evidence_policy=EvidencePolicy(
        enabled=True,
        capture_on={"suspicious", "failed"},
        capture_frames=True,
        capture_face_crops=True,
        max_frames=3,
        retention_days=30,
    ),
    evidence_sink=sink,
)
run = verifier.verify(id_portrait_bgr, live_frames(), evidence_consent=True)
```

Install `face-liveness-check[evidence-local]` for encrypted local files. The
local sink writes encrypted event metadata and NPY image artifacts; keep its
Fernet key in a separate secret manager and delete evidence at the recorded
retention deadline. For AWS, install `face-liveness-check[evidence-s3]` and use
`S3EvidenceSink(bucket, kms_key_id="...")`; it writes objects with SSE-KMS.
Bucket lifecycle rules, IAM access, KMS permissions, consent, and regional
biometric-data obligations remain the integrator's responsibility.

The webcam CLI exposes the same local flow without ever placing the key in shell
history. Generate and store a Fernet key in your secret manager, then expose it
to the process as an environment variable. `--pad-advisory` makes low PAD scores
record a suspicious session rather than automatically reject it.

```powershell
$env:FACE_LIVENESS_EVIDENCE_KEY = "<Fernet key from your secret manager>"
face-liveness-check webcam id-portrait.png --pad-advisory `
  --evidence-local-dir .\encrypted-evidence --evidence-consent `
  --evidence-capture-face-crops --evidence-retention-days 30
```

## Release and TestPyPI

The verified TestPyPI candidate is `0.1.0rc1`; the next production release is
`0.1.0`. CI tests Python 3.10 through 3.12, builds both distributions, and
checks their metadata. A tag such as `v0.1.0rc1` triggers TestPyPI publishing;
a published GitHub Release for `v0.1.0` triggers the production PyPI workflow.

Before publishing, create Trusted Publishers in TestPyPI and PyPI with these
exact values:

| Field | TestPyPI | PyPI |
| --- | --- | --- |
| Owner | `AbdulazeezAde` | `AbdulazeezAde` |
| Repository | `face-liveness-check` | `face-liveness-check` |
| Workflow | `release-testpypi.yml` | `release.yml` |
| Environment | `testpypi` | `pypi` |

Protect both GitHub environments with required reviewers. After the TestPyPI
workflow succeeds, install the candidate in a clean environment with:

```bash
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ face-liveness-check==0.1.0rc1
```

## Security notes

Do not use a fixed challenge order. Do not accept a single frame. Treat daylight as a quality signal—not a requirement—because indoor, low-light, and accessibility scenarios are valid. Reject or manually review multiple faces, identity switching, replay-like duplicate frames, poor quality, and missing PAD evidence.

## Development

```bash
python -m pip install -e '.[dev]'
pytest
python -m build
```
