Skip to main content

AlchemyFace

PyPI CI Ruff Python 3.10+

Face recognition built on YuNet and SFacea typed Python library and a desktop application for building face databases.

pip install alchemyface
alchemyface db          # the Face DB Builder

Why

Most Python face-recognition packages pull in dlib, PyTorch or TensorFlow. AlchemyFace uses two small ONNX models through OpenCV's own DNN runtime: a working install is a few megabytes of Python plus about 37 MB of weights fetched once, on first use.


The library

import cv2
from alchemyface import Recognizer

r = Recognizer()                       # weights download once, then cached

r.enroll("prashant", cv2.imread("me.jpg"))
r.enroll("alice",    cv2.imread("alice.jpg"))

for recognition in r.identify(cv2.imread("group.jpg")):
    face, match = recognition.face, recognition.match
    if match:
        print(f"{match.label} at {face.bbox} ({match.score:.2f})")
    else:
        print(f"unknown face at {face.bbox}")

identify returns one Recognition per detected face. match is None when nothing clears the threshold — the library never invents a label.

Galleries

Recognizer is a facade over three protocols — Detector, Embedder and FaceStore — so any conforming object can be substituted.

Store
InMemoryStore numpy matrix, unit vectors, .npz save/load. The default.
PickleStore the Unitree G1 robot's list[(id, name, group, vector)] pickle. Stores vectors verbatim.
from alchemyface.store import PickleStore

store = PickleStore()
store.load("face_db.pkl")
print(len(store), store.dim)
for entry in store.entries():
    print(entry.label, entry.group, entry.vector.shape)

Raw versus unit embeddings

SFaceEmbedder returns unit-length vectors by default, because the Embedder protocol promises it and the rest of the library relies on it. Pass normalize=False for SFace's raw output, whose L2 norm is around 10:

from alchemyface.embedding import SFaceEmbedder

SFaceEmbedder().embed(image, face)                    # L2 == 1
SFaceEmbedder(normalize=False).embed(image, face)     # L2 ≈ 10, raw

Cosine similarity is scale-invariant, so matching is identical either way. What differs is what lands on disk: the robot's schema stores raw values, and keeping them means a database written here stays comparable with one written by anything else, and the L2 norm column remains a useful diagnostic rather than reading 1.0000 for every entry.

Live video

from alchemyface import Recognizer
from alchemyface.capture import VideoSource

r = Recognizer()
with VideoSource(0, width=1280, height=720) as camera:
    for frame in camera.frames():
        for recognition in r.identify(frame):
            print(recognition.match.label if recognition.match else "unknown")

The application

alchemyface db

A Tkinter desktop app that turns folders of photos into a .pkl face database.

Build DB

Three panes: image sidebar, the current image with numbered face boxes, and one panel per face.

  1. Choose an input folder and click Open. Every image is detected in the background, the one on screen first, so the sidebar fills in as you work.
  2. Each detected face becomes a numbered box on the canvas and a row on the right — thumbnail, Include, Name, Group. Names default to the filename, or <stem>_faceN when an image holds several.
  3. Untick Include to drop a face; its box turns dashed.
  4. Click a box to select that face. Re-detect runs YuNet again, asking first if you have unsaved edits.
  5. Save .pkl writes every included, named face, computing any embedding not already cached and renumbering ids from "0".

Sidebar glyphs: · pending · detection failed · no face · ○ (0/N) nothing included · ✓ (k/N) k of N included.

A failure and an empty result are shown differently on purpose. Reporting a broken model or an unreadable file as "no face detected" makes it look like a finding about the photograph — which once put a false claim in this project's own roadmap.

Inspect DB

Read-only viewer for any database: ID · Name · Group · Dim · L2 norm · first values, with a summary line of counts, dimension and file size. Reads the four-tuple list form and the back-compatible {name: vector} dict.

Edit DB

Open an existing database and change it.

  • Load any .pkl; the table shows # · Name · Group · Dim · L2 norm · first values. The frame title gains * while there are unsaved changes, and closing the window asks before discarding them.
  • Remove selected — or the Delete key — drops the chosen rows.
  • Double-click a Group cell to edit it inline from a combobox of presets. Anything you type is added to the presets.
  • Add faces from a folder or a single image. Each detected face becomes a candidate card — thumbnail, Include, Name, Group — and nothing changes until you press Add checked. Duplicate names are allowed: the robot resolves by best cosine similarity, so a second photo of someone is an improvement.
  • Save writes over the loaded path; Save as… writes elsewhere.

Resize is planned — see versions.md.

The .pkl schema

[
    ("0", "Alice", "staff",   np.ndarray(shape=(128,), dtype=float32)),
    ("1", "Bob",   "visitor", np.ndarray(shape=(128,), dtype=float32)),
]

Reading is deliberately forgiving. Real databases disagree with this documentation — id is sometimes an int, and the vector sometimes (1, 128) — so both are coerced. A stricter reader would refuse a database that works today.


Requirements

opencv-python-headless, numpy, typer, Pillow. Python 3.10 or newer.

The GUI needs tkinter, but the library does not. Nothing in the library imports it, so import alchemyface works on a server, in Docker, or in CI with no Tk installed — enforced by tests, not hoped for. alchemyface db reports what to install rather than raising:

$ alchemyface db
the desktop application needs tkinter, which is not available: No module named '_tkinter'
  Debian/Ubuntu:  sudo apt-get install python3-tk
  Fedora:         sudo dnf install python3-tkinter

The presentation helpers are Tk-free too, so you can render a database in a web app or a notebook:

from alchemyface.gui.inspect_data import entry_rows, summarise

OpenCV is the headless build, so there is no libGL requirement either.

Model weights

Resolved in this order, first hit wins:

  1. model_dir= passed to Recognizer
  2. $ALCHEMYFACE_MODEL_DIR
  3. ~/.cache/alchemyface/models/
  4. downloaded from the OpenCV Zoo and SHA256-verified

alchemyface download-models pre-fetches. Set ALCHEMYFACE_MODEL_DIR to work offline.

The recognition threshold

The library defaults to cosine 0.363, SFace's published operating point. The G1 robot matches at 0.32. It is a tunable, not a constant — validate it against your own data.

Development

pyenv install 3.10.6
pyenv virtualenv 3.10.6 alchemyface     # .python-version activates it here
pip install -e ".[dev]"
Command
pytest tests/ -m "not models and not camera and not gui" the fast suite — no display, no models, no network
pytest tests/ -m "gui" needs a display; xvfb-run -a on a headless box
pytest tests/ -m "not camera" everything except the camera
ruff check src tests · ruff format src tests lint and format
mypy src/alchemyface type check
python -m build wheel and sdist

There is deliberately no coverage badge. A hand-written percentage goes stale silently, and generating a real one needs either a third-party service or a bot committing to main — which the repository's commit-identity check refuses. The CI badge already means the gate passed, and that gate includes a coverage floor.

Model-backed tests skip unless the weights are present:

export ALCHEMYFACE_MODEL_DIR="$PWD/_local/onnx"

A note on data

_local/ is git-ignored and must stay that way. It holds face embeddings, recordings and photographs of real, identifiable people, carried over from the prototype this grew out of. Under Japan's APPI and GDPR Article 9 those are sensitive personal data. They are development fixtures: excluded from the wheel, the sdist and version control, and a CI step fails the build if any of them ever reach a distribution.

Links

Licence

MIT — see LICENSE.

Model weights are distributed by the OpenCV Zoo under their own terms — YuNet MIT, SFace Apache-2.0 — and are downloaded at runtime rather than redistributed here.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

alchemyface-0.5.0.tar.gz (56.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

alchemyface-0.5.0-py3-none-any.whl (60.7 kB view details)

Uploaded Python 3

File details

Details for the file alchemyface-0.5.0.tar.gz.

File metadata

  • Download URL: alchemyface-0.5.0.tar.gz
  • Upload date:
  • Size: 56.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for alchemyface-0.5.0.tar.gz
Algorithm Hash digest
SHA256 b42a8e3c63a2a104d3ad463d3a00128a7c3f7bf5b5b052ac2b86e92aa4dbe632
MD5 7f19f75a407717366ed7bfad36badcbc
BLAKE2b-256 e0dcbc201a9c2df5e7645399b1d6f0c9cfb3c2152805e74f3ed54cbe16da65fa

See more details on using hashes here.

File details

Details for the file alchemyface-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: alchemyface-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 60.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for alchemyface-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b8160660dab72892f1dfb850ffebdd84a017ec00522e49dd34b6b101cd009e89
MD5 f92275f458255732b719a28faa5723a6
BLAKE2b-256 568cae47391108028e91175116f7282ccf077a87df1a6e9206363f0d94c9b02f

See more details on using hashes here.

Release history Release notifications | RSS feed

1.2.0

2 files

1.1.1

2 files

1.0.0

2 files

0.6.0

2 files

This release

0.5.0 This release

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page