Modern face-embedding framework for detection, alignment, embedding, and comparison.
Project description
lvface
lvface detects faces, aligns them, produces 512-dimensional LVFace embeddings, and compares
people across portraits, group photos, or whole albums. The high-level API is small, while the
detector and embedder are both replaceable.
from lvface import FaceRecognizer
recognizer = FaceRecognizer("LVFace-T_Glint360K")
result = recognizer.compare("id-photo.jpg", "selfie.jpg")
print(result.is_match)
print(f"cosine={result.cosine:.4f}, display={result.percentage:.1f}%")
Face recognition is biometric processing. Get informed consent, protect stored embeddings, define retention rules, and evaluate accuracy and bias on data representative of your users.
Why lvface?
- One pipeline for paths, image bytes, URLs, and RGB NumPy arrays.
- Every face in an image can be returned, not only the largest one.
- Multi-face search, group-photo matching, and album clustering are built in.
- Released LVFace ONNX models run through ONNX Runtime; PyTorch is not required.
- Custom detectors and embedders plug into the same
FaceRecognizer. - Named weights are revision-pinned and checksum-verified.
Install
Python 3.11 or newer is required.
# Recommended: recognition from ordinary photos + automatic weight download
python -m pip install "lvface[detect,hub]"
# Local ONNX weights and already aligned 112×112 face crops
python -m pip install lvface
# Add guarded http(s) image loading
python -m pip install "lvface[detect,hub,http]"
The [detect] extra installs the default InsightFace detector. The [hub] extra lets a
registered model name download its pinned ONNX file on first construction:
recognizer = FaceRecognizer("LVFace-T_Glint360K")
To keep weights under your own control, pass a local file. This path never accesses Hugging Face
and does not need [hub]:
recognizer = FaceRecognizer("/models/LVFace-T_Glint360K.onnx")
Model resolution and download happen while FaceRecognizer is constructed. The ONNX Runtime
session itself is still created lazily on the first embedding call.
CPU is the supported runtime for the 0.1 release. There is no [gpu] extra because
onnxruntime and onnxruntime-gpu provide the same Python package. For best-effort NVIDIA CUDA
use on Linux or Windows, install all extras first, then replace the runtime:
python -m pip uninstall -y onnxruntime
python -m pip install onnxruntime-gpu
A 60-second tour
Compare two photos
from lvface import FaceRecognizer
recognizer = FaceRecognizer(device="auto")
result = recognizer.compare("first.jpg", "second.jpg", select="largest")
if result.is_match:
print(f"Likely the same person ({result.percentage:.1f}% display score)")
percentage is a readable, threshold-centered display score. It is not a probability or a
calibrated confidence value. Use cosine and a threshold calibrated for your own camera,
population, and risk tolerance to make decisions.
Get an embedding for every face
faces = recognizer.analyze("team-photo.jpg")
for face in faces:
vector = face.embedding.vector
print(face.face_index, face.bbox, vector.shape) # (512,)
analyze() performs load → detect → align → embed and returns a Face for every alignable
face. Each result carries its bounding box, five landmarks, aligned crop, and L2-normalized
embedding.
If you only need vectors:
embeddings = recognizer.embed("team-photo.jpg") # list[Embedding]
one_embedding = recognizer.embed("portrait.jpg", select="largest")
Store face embeddings with FAISS
The FAISS example stores every detected face in a local cosine-similarity index. A small JSON file keeps the image and face index associated with each vector:
python -m pip install faiss-cpu
python examples/embed_and_store.py
Edit the IMAGE constant at the top of the script before running it. The index uses cosine
similarity, matching lvface's canonical comparison metric. The script writes faces.index and
faces.json. For production, treat both files as biometric data: restrict access, encrypt
backups, and delete vectors when their source data must be removed.
To search the saved index, edit QUERY_IMAGE in the companion example:
python examples/search_faiss.py
It embeds the largest face in the query photo, loads faces.index, and prints the nearest stored
faces with their cosine similarity. Use the same LVFace model for indexing and searching.
Find someone in a group photo
hits = recognizer.find(
"person-to-find.jpg",
"group-photo.jpg",
top_k=3,
)
for hit in hits:
print(hit.candidate.face_index, hit.percentage, hit.candidate.bbox)
Match two group photos
result = recognizer.match("group-before.jpg", "group-after.jpg")
for pair in result.pairs:
print(
pair.query.face_index,
"↔",
pair.candidate.face_index,
f"{pair.percentage:.1f}%",
)
The default greedy assignment uses each face at most once. Install lvface[hungarian] and pass
assignment="hungarian" for globally optimal one-to-one assignment.
Group an album by identity
identities = recognizer.group(["day-1.jpg", "day-2.jpg", "day-3.jpg"])
for identity in identities:
print([(face.image_index, face.face_index) for face in identity])
Clustering is conservative: every member must meet the threshold against every other member, and
one identity cannot contain two faces from the same image unless one_per_image=False.
API at a glance
| Call | Result |
|---|---|
analyze(image) |
Every detected face, aligned crop, and embedding |
embed(image) |
Embeddings for every face |
embed(image, select="largest") |
One explicitly selected embedding |
embed_aligned(crop) |
Embed one pre-aligned 112×112 RGB crop |
compare(a, b) |
Cosine, Euclidean distance, display score, and decision |
verify(a, b) |
Boolean match decision |
find(query, gallery) |
One-to-many ranked face search |
match(a, b) |
Full many-to-many matrix and assigned pairs |
group(images) |
Conservative identity clusters across images |
Accepted image inputs are a path, http(s) URL with [http], encoded bytes, or an RGB
uint8 NumPy array. NumPy arrays are assumed to be RGB, not OpenCV BGR.
Bring your own detector
A detector only needs to subclass FaceDetector and provide lazy load() plus detect().
Each detected Face should contain a bounding box and five ArcFace-order landmarks. The base
class supplies the 112×112 alignment implementation.
detector = MyDetector(...)
recognizer = FaceRecognizer(
embedder="LVFace-T_Glint360K",
detector=detector,
)
faces = recognizer.analyze("photo.jpg")
examples/custom_detector.py is a complete OpenCV YuNet adapter
and shows the important part explicitly: the custom detector instance is passed into
FaceRecognizer, so its detections flow through alignment and LVFace embedding.
Change the detector model and image paths at the bottom of the example, then run it:
python examples/custom_detector.py
Custom embedding backends follow the same pattern: subclass FaceEmbedder, lazily initialize the
runtime in load(), and implement _forward(batch) to return an (N, 512) floating-point
array. The base class validates 112×112 RGB inputs, preprocesses them, batches inference, and
returns validated Embedding objects.
Concepts that matter
Embedding. A 512-number representation of an aligned face. Embeddings returned by the public API are L2-normalized.
Cosine similarity. The decision metric. Higher means more similar. The packaged 0.35
default is a provisional starting point, not a domain-general operating threshold.
Euclidean distance. A diagnostic value. For normalized vectors,
euclidean² = 2 - 2 × cosine.
Alignment. Five facial landmarks are warped onto the ArcFace 112×112 template before embedding. Good detection and alignment are part of recognition quality, not merely preprocessing details.
Display percentage. A sigmoid mapping centered on the decision threshold. It is for UI display only and must not be presented as probability, certainty, or an estimated false-match rate.
Runnable examples
Each example is intentionally a small, direct Python script. Open one, replace the sample image paths, and run it:
python examples/verify_two_faces.py
python examples/embed_and_store.py
python examples/search_faiss.py
python examples/find_in_group.py
python examples/match_two_group_photos.py
python examples/cluster_album.py
python examples/custom_detector.py
From a source checkout:
python -m pip install -e ".[detect,hub]"
Weights, licenses, and citation
The package code is MIT licensed.
The default InsightFace model packs, including buffalo_l, are separately licensed for
non-commercial research use. Applications requiring other terms should supply a detector with
appropriate weights or pass pre-aligned crops with detector=None.
LVFace embedding-weight licensing is unresolved. The official repository metadata declares MIT,
while its model-card prose restricts downloaded models to non-commercial research. The
unofficial
Mowshon/lvface-weights preservation mirror
grants no additional rights. lvface pins mirror revision
83b567cd6a3fc34434667e4415b6125feceb39ea; the mirror records unchanged files from official
bytedance-research/LVFace revision
b12702ab1f5c721748e054a66dc90e1edd1f0724. Review the official model card and seek
clarification from the authors when necessary.
Use of the weights requires citation of the original work:
@inproceedings{you2025lvface,
title={{LVFace}: Progressive Cluster Optimization for Large Vision Models in Face Recognition},
author={You, Jinghan and Li, Shanglin and Sun, Yuanrui and Wei, Jiangchuan and Guo, Mingyu and Feng, Chao and Ran, Jiao},
booktitle={ICCV},
year={2025}
}
Development
python -m pip install -e ".[dev]"
ruff check .
ruff format --check .
mypy src
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lvface-0.1.0.tar.gz.
File metadata
- Download URL: lvface-0.1.0.tar.gz
- Upload date:
- Size: 34.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71bc1a56d1a37c2ce19f120078961050b583a4a2042adb884921977cf515029b
|
|
| MD5 |
b768ea7bd6d3dbbf8639b6faa3f9b466
|
|
| BLAKE2b-256 |
90a7fbfa4c323330b50d488ea1913dc7f54cb9f3ab10f787b9a79a8570daa5cb
|
Provenance
The following attestation bundles were made for lvface-0.1.0.tar.gz:
Publisher:
publish.yml on mowshon/lvface
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lvface-0.1.0.tar.gz -
Subject digest:
71bc1a56d1a37c2ce19f120078961050b583a4a2042adb884921977cf515029b - Sigstore transparency entry: 1926930881
- Sigstore integration time:
-
Permalink:
mowshon/lvface@1742f56b061987025c27a21e23f4699652964bde -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mowshon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1742f56b061987025c27a21e23f4699652964bde -
Trigger Event:
push
-
Statement type:
File details
Details for the file lvface-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lvface-0.1.0-py3-none-any.whl
- Upload date:
- Size: 35.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37131cea8f3654d8f54b609cff0cc03f0f19204412e7e904d442c0b35a45494f
|
|
| MD5 |
5547b932c9ad0aa36b0548fd7c303053
|
|
| BLAKE2b-256 |
876d34d898a9a411f4bd03393adc95e062c2ccd85dd3bd110df6479044884dce
|
Provenance
The following attestation bundles were made for lvface-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on mowshon/lvface
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lvface-0.1.0-py3-none-any.whl -
Subject digest:
37131cea8f3654d8f54b609cff0cc03f0f19204412e7e904d442c0b35a45494f - Sigstore transparency entry: 1926931316
- Sigstore integration time:
-
Permalink:
mowshon/lvface@1742f56b061987025c27a21e23f4699652964bde -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mowshon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1742f56b061987025c27a21e23f4699652964bde -
Trigger Event:
push
-
Statement type: