SigOrbit
Rotation-robust handwritten-signature embeddings with continuous SO(2) canonicalization and steerable C4/C8 backbones. Created and maintained by Jordi Murgó (GitHub · jordi.murgo@gmail.com · jordi.murgo@gft.com).
SigOrbit turns a cropped signature image into a deterministic, L2-normalized 256-dimensional vector. It is designed for retrieval, clustering and downstream verification systems that must tolerate arbitrary in-plane rotation without making mirrored signatures equivalent.
Demo
The analyzed signatures are compared with six references while rotation, scale, noise, blur and contrast vary. The matching reference remains identifiable in the per-reference cosine-similarity traces.
Watch or download the MP4 version.
Alpha release. SigOrbit generates embeddings; it is not a complete fraud detector, legal signature verifier, document detector, identity database or anti-spoofing system. Do not use one model score as the sole basis for legal, financial or access-control decisions.
Why the name?
A group action moves an input through its orbit. SigOrbit learns a canonical
SO(2) pose and embeds signatures consistently across that rotation orbit.
sigorbit was clear on PyPI and had no exact GitHub repository collision when
checked on 2026-08-07. This is not a trademark opinion.
Architecture
- C4: 2,254,466 trainable parameters (2.2 M); C8: 4,276,354 (4.3 M)
- 257×257 grayscale input
- 256-dimensional float32 output
- continuous SO(2) rotation canonicalization; no scale or reflection canonicalization
- C4 or C8 regular representations and invariant group pooling; the canonicalizer handles continuous rotation, so C4 equivariance (90° symmetry) is sufficient for signatures and trains 2.7× faster
See the architecture notes and model card.
The end-to-end workflow wraps three training stages:
- validate an immutable local manifest and a separate rights attestation;
- train the C4 or C8 backbone from random weights with PK sampling, discrete rotation augmentation and a temporary ArcFace head;
- restore the best backbone and train only the SO(2) canonicalizer against known synthetic angles;
- jointly optimize canonicalizer, backbone and ArcFace head using identity, circular-orientation and cosine-consistency objectives;
- evaluate on signer-disjoint validation identities, restore the best joint checkpoint and export an inference-only SigOrbit artifact.
There are no reflections: the symmetry is SO(2)+C_N (N = group_order), not O(2)/D_N. The ArcFace classifier, optimizers and schedulers exist only in recovery checkpoints; the deployable artifact contains the canonicalizer and backbone.
Training and model lineage
The runtime package owns the exact model and preprocessing classes. The companion
sigorbit-trainer package
imports those classes directly rather than maintaining a second architecture.
Its current from-scratch protocol has three stages:
- train the C4 or C8 backbone and a temporary ArcFace classifier;
- restore the best backbone, freeze it, and pretrain only the SO(2) canonicalizer against known synthetic angles;
- jointly fine-tune canonicalizer, backbone and ArcFace head with identity, circular-orientation and embedding-consistency losses.
The ArcFace head exists only during training and is not part of an exported
encoder. The published package still identifies the historically selected
checkpoint as sigorbit-c8-257-v1; that checkpoint used an older C8 initializer
whose complete resume history was not archived. The auditable trainer produces
two from-scratch model IDs: sigorbit-c8-257-retrained-v1 (C8, batch 32) and
sigorbit-c4-257-b64 (C4, batch 64). Neither claims a byte-for-byte
reproduction of the deployed model. See training and reproducibility
for all lineages.
Install
Python 3.10+ is supported. Install the correct PyTorch build for your CPU, CUDA or ROCm platform first, then install SigOrbit:
python -m pip install sigorbit
# Optional FastAPI example:
python -m pip install "sigorbit[api]"
For an unreleased commit, installation directly from GitHub is also supported:
python -m pip install "sigorbit @ git+https://github.com/jordi-murgo/sigorbit.git@main"
For NVIDIA/ROCm, follow the official PyTorch selector rather than relying on the CPU wheel chosen by a generic resolver.
Python API
from sigorbit import SignatureEncoder
encoder = SignatureEncoder(
checkpoint="/secure/path/sigorbit-c8-257-v1.pt",
device="auto",
)
vector = encoder.embed("signature.png")
print(vector.shape) # (256,)
print(vector.dtype) # float32
print(float(vector @ vector)) # ~1.0
print(encoder.model_id) # sigorbit-c8-257-v1
print(encoder.preprocess_version) # sigorbit-gray-square-257-v1
Batch inference preserves order:
vectors = encoder.embed_batch(["a.png", "b.png"], batch_size=16)
assert vectors.shape == (2, 256)
Accepted inputs are PIL.Image, NumPy arrays, encoded bytes and file paths.
Inputs must already be cropped signatures. Never compare embeddings generated
by different model_id or preprocess_version values.
FastAPI example
pip install -e '.[api]'
export SIGORBIT_CHECKPOINT=/secure/path/sigorbit-c8-257-v1.pt
export SIGORBIT_CHECKPOINT_SHA256=ec8d99f887f5a2658d93b14a14911b29a1411e9cf142efa85862a47b30cd233e
export SIGORBIT_API_KEY=replace-with-a-secret-from-your-secret-manager
SIGORBIT_DEVICE=auto sigorbit-api
Then, from an authorized client:
curl http://127.0.0.1:8000/health
curl -X POST http://127.0.0.1:8000/embed \
-H "Authorization: Bearer $SIGORBIT_API_KEY" \
-F 'file=@signature.png'
The response includes the model/preprocess identity, predicted canonicalization
angle and 256 normalized floats. Interactive docs are at /docs.
Configuration:
| Variable | Default | Meaning |
|---|---|---|
SIGORBIT_CHECKPOINT |
required in code-only release | Local approved checkpoint path |
SIGORBIT_CHECKPOINT_SHA256 |
required by HTTP API | Expected approved artifact digest |
SIGORBIT_DEVICE |
auto |
cpu, cuda, cuda:0, etc. |
SIGORBIT_API_KEY |
unset on loopback | Bearer token; required by CLI for non-loopback binding |
SIGORBIT_HOST |
127.0.0.1 |
Bind address |
SIGORBIT_PORT |
8000 |
Bind port |
SIGORBIT_MAX_UPLOAD_BYTES |
10485760 |
Encoded upload-file byte limit |
SIGORBIT_MAX_REQUEST_BYTES |
upload limit + 65536 | Pre-multipart request-body limit |
SIGORBIT_MAX_IMAGE_PIXELS |
4194304 |
Decompression-bomb pixel limit |
SIGORBIT_MAX_CONCURRENT_REQUESTS |
1 |
Full concurrent /embed request capacity |
SIGORBIT_QUEUE_TIMEOUT_SECONDS |
1.0 |
Wait before returning HTTP 503 |
Uploaded filenames are ignored: the API passes bytes to the decoder and never
constructs or writes a filesystem path from client input. Encoded inputs are
restricted to PNG, JPEG and WebP. The in-process Python API deliberately accepts
trusted str/Path inputs, so applications must not forward a remote filename
to SignatureEncoder.embed().
See the security review for the threat model, adversarial tests and residual risks.
The server binds to loopback by default. Before binding to a non-loopback address, configure bearer authentication and place it behind a TLS reverse proxy with request/time/rate limits. The built-in limits reduce accidental and simple resource exhaustion; they are not a replacement for an edge proxy or process isolation.
The example intentionally has no signer database and no hard-coded MATCH threshold. Thresholds are catalogue-, domain- and reference-count-specific.
Measured behavior
On the held-out 33-signer/792-image BHSig260 Bengali test split, the 257px checkpoint achieved:
- clean leave-one-out top-1: 100.0% (792/792);
- clean median margin: +0.3196;
- model-canvas non-zero-angle mean top-1: 98.04%;
- square-expand mean top-1: 98.16%;
- raw-expand mean top-1: 89.87%;
- real-signature all-triplet mean top-1: 87.64%.
These numbers describe one dataset/protocol, not a universal error rate. See the model card for exact protocols, limitations and the 129px comparison.
Development
python -m pip install -e '.[api,dev]'
ruff check .
pytest
python -m build
Repository layout:
src/sigorbit/model.py architecture
src/sigorbit/checkpoint.py safe artifact loading and identity
src/sigorbit/preprocessing.py versioned image contract
src/sigorbit/encoder.py public Python API
src/sigorbit/api.py minimal FastAPI example
docs/ architecture, model card, training and release notes
CITATION.bib project and technical bibliography
Responsible use and release status
Handwritten signatures are biometric personal data. Do not log uploads or embeddings unnecessarily, and establish retention, consent, access control and deletion policies before deployment.
The source code is MIT licensed. The repository contains no training images. Before publishing the trained checkpoint, read the completed dataset licence audit and complete RELEASING.md. All 7,560 aggregate images were matched pixel-for-pixel to genuine CEDAR and BHSig260 samples. CEDAR has no explicit licence grant, while the BHSig260 authors state only “for research purposes”. The Hub uploader's MIT tag therefore does not establish a sublicence chain. The local checkpoint is present to validate this extraction; do not push or publish it until upstream permission or counsel approval is recorded.
License and citation
Source code: MIT. Third-party notices: NOTICE. Repository: https://github.com/jordi-murgo/sigorbit.
If SigOrbit is useful in research, cite the software as:
@software{murgo2026sigorbit,
author = {Jordi Murgó},
title = {{SigOrbit}: Rotation-Robust Handwritten-Signature Embeddings},
year = {2026},
version = {0.1.0},
url = {https://github.com/jordi-murgo/sigorbit},
license = {MIT}
}
The complete BibTeX bibliography—including the e2cnn, CEDAR and BHSig260
references—is in CITATION.bib; machine-readable citation
metadata is in CITATION.cff. Citing a dataset does not imply
that it has an open licence; consult the
dataset licence audit.
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 sigorbit-0.2.5.tar.gz.
File metadata
- Download URL: sigorbit-0.2.5.tar.gz
- Upload date:
- Size: 2.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d919d17ae5a90ffb1d4fdc02f39924967d7bf055aca175f50d10af23d318b7f
|
|
| MD5 |
a1301a9526a03748ad569759b12e6786
|
|
| BLAKE2b-256 |
272cd72a77483b845b8f46bdb274d8fd86e2bc4236e03a3320fa32a528379879
|
Provenance
The following attestation bundles were made for sigorbit-0.2.5.tar.gz:
Publisher:
publish.yml on jordi-murgo/sigorbit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sigorbit-0.2.5.tar.gz -
Subject digest:
5d919d17ae5a90ffb1d4fdc02f39924967d7bf055aca175f50d10af23d318b7f - Sigstore transparency entry: 2498824053
- Sigstore integration time:
-
Permalink:
jordi-murgo/sigorbit@598b4a9331f3dfba368e406051e8807e3cbac832 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/jordi-murgo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@598b4a9331f3dfba368e406051e8807e3cbac832 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sigorbit-0.2.5-py3-none-any.whl.
File metadata
- Download URL: sigorbit-0.2.5-py3-none-any.whl
- Upload date:
- Size: 24.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b908bf3fc2140120e7cbcb3c99161ec018d832ef9f09356da42b8e5cf6a19450
|
|
| MD5 |
648a7c7cef46d9c69325033cdb18bc16
|
|
| BLAKE2b-256 |
1da7a9303c7628dc407f22c97e3c10dcfe8c9ea978ded8bcbd0c12d77a3ae2f6
|
Provenance
The following attestation bundles were made for sigorbit-0.2.5-py3-none-any.whl:
Publisher:
publish.yml on jordi-murgo/sigorbit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sigorbit-0.2.5-py3-none-any.whl -
Subject digest:
b908bf3fc2140120e7cbcb3c99161ec018d832ef9f09356da42b8e5cf6a19450 - Sigstore transparency entry: 2498824062
- Sigstore integration time:
-
Permalink:
jordi-murgo/sigorbit@598b4a9331f3dfba368e406051e8807e3cbac832 -
Branch / Tag:
refs/tags/v0.2.5 - Owner: https://github.com/jordi-murgo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@598b4a9331f3dfba368e406051e8807e3cbac832 -
Trigger Event:
release
-
Statement type: