lvface 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lvface-0.1.0/.gitignore +218 -0
- lvface-0.1.0/CHANGELOG.md +13 -0
- lvface-0.1.0/CONTRIBUTING.md +16 -0
- lvface-0.1.0/LICENSE +21 -0
- lvface-0.1.0/PKG-INFO +357 -0
- lvface-0.1.0/README.md +299 -0
- lvface-0.1.0/docs/api.md +31 -0
- lvface-0.1.0/docs/contributing.md +7 -0
- lvface-0.1.0/docs/index.md +11 -0
- lvface-0.1.0/examples/cluster_album.py +17 -0
- lvface-0.1.0/examples/custom_detector.py +93 -0
- lvface-0.1.0/examples/embed_and_store.py +48 -0
- lvface-0.1.0/examples/find_in_group.py +19 -0
- lvface-0.1.0/examples/match_two_group_photos.py +14 -0
- lvface-0.1.0/examples/search_faiss.py +41 -0
- lvface-0.1.0/examples/verify_two_faces.py +11 -0
- lvface-0.1.0/mkdocs.yml +18 -0
- lvface-0.1.0/pyproject.toml +124 -0
- lvface-0.1.0/src/lvface/__init__.py +37 -0
- lvface-0.1.0/src/lvface/detect/__init__.py +16 -0
- lvface-0.1.0/src/lvface/detect/align.py +196 -0
- lvface-0.1.0/src/lvface/detect/base.py +71 -0
- lvface-0.1.0/src/lvface/detect/insightface.py +191 -0
- lvface-0.1.0/src/lvface/embed/__init__.py +6 -0
- lvface-0.1.0/src/lvface/embed/base.py +126 -0
- lvface-0.1.0/src/lvface/embed/onnx.py +182 -0
- lvface-0.1.0/src/lvface/errors.py +9 -0
- lvface-0.1.0/src/lvface/hub.py +126 -0
- lvface-0.1.0/src/lvface/io.py +327 -0
- lvface-0.1.0/src/lvface/metrics.py +283 -0
- lvface-0.1.0/src/lvface/py.typed +1 -0
- lvface-0.1.0/src/lvface/recognizer.py +732 -0
- lvface-0.1.0/src/lvface/registry.py +209 -0
- lvface-0.1.0/src/lvface/types.py +193 -0
lvface-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial ONNX-only LVFace embedding API for Python 3.11–3.13.
|
|
6
|
+
- Pluggable face detector and embedder adapters.
|
|
7
|
+
- Face comparison, search, group matching, and conservative identity clustering.
|
|
8
|
+
- Revision-pinned, checksum-validated optional model downloads.
|
|
9
|
+
|
|
10
|
+
CPU inference is the supported runtime for this release. The default cosine threshold is
|
|
11
|
+
provisional and must be calibrated for each deployment. LVFace embedding-weight licensing is
|
|
12
|
+
unresolved because the official metadata and model-card prose conflict; the default InsightFace
|
|
13
|
+
detector weights are separately restricted to non-commercial research use.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Install the development dependencies and run the complete local checks:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m pip install -e ".[dev]"
|
|
7
|
+
ruff check .
|
|
8
|
+
ruff format --check .
|
|
9
|
+
mypy src
|
|
10
|
+
pytest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Changes to preprocessing, alignment, model resolution, or ONNX inference must keep the frozen
|
|
14
|
+
golden-embedding tests green. Do not commit model weights, face-photo datasets, caches, or
|
|
15
|
+
generated build artifacts. New image fixtures must be synthetic or openly licensed and have
|
|
16
|
+
their source, license, and attribution recorded in `tests/data/FIXTURES.md`.
|
lvface-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
lvface-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lvface
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Modern face-embedding framework for detection, alignment, embedding, and comparison.
|
|
5
|
+
Project-URL: Homepage, https://github.com/mowshon/lvface
|
|
6
|
+
Project-URL: Documentation, https://github.com/mowshon/lvface#readme
|
|
7
|
+
Project-URL: Issues, https://github.com/mowshon/lvface/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/mowshon/lvface
|
|
9
|
+
Author: Mowshon
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: arcface,face-embedding,face-recognition,lvface,onnx
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: numpy>=2.4
|
|
26
|
+
Requires-Dist: onnxruntime>=1.23.2
|
|
27
|
+
Requires-Dist: pillow>=12.2
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: huggingface-hub>=1.20; extra == 'all'
|
|
30
|
+
Requires-Dist: insightface>=1.0.1; extra == 'all'
|
|
31
|
+
Requires-Dist: requests>=2.32; extra == 'all'
|
|
32
|
+
Requires-Dist: scikit-learn>=1.7; extra == 'all'
|
|
33
|
+
Requires-Dist: scipy>=1.15; extra == 'all'
|
|
34
|
+
Provides-Extra: cluster
|
|
35
|
+
Requires-Dist: scikit-learn>=1.7; extra == 'cluster'
|
|
36
|
+
Provides-Extra: detect
|
|
37
|
+
Requires-Dist: insightface>=1.0.1; extra == 'detect'
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: mypy>=2.1; extra == 'dev'
|
|
40
|
+
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest-cov>=7.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest>=9.1; extra == 'dev'
|
|
43
|
+
Requires-Dist: requests>=2.32; extra == 'dev'
|
|
44
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
45
|
+
Provides-Extra: docs
|
|
46
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
47
|
+
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'docs'
|
|
48
|
+
Provides-Extra: http
|
|
49
|
+
Requires-Dist: requests>=2.32; extra == 'http'
|
|
50
|
+
Provides-Extra: hub
|
|
51
|
+
Requires-Dist: huggingface-hub>=1.20; extra == 'hub'
|
|
52
|
+
Provides-Extra: hungarian
|
|
53
|
+
Requires-Dist: scipy>=1.15; extra == 'hungarian'
|
|
54
|
+
Provides-Extra: release
|
|
55
|
+
Requires-Dist: build>=1.2; extra == 'release'
|
|
56
|
+
Requires-Dist: twine>=5.1; extra == 'release'
|
|
57
|
+
Description-Content-Type: text/markdown
|
|
58
|
+
|
|
59
|
+
# lvface
|
|
60
|
+
|
|
61
|
+
`lvface` detects faces, aligns them, produces 512-dimensional LVFace embeddings, and compares
|
|
62
|
+
people across portraits, group photos, or whole albums. The high-level API is small, while the
|
|
63
|
+
detector and embedder are both replaceable.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from lvface import FaceRecognizer
|
|
67
|
+
|
|
68
|
+
recognizer = FaceRecognizer("LVFace-T_Glint360K")
|
|
69
|
+
result = recognizer.compare("id-photo.jpg", "selfie.jpg")
|
|
70
|
+
|
|
71
|
+
print(result.is_match)
|
|
72
|
+
print(f"cosine={result.cosine:.4f}, display={result.percentage:.1f}%")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> Face recognition is biometric processing. Get informed consent, protect stored embeddings,
|
|
76
|
+
> define retention rules, and evaluate accuracy and bias on data representative of your users.
|
|
77
|
+
|
|
78
|
+
## Why lvface?
|
|
79
|
+
|
|
80
|
+
- One pipeline for paths, image bytes, URLs, and RGB NumPy arrays.
|
|
81
|
+
- Every face in an image can be returned, not only the largest one.
|
|
82
|
+
- Multi-face search, group-photo matching, and album clustering are built in.
|
|
83
|
+
- Released LVFace ONNX models run through ONNX Runtime; PyTorch is not required.
|
|
84
|
+
- Custom detectors and embedders plug into the same `FaceRecognizer`.
|
|
85
|
+
- Named weights are revision-pinned and checksum-verified.
|
|
86
|
+
|
|
87
|
+
## Install
|
|
88
|
+
|
|
89
|
+
Python 3.11 or newer is required.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Recommended: recognition from ordinary photos + automatic weight download
|
|
93
|
+
python -m pip install "lvface[detect,hub]"
|
|
94
|
+
|
|
95
|
+
# Local ONNX weights and already aligned 112×112 face crops
|
|
96
|
+
python -m pip install lvface
|
|
97
|
+
|
|
98
|
+
# Add guarded http(s) image loading
|
|
99
|
+
python -m pip install "lvface[detect,hub,http]"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The `[detect]` extra installs the default InsightFace detector. The `[hub]` extra lets a
|
|
103
|
+
registered model name download its pinned ONNX file on first construction:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
recognizer = FaceRecognizer("LVFace-T_Glint360K")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
To keep weights under your own control, pass a local file. This path never accesses Hugging Face
|
|
110
|
+
and does not need `[hub]`:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
recognizer = FaceRecognizer("/models/LVFace-T_Glint360K.onnx")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Model resolution and download happen while `FaceRecognizer` is constructed. The ONNX Runtime
|
|
117
|
+
session itself is still created lazily on the first embedding call.
|
|
118
|
+
|
|
119
|
+
CPU is the supported runtime for the 0.1 release. There is no `[gpu]` extra because
|
|
120
|
+
`onnxruntime` and `onnxruntime-gpu` provide the same Python package. For best-effort NVIDIA CUDA
|
|
121
|
+
use on Linux or Windows, install all extras first, then replace the runtime:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
python -m pip uninstall -y onnxruntime
|
|
125
|
+
python -m pip install onnxruntime-gpu
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## A 60-second tour
|
|
129
|
+
|
|
130
|
+
### Compare two photos
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from lvface import FaceRecognizer
|
|
134
|
+
|
|
135
|
+
recognizer = FaceRecognizer(device="auto")
|
|
136
|
+
result = recognizer.compare("first.jpg", "second.jpg", select="largest")
|
|
137
|
+
|
|
138
|
+
if result.is_match:
|
|
139
|
+
print(f"Likely the same person ({result.percentage:.1f}% display score)")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`percentage` is a readable, threshold-centered display score. It is not a probability or a
|
|
143
|
+
calibrated confidence value. Use `cosine` and a threshold calibrated for your own camera,
|
|
144
|
+
population, and risk tolerance to make decisions.
|
|
145
|
+
|
|
146
|
+
### Get an embedding for every face
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
faces = recognizer.analyze("team-photo.jpg")
|
|
150
|
+
|
|
151
|
+
for face in faces:
|
|
152
|
+
vector = face.embedding.vector
|
|
153
|
+
print(face.face_index, face.bbox, vector.shape) # (512,)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`analyze()` performs load → detect → align → embed and returns a `Face` for every alignable
|
|
157
|
+
face. Each result carries its bounding box, five landmarks, aligned crop, and L2-normalized
|
|
158
|
+
embedding.
|
|
159
|
+
|
|
160
|
+
If you only need vectors:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
embeddings = recognizer.embed("team-photo.jpg") # list[Embedding]
|
|
164
|
+
one_embedding = recognizer.embed("portrait.jpg", select="largest")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Store face embeddings with FAISS
|
|
168
|
+
|
|
169
|
+
The FAISS example stores every detected face in a local cosine-similarity index. A small JSON
|
|
170
|
+
file keeps the image and face index associated with each vector:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
python -m pip install faiss-cpu
|
|
174
|
+
python examples/embed_and_store.py
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Edit the `IMAGE` constant at the top of the script before running it. The index uses cosine
|
|
178
|
+
similarity, matching `lvface`'s canonical comparison metric. The script writes `faces.index` and
|
|
179
|
+
`faces.json`. For production, treat both files as biometric data: restrict access, encrypt
|
|
180
|
+
backups, and delete vectors when their source data must be removed.
|
|
181
|
+
|
|
182
|
+
To search the saved index, edit `QUERY_IMAGE` in the companion example:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
python examples/search_faiss.py
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
It embeds the largest face in the query photo, loads `faces.index`, and prints the nearest stored
|
|
189
|
+
faces with their cosine similarity. Use the same LVFace model for indexing and searching.
|
|
190
|
+
|
|
191
|
+
### Find someone in a group photo
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
hits = recognizer.find(
|
|
195
|
+
"person-to-find.jpg",
|
|
196
|
+
"group-photo.jpg",
|
|
197
|
+
top_k=3,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
for hit in hits:
|
|
201
|
+
print(hit.candidate.face_index, hit.percentage, hit.candidate.bbox)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Match two group photos
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
result = recognizer.match("group-before.jpg", "group-after.jpg")
|
|
208
|
+
|
|
209
|
+
for pair in result.pairs:
|
|
210
|
+
print(
|
|
211
|
+
pair.query.face_index,
|
|
212
|
+
"↔",
|
|
213
|
+
pair.candidate.face_index,
|
|
214
|
+
f"{pair.percentage:.1f}%",
|
|
215
|
+
)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The default greedy assignment uses each face at most once. Install `lvface[hungarian]` and pass
|
|
219
|
+
`assignment="hungarian"` for globally optimal one-to-one assignment.
|
|
220
|
+
|
|
221
|
+
### Group an album by identity
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
identities = recognizer.group(["day-1.jpg", "day-2.jpg", "day-3.jpg"])
|
|
225
|
+
|
|
226
|
+
for identity in identities:
|
|
227
|
+
print([(face.image_index, face.face_index) for face in identity])
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Clustering is conservative: every member must meet the threshold against every other member, and
|
|
231
|
+
one identity cannot contain two faces from the same image unless `one_per_image=False`.
|
|
232
|
+
|
|
233
|
+
## API at a glance
|
|
234
|
+
|
|
235
|
+
| Call | Result |
|
|
236
|
+
| --- | --- |
|
|
237
|
+
| `analyze(image)` | Every detected face, aligned crop, and embedding |
|
|
238
|
+
| `embed(image)` | Embeddings for every face |
|
|
239
|
+
| `embed(image, select="largest")` | One explicitly selected embedding |
|
|
240
|
+
| `embed_aligned(crop)` | Embed one pre-aligned 112×112 RGB crop |
|
|
241
|
+
| `compare(a, b)` | Cosine, Euclidean distance, display score, and decision |
|
|
242
|
+
| `verify(a, b)` | Boolean match decision |
|
|
243
|
+
| `find(query, gallery)` | One-to-many ranked face search |
|
|
244
|
+
| `match(a, b)` | Full many-to-many matrix and assigned pairs |
|
|
245
|
+
| `group(images)` | Conservative identity clusters across images |
|
|
246
|
+
|
|
247
|
+
Accepted image inputs are a path, `http(s)` URL with `[http]`, encoded bytes, or an RGB
|
|
248
|
+
`uint8` NumPy array. NumPy arrays are assumed to be RGB, not OpenCV BGR.
|
|
249
|
+
|
|
250
|
+
## Bring your own detector
|
|
251
|
+
|
|
252
|
+
A detector only needs to subclass `FaceDetector` and provide lazy `load()` plus `detect()`.
|
|
253
|
+
Each detected `Face` should contain a bounding box and five ArcFace-order landmarks. The base
|
|
254
|
+
class supplies the 112×112 alignment implementation.
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
detector = MyDetector(...)
|
|
258
|
+
recognizer = FaceRecognizer(
|
|
259
|
+
embedder="LVFace-T_Glint360K",
|
|
260
|
+
detector=detector,
|
|
261
|
+
)
|
|
262
|
+
faces = recognizer.analyze("photo.jpg")
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
[`examples/custom_detector.py`](examples/custom_detector.py) is a complete OpenCV YuNet adapter
|
|
266
|
+
and shows the important part explicitly: the custom detector instance is passed into
|
|
267
|
+
`FaceRecognizer`, so its detections flow through alignment and LVFace embedding.
|
|
268
|
+
|
|
269
|
+
Change the detector model and image paths at the bottom of the example, then run it:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
python examples/custom_detector.py
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Custom embedding backends follow the same pattern: subclass `FaceEmbedder`, lazily initialize the
|
|
276
|
+
runtime in `load()`, and implement `_forward(batch)` to return an `(N, 512)` floating-point
|
|
277
|
+
array. The base class validates 112×112 RGB inputs, preprocesses them, batches inference, and
|
|
278
|
+
returns validated `Embedding` objects.
|
|
279
|
+
|
|
280
|
+
## Concepts that matter
|
|
281
|
+
|
|
282
|
+
**Embedding.** A 512-number representation of an aligned face. Embeddings returned by the public
|
|
283
|
+
API are L2-normalized.
|
|
284
|
+
|
|
285
|
+
**Cosine similarity.** The decision metric. Higher means more similar. The packaged `0.35`
|
|
286
|
+
default is a provisional starting point, not a domain-general operating threshold.
|
|
287
|
+
|
|
288
|
+
**Euclidean distance.** A diagnostic value. For normalized vectors,
|
|
289
|
+
`euclidean² = 2 - 2 × cosine`.
|
|
290
|
+
|
|
291
|
+
**Alignment.** Five facial landmarks are warped onto the ArcFace 112×112 template before
|
|
292
|
+
embedding. Good detection and alignment are part of recognition quality, not merely
|
|
293
|
+
preprocessing details.
|
|
294
|
+
|
|
295
|
+
**Display percentage.** A sigmoid mapping centered on the decision threshold. It is for UI
|
|
296
|
+
display only and must not be presented as probability, certainty, or an estimated false-match
|
|
297
|
+
rate.
|
|
298
|
+
|
|
299
|
+
## Runnable examples
|
|
300
|
+
|
|
301
|
+
Each example is intentionally a small, direct Python script. Open one, replace the sample image
|
|
302
|
+
paths, and run it:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
python examples/verify_two_faces.py
|
|
306
|
+
python examples/embed_and_store.py
|
|
307
|
+
python examples/search_faiss.py
|
|
308
|
+
python examples/find_in_group.py
|
|
309
|
+
python examples/match_two_group_photos.py
|
|
310
|
+
python examples/cluster_album.py
|
|
311
|
+
python examples/custom_detector.py
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
From a source checkout:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
python -m pip install -e ".[detect,hub]"
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Weights, licenses, and citation
|
|
321
|
+
|
|
322
|
+
The package code is MIT licensed.
|
|
323
|
+
|
|
324
|
+
The default InsightFace model packs, including `buffalo_l`, are separately licensed for
|
|
325
|
+
non-commercial research use. Applications requiring other terms should supply a detector with
|
|
326
|
+
appropriate weights or pass pre-aligned crops with `detector=None`.
|
|
327
|
+
|
|
328
|
+
LVFace embedding-weight licensing is unresolved. The official repository metadata declares MIT,
|
|
329
|
+
while its model-card prose restricts downloaded models to non-commercial research. The
|
|
330
|
+
unofficial
|
|
331
|
+
[`Mowshon/lvface-weights`](https://huggingface.co/Mowshon/lvface-weights) preservation mirror
|
|
332
|
+
grants no additional rights. `lvface` pins mirror revision
|
|
333
|
+
`83b567cd6a3fc34434667e4415b6125feceb39ea`; the mirror records unchanged files from official
|
|
334
|
+
[`bytedance-research/LVFace`](https://huggingface.co/bytedance-research/LVFace) revision
|
|
335
|
+
`b12702ab1f5c721748e054a66dc90e1edd1f0724`. Review the official model card and seek
|
|
336
|
+
clarification from the authors when necessary.
|
|
337
|
+
|
|
338
|
+
Use of the weights requires citation of the original work:
|
|
339
|
+
|
|
340
|
+
```bibtex
|
|
341
|
+
@inproceedings{you2025lvface,
|
|
342
|
+
title={{LVFace}: Progressive Cluster Optimization for Large Vision Models in Face Recognition},
|
|
343
|
+
author={You, Jinghan and Li, Shanglin and Sun, Yuanrui and Wei, Jiangchuan and Guo, Mingyu and Feng, Chao and Ran, Jiao},
|
|
344
|
+
booktitle={ICCV},
|
|
345
|
+
year={2025}
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Development
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
python -m pip install -e ".[dev]"
|
|
353
|
+
ruff check .
|
|
354
|
+
ruff format --check .
|
|
355
|
+
mypy src
|
|
356
|
+
pytest
|
|
357
|
+
```
|