Skip to main content

Cryptographically secure facial anonymization for computer vision pipelines

Project description

Pseudopros

Pseudo (false) + Prosopon (face/mask)

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⡆⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⣿⣿⣿⠃⣠⠀⠀⠀
⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣶⣿⣿⣿⣿⣿⠇⢰⣿⣇⠀⠀
⠀⠀⣼⣿⣿⣶⣤⣀⡀⠀⠀⠀⠀⠻⠿⠟⠛⣿⣿⣿⣿⣿⣿⡏⠀⠛⣿⣿⡄⠀
⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣶⡆⢀⡛⠛⠛⠛⢿⣿⣆⣀⣠⣿⣿⡧⠀
⠀⢸⣉⠁⠀⢠⣿⣿⠁⣈⡙⠛⠻⠿⠿⠃⠸⠿⠇⠀⠀⢈⡁⢸⣿⣿⡿⠟⣡⠀
⠀⣿⣿⣆⣀⣸⣿⡿⠀⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣾⣿⣿⣇⠈⠟⢉⣤⣾⡇⠀
⠀⢸⣿⣿⣿⣿⣿⡇⠀⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠀⣶⣿⣿⠟⠀⠀
⠀⠈⣉⣉⣉⣉⡉⠓⠀⣶⣦⣤⠀⠉⠙⣿⣿⡏⠉⠀⣤⣤⡖⠀⠉⠉⠁⠀⠀⠀
⠀⠀⠘⢿⣿⣿⣿⣿⡀⢿⣿⣿⣶⣤⠀⣿⣿⠁⢠⣶⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠈⠛⠻⠿⠟⠃⠘⣿⣿⣿⣿⣶⣿⣿⣶⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣯⣉⠉⠉⠉⢉⣩⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣶⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

A Python library for cryptographically secure facial anonymization in computer vision pipelines.

Pseudopros replaces real faces in video streams with deterministic synthetic faces that:

  • Cannot be re-identified - output faces are cryptographically unlinked from the original (validated against ArcFace ReID)
  • Are repeatable - the same input face always produces the same synthetic output for a given key and salt
  • Are cross-source - the same key and salt produce consistent identities across independently captured video of the same subject
  • Preserve motion - expression, pose, and movement data are retained for downstream tracking and aggregate analysis

Intended for use within streaming pipelines (e.g. GStreamer). Keys and salts are passed as parameters and should be stored remotely, never embedded with the video.

Security properties

Property Mechanism
Re-identification resistance LDM-generated face is orthogonal to original in ArcFace space (validated: similarity ≈ -0.03 vs threshold 0.28)
Repeatability HKDF is deterministic; same inputs always produce the same seed and therefore the same synthetic face
Cross-source consistency Key derivation depends only on the identity embedding, not the video source
Key isolation Rotating the salt changes all synthetic identities without changing the secret

Cryptography

pseudopros depends on the PyCA cryptography library for one purpose: HKDF-SHA256 key derivation in keying.py.

HKDF (HMAC-based Key Derivation Function, RFC 5869) turns a secret key and a subject embedding into a fixed-length seed:

Input Value
IKM (input key material) Your secret bytes (secret)
Salt Per-deployment salt (salt)
Info b"pseudopros-v1:" + subject_embedding_bytes
Output length 32 bytes

The info field provides domain separation: it binds the derived seed to this library version and to the specific subject identity. Changing the subject embedding, the salt, or the secret each produces an independent seed.

No encryption, signing, MAC verification, or other crypto primitives are used anywhere else in the codebase.

How it works

  1. Enrollment - ArcFace embeds the subject's face into a 512-dimensional identity vector
  2. Key derivation - HKDF-SHA256 over (secret, salt, identity_vector) produces a 32-byte seed
  3. Synthetic face generation - the seed drives a Latent Diffusion Model to produce a deterministic synthetic face unrelated to the original
  4. Animation - First Order Motion Model transfers the subject's motion onto the synthetic face frame by frame
  5. Paste-back - the animated region is blended back into the original frame with a soft oval mask

The synthetic face is the cryptographic output: it is a pure function of (secret, salt, subject) with no recoverable path to the original identity.

Installation

pip install pseudopros

Requires Python 3.12. PyTorch is installed from the PyTorch CPU index on Intel Mac:

pip install pseudopros --extra-index-url https://download.pytorch.org/whl/cpu

On first use, pseudopros downloads three models:

  • InsightFace buffalo_l (~280 MB, cached in ~/.insightface/) - ArcFace face detection and embedding
  • dactylroot/pseudopros-fomm (~695 MB, cached in ~/.cache/huggingface/) - First Order Motion Model checkpoint
  • CompVis/ldm-celebahq-256 (~1.3 GB, cached in ~/.cache/huggingface/) - Latent Diffusion Model for synthetic face generation (only when using RosterSynthesizer)

Usage

Common usage

import pseudopros

# Build the pipeline (FOMM weights auto-download ~695 MB on first call)
anon = pseudopros.Anonymizer.build(
    secret=b"your-secret-key-min-16-bytes",
    salt=b"your-deployment-salt",
)

# Enroll subjects from representative frames
anon.enroll(frame_of_alice, "alice")
anon.enroll(frame_of_bob, "bob")

# Process frames — every enrolled subject found is anonymized
for frame in video_frames:
    output = anon.process(frame)  # HWC uint8 RGB, same shape as input

Each subject keeps their own motion baseline across frames. Faces not matching any enrolled subject are left unchanged. To remove a subject mid-stream:

anon.unenroll("alice")
print(anon.enrolled_ids)  # ["bob"]

To use a local FOMM checkpoint instead of downloading:

anon = pseudopros.Anonymizer.build(
    secret=b"your-secret-key-min-16-bytes",
    salt=b"your-deployment-salt",
    checkpoint_path="path/to/fomm.pth.tar",
    config_path="path/to/fomm-config.yml",
)

Security validation

import pseudopros

audit = pseudopros.SecurityAudit(embedder)
result = audit.audit_frame(original_frame, anonymized_frame)

print(result.is_secure)    # True if similarity < threshold
print(result.similarity)   # ArcFace cosine similarity (lower is more secure)

# Audit a sequence
summary = audit.audit_sequence(original_frames, anonymized_frames)
print(summary)
# SecurityAudit - 30 frame(s)
#   All secure:       True
#   Threshold:        0.280
#   Max similarity:   0.0031
#   Mean similarity:  -0.0018
#   Face detection:   30/30 anonymized frames

Configuring the synthesizer

The default synthesizer is NoiseSynthesizer - a fast placeholder that produces non-face-like output. Use it during development to skip the LDM model download.

For production anonymization, use RosterSynthesizer (downloads ~1.3 GB on first call):

import pseudopros

anon = pseudopros.Anonymizer.build(
    secret=b"your-secret-key",
    salt=b"your-salt",
    synthesizer=pseudopros.RosterSynthesizer(num_steps=50),
)

For large known subject populations, RapidSynthesizer uses a pre-generated face bank for O(1) enrollment with no per-subject model inference:

anon = pseudopros.Anonymizer.build(
    secret=b"your-secret-key",
    salt=b"your-salt",
    synthesizer=pseudopros.RapidSynthesizer("path/to/face_bank.npy"),
)

# Build a face bank offline (one-time, ~20s per face on CPU)
pseudopros.build_face_bank(n=10_000, output_path="face_bank.npy")

Advanced / custom components

Lower-level components are importable from their submodules when you need to customize the pipeline:

# Custom embedder (e.g. swap InsightFace for another ArcFace backend)
from pseudopros.embedding import InsightFaceEmbedder, FaceEmbedder, DetectedFace

embedder = InsightFaceEmbedder(model_pack="buffalo_sc")  # lighter model
anon = pseudopros.Anonymizer(model, key, embedder)

# Custom synthesizer (implement SynthesizerBackend protocol)
from pseudopros.synthesizer import SynthesizerBackend

class MyGANSynthesizer:
    def synthesize(self, seed: bytes) -> np.ndarray:
        ...  # return (256, 256, 3) float32 [0, 1]

# Access FOMM directly
from pseudopros.fomm import FommModel, FommSubjectState

model = FommModel.from_pretrained(device="cpu")            # auto-download
model = FommModel.from_checkpoint("model.pth.tar", "config.yml", device="cpu")  # local

# Per-subject stateless API — safe to interleave subjects on a shared model
state_alice = model.prepare_subject(reference_image_alice)
state_bob   = model.prepare_subject(reference_image_bob)

output_alice, state_alice = model.animate_subject(driving_frame_alice, state_alice)
output_bob,   state_bob   = model.animate_subject(driving_frame_bob,   state_bob)

# Audit result types
from pseudopros.security import AuditSummary, FrameAuditResult

Command-line interface

Anonymize a video file directly from the terminal:

pseudopros anonymize input.mp4 output.mp4 \
    --enrollment face.jpg \
    --secret-file /path/to/secret.bin \
    --synthesizer roster

Secret options (pick one):

Flag Description
--secret-file PATH Read raw secret bytes from a file. Recommended for production.
--secret-hex HEX Secret as a hex string.
PSEUDOPROS_SECRET Env var (hex). Used when no flag is provided.

Additional options: --salt-hex HEX, --device cpu\|cuda\|mps, --checkpoint PATH --config PATH (to use a local FOMM checkpoint instead of downloading).

GStreamer integration

pseudopros.gst provides a pseudopros GStreamer element that applies face anonymization as an in-place video filter. It is a separate submodule so that the rest of the library works without GStreamer installed.

Requirements

# Debian / Ubuntu
sudo apt-get install python3-gi gstreamer1.0-python3-plugin-scanner \
    gstreamer1.0-plugins-base gstreamer1.0-plugins-good

# macOS (Homebrew)
brew install pygobject3 gstreamer

Usage

Register the element once after Gst.init(), then use it in any pipeline string:

import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst

import pseudopros.gst as ppgst

Gst.init(None)
ppgst.register()

pipeline = Gst.parse_launch(
    "v4l2src"
    " ! videoconvert ! video/x-raw,format=RGB"
    " ! pseudopros"
    "     secret=BASE64_SECRET"
    "     salt=BASE64_SALT"
    "     enrollment-image=/path/to/face.jpg"
    "     synthesizer=noise"
    " ! videoconvert ! autovideosink"
)
pipeline.set_state(Gst.State.PLAYING)

Element properties

Property Type Description
secret string Base64-encoded secret key (min 16 bytes decoded)
salt string Base64-encoded deployment salt (default: pseudopros)
enrollment-image string Path to an image file for subject enrollment
synthesizer string noise (fast placeholder) or roster (full LDM, ~1.3 GB download)

Secrets are base64-encoded for safe embedding in pipeline strings:

import base64
secret_b64 = base64.b64encode(b"your-secret-key-min-16-bytes").decode()
salt_b64 = base64.b64encode(b"your-deployment-salt").decode()

The element enrolls on the first frame after enrollment-image is set. Changing secret or salt at runtime clears the cached anonymizer and re-enrolls on the next frame.

Running tests

# Fast tests only (no model downloads)
pytest -m "not slow"

# Full suite including security validation (~5 min)
pytest

Project details


Download files

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

Source Distribution

pseudopros-0.1.0.tar.gz (106.7 kB view details)

Uploaded Source

Built Distribution

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

pseudopros-0.1.0-py3-none-any.whl (44.5 kB view details)

Uploaded Python 3

File details

Details for the file pseudopros-0.1.0.tar.gz.

File metadata

  • Download URL: pseudopros-0.1.0.tar.gz
  • Upload date:
  • Size: 106.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pseudopros-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ef10fc9a8f7d6f241dbaf7c8fc85105f2acd27f235c8886da8f63b88b40cabd1
MD5 47ccb34c969388f945ea85d9d564ab5e
BLAKE2b-256 a26a618f256af1b83a36f84c77833df5f1cb6015effd7fc1826c13c71de5457f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pseudopros-0.1.0.tar.gz:

Publisher: publish.yml on dactylroot/pseudopros

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pseudopros-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pseudopros-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 44.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pseudopros-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 078c495de3ba665b0f5c10484ac6ca065c070b3703c1ff9cb3c921c2c8bcfe2f
MD5 e4dbf4bcd37992074d76d557793d1a87
BLAKE2b-256 603ea158ce214d6ebe3db3622ffc06f2040f200785c33457dec8883eb1607d17

See more details on using hashes here.

Provenance

The following attestation bundles were made for pseudopros-0.1.0-py3-none-any.whl:

Publisher: publish.yml on dactylroot/pseudopros

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page