pseudopros 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.
- pseudopros-0.1.0/.github/workflows/publish.yml +46 -0
- pseudopros-0.1.0/.gitignore +15 -0
- pseudopros-0.1.0/.python-version +1 -0
- pseudopros-0.1.0/LICENSE +21 -0
- pseudopros-0.1.0/PKG-INFO +366 -0
- pseudopros-0.1.0/Packaging.md +26 -0
- pseudopros-0.1.0/README.md +302 -0
- pseudopros-0.1.0/experiments/CONCEPT.MD +36 -0
- pseudopros-0.1.0/experiments/Experiments.log +279 -0
- pseudopros-0.1.0/experiments/exp-001/download_hdtf.py +200 -0
- pseudopros-0.1.0/experiments/exp-001/eval.py +118 -0
- pseudopros-0.1.0/experiments/exp-001/train.py +553 -0
- pseudopros-0.1.0/experiments/exp-002/Dockerfile +68 -0
- pseudopros-0.1.0/experiments/exp-002/PLAN.md +165 -0
- pseudopros-0.1.0/experiments/exp-002/download_hdtf.py +200 -0
- pseudopros-0.1.0/experiments/exp-002/entrypoint.sh +30 -0
- pseudopros-0.1.0/experiments/exp-002/eval.py +118 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/00-pvc.yaml +56 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/01-secret.example.yaml +19 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/02-job-precompute.yaml +75 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/03-job-train.yaml +76 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/04-build.yaml +40 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/05-debug-pod.yaml +51 -0
- pseudopros-0.1.0/experiments/exp-002/openshift/README.md +118 -0
- pseudopros-0.1.0/experiments/exp-002/train.py +582 -0
- pseudopros-0.1.0/licenses.json +282 -0
- pseudopros-0.1.0/pyproject.toml +87 -0
- pseudopros-0.1.0/scripts/test_fomm_inference.py +99 -0
- pseudopros-0.1.0/scripts/webcam_demo.py +53 -0
- pseudopros-0.1.0/src/pseudopros/__init__.py +26 -0
- pseudopros-0.1.0/src/pseudopros/_cli.py +159 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/__init__.py +0 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/modules/__init__.py +0 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/modules/dense_motion.py +113 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/modules/generator.py +97 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/modules/keypoint_detector.py +75 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/modules/util.py +245 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/sync_batchnorm/__init__.py +12 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/sync_batchnorm/batchnorm.py +315 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/sync_batchnorm/comm.py +137 -0
- pseudopros-0.1.0/src/pseudopros/_fomm/sync_batchnorm/replicate.py +94 -0
- pseudopros-0.1.0/src/pseudopros/anonymizer.py +227 -0
- pseudopros-0.1.0/src/pseudopros/embedding.py +115 -0
- pseudopros-0.1.0/src/pseudopros/fomm.py +299 -0
- pseudopros-0.1.0/src/pseudopros/gst.py +231 -0
- pseudopros-0.1.0/src/pseudopros/keying.py +113 -0
- pseudopros-0.1.0/src/pseudopros/security.py +177 -0
- pseudopros-0.1.0/src/pseudopros/synthesizer.py +262 -0
- pseudopros-0.1.0/src/pseudopros/unified_renderer/__init__.py +12 -0
- pseudopros-0.1.0/src/pseudopros/unified_renderer/model.py +234 -0
- pseudopros-0.1.0/tests/__init__.py +0 -0
- pseudopros-0.1.0/tests/test_anonymizer.py +216 -0
- pseudopros-0.1.0/tests/test_cli.py +209 -0
- pseudopros-0.1.0/tests/test_embedding.py +107 -0
- pseudopros-0.1.0/tests/test_fomm.py +71 -0
- pseudopros-0.1.0/tests/test_fomm_pretrained.py +54 -0
- pseudopros-0.1.0/tests/test_fomm_subject.py +110 -0
- pseudopros-0.1.0/tests/test_gst.py +166 -0
- pseudopros-0.1.0/tests/test_keying.py +107 -0
- pseudopros-0.1.0/tests/test_security.py +169 -0
- pseudopros-0.1.0/tests/test_synthesizer.py +138 -0
- pseudopros-0.1.0/vendor/first-order-model/__init__.py +0 -0
- pseudopros-0.1.0/vendor/first-order-model/demo.py +168 -0
- pseudopros-0.1.0/vendor/first-order-model/modules/__init__.py +0 -0
- pseudopros-0.1.0/vendor/first-order-model/modules/dense_motion.py +113 -0
- pseudopros-0.1.0/vendor/first-order-model/modules/generator.py +97 -0
- pseudopros-0.1.0/vendor/first-order-model/modules/keypoint_detector.py +75 -0
- pseudopros-0.1.0/vendor/first-order-model/modules/util.py +245 -0
- pseudopros-0.1.0/vendor/first-order-model/sync_batchnorm/__init__.py +12 -0
- pseudopros-0.1.0/vendor/first-order-model/sync_batchnorm/batchnorm.py +315 -0
- pseudopros-0.1.0/vendor/first-order-model/sync_batchnorm/comm.py +137 -0
- pseudopros-0.1.0/vendor/first-order-model/sync_batchnorm/replicate.py +94 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
id-token: write
|
|
10
|
+
attestations: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build distribution
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Install hatch
|
|
22
|
+
run: pip install hatch
|
|
23
|
+
- name: Build wheel and sdist
|
|
24
|
+
run: hatch build
|
|
25
|
+
- name: Attest build provenance
|
|
26
|
+
uses: actions/attest-build-provenance@v2
|
|
27
|
+
continue-on-error: true
|
|
28
|
+
with:
|
|
29
|
+
subject-path: dist/*
|
|
30
|
+
- uses: actions/upload-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
name: Publish to PyPI
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment: pypi
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
- name: Publish via Trusted Publisher (OIDC)
|
|
46
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12.8
|
pseudopros-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Cory Root
|
|
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.
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pseudopros
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cryptographically secure facial anonymization for computer vision pipelines
|
|
5
|
+
Project-URL: Homepage, https://github.com/dactylroot/pseudopros
|
|
6
|
+
Project-URL: Repository, https://github.com/dactylroot/pseudopros
|
|
7
|
+
Project-URL: Issues, https://github.com/dactylroot/pseudopros/issues
|
|
8
|
+
Author-email: Cory Root <dactylroot@gmail.com>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2024 Cory Root
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: anonymization,computer-vision,cryptography,face,privacy,re-identification,video
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Intended Audience :: Science/Research
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
39
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
40
|
+
Classifier: Topic :: Security :: Cryptography
|
|
41
|
+
Requires-Python: >=3.12
|
|
42
|
+
Requires-Dist: accelerate>=0.30
|
|
43
|
+
Requires-Dist: cryptography>=41.0
|
|
44
|
+
Requires-Dist: diffusers<0.31,>=0.28
|
|
45
|
+
Requires-Dist: huggingface-hub>=0.20
|
|
46
|
+
Requires-Dist: imageio-ffmpeg>=0.4
|
|
47
|
+
Requires-Dist: imageio>=2.31
|
|
48
|
+
Requires-Dist: insightface>=0.7
|
|
49
|
+
Requires-Dist: numpy<2.0,>=1.24
|
|
50
|
+
Requires-Dist: onnxruntime<1.20,>=1.16
|
|
51
|
+
Requires-Dist: opencv-python>=4.8
|
|
52
|
+
Requires-Dist: pillow>=10.0
|
|
53
|
+
Requires-Dist: pyyaml>=6.0
|
|
54
|
+
Requires-Dist: scikit-image<0.24,>=0.21
|
|
55
|
+
Requires-Dist: scipy<1.14,>=1.11
|
|
56
|
+
Requires-Dist: torch<2.3,>=2.2
|
|
57
|
+
Requires-Dist: torchvision<0.18,>=0.17
|
|
58
|
+
Provides-Extra: dev
|
|
59
|
+
Requires-Dist: hatch>=1.12; extra == 'dev'
|
|
60
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
61
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
62
|
+
Requires-Dist: rtsp>=2.0.2; extra == 'dev'
|
|
63
|
+
Description-Content-Type: text/markdown
|
|
64
|
+
|
|
65
|
+
# Pseudopros
|
|
66
|
+
|
|
67
|
+
Pseudo (false) + Prosopon (face/mask)
|
|
68
|
+
|
|
69
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⡆⠀⠀⠀⠀
|
|
70
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⣿⣿⣿⠃⣠⠀⠀⠀
|
|
71
|
+
⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣶⣿⣿⣿⣿⣿⠇⢰⣿⣇⠀⠀
|
|
72
|
+
⠀⠀⣼⣿⣿⣶⣤⣀⡀⠀⠀⠀⠀⠻⠿⠟⠛⣿⣿⣿⣿⣿⣿⡏⠀⠛⣿⣿⡄⠀
|
|
73
|
+
⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣶⡆⢀⡛⠛⠛⠛⢿⣿⣆⣀⣠⣿⣿⡧⠀
|
|
74
|
+
⠀⢸⣉⠁⠀⢠⣿⣿⠁⣈⡙⠛⠻⠿⠿⠃⠸⠿⠇⠀⠀⢈⡁⢸⣿⣿⡿⠟⣡⠀
|
|
75
|
+
⠀⣿⣿⣆⣀⣸⣿⡿⠀⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣾⣿⣿⣇⠈⠟⢉⣤⣾⡇⠀
|
|
76
|
+
⠀⢸⣿⣿⣿⣿⣿⡇⠀⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠀⣶⣿⣿⠟⠀⠀
|
|
77
|
+
⠀⠈⣉⣉⣉⣉⡉⠓⠀⣶⣦⣤⠀⠉⠙⣿⣿⡏⠉⠀⣤⣤⡖⠀⠉⠉⠁⠀⠀⠀
|
|
78
|
+
⠀⠀⠘⢿⣿⣿⣿⣿⡀⢿⣿⣿⣶⣤⠀⣿⣿⠁⢠⣶⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀
|
|
79
|
+
⠀⠀⠀⠈⠛⠻⠿⠟⠃⠘⣿⣿⣿⣿⣶⣿⣿⣶⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀
|
|
80
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣯⣉⠉⠉⠉⢉⣩⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀
|
|
81
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣶⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
82
|
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
83
|
+
|
|
84
|
+
A Python library for cryptographically secure facial anonymization in computer vision pipelines.
|
|
85
|
+
|
|
86
|
+
Pseudopros replaces real faces in video streams with deterministic synthetic faces that:
|
|
87
|
+
|
|
88
|
+
- **Cannot be re-identified** - output faces are cryptographically unlinked from the original (validated against ArcFace ReID)
|
|
89
|
+
- **Are repeatable** - the same input face always produces the same synthetic output for a given key and salt
|
|
90
|
+
- **Are cross-source** - the same key and salt produce consistent identities across independently captured video of the same subject
|
|
91
|
+
- **Preserve motion** - expression, pose, and movement data are retained for downstream tracking and aggregate analysis
|
|
92
|
+
|
|
93
|
+
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.
|
|
94
|
+
|
|
95
|
+
## Security properties
|
|
96
|
+
|
|
97
|
+
| Property | Mechanism |
|
|
98
|
+
|---|---|
|
|
99
|
+
| Re-identification resistance | LDM-generated face is orthogonal to original in ArcFace space (validated: similarity ≈ -0.03 vs threshold 0.28) |
|
|
100
|
+
| Repeatability | HKDF is deterministic; same inputs always produce the same seed and therefore the same synthetic face |
|
|
101
|
+
| Cross-source consistency | Key derivation depends only on the identity embedding, not the video source |
|
|
102
|
+
| Key isolation | Rotating the salt changes all synthetic identities without changing the secret |
|
|
103
|
+
|
|
104
|
+
## Cryptography
|
|
105
|
+
|
|
106
|
+
pseudopros depends on the [PyCA `cryptography`](https://cryptography.io) library for one purpose: **HKDF-SHA256** key derivation in `keying.py`.
|
|
107
|
+
|
|
108
|
+
**HKDF** (HMAC-based Key Derivation Function, [RFC 5869](https://datatracker.ietf.org/doc/html/rfc5869)) turns a secret key and a subject embedding into a fixed-length seed:
|
|
109
|
+
|
|
110
|
+
| Input | Value |
|
|
111
|
+
|---|---|
|
|
112
|
+
| IKM (input key material) | Your secret bytes (`secret`) |
|
|
113
|
+
| Salt | Per-deployment salt (`salt`) |
|
|
114
|
+
| Info | `b"pseudopros-v1:" + subject_embedding_bytes` |
|
|
115
|
+
| Output length | 32 bytes |
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
No encryption, signing, MAC verification, or other crypto primitives are used anywhere else in the codebase.
|
|
120
|
+
|
|
121
|
+
## How it works
|
|
122
|
+
|
|
123
|
+
1. **Enrollment** - ArcFace embeds the subject's face into a 512-dimensional identity vector
|
|
124
|
+
2. **Key derivation** - HKDF-SHA256 over `(secret, salt, identity_vector)` produces a 32-byte seed
|
|
125
|
+
3. **Synthetic face generation** - the seed drives a Latent Diffusion Model to produce a deterministic synthetic face unrelated to the original
|
|
126
|
+
4. **Animation** - First Order Motion Model transfers the subject's motion onto the synthetic face frame by frame
|
|
127
|
+
5. **Paste-back** - the animated region is blended back into the original frame with a soft oval mask
|
|
128
|
+
|
|
129
|
+
The synthetic face is the cryptographic output: it is a pure function of `(secret, salt, subject)` with no recoverable path to the original identity.
|
|
130
|
+
|
|
131
|
+
## Installation
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pip install pseudopros
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Requires Python 3.12. PyTorch is installed from the PyTorch CPU index on Intel Mac:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pip install pseudopros --extra-index-url https://download.pytorch.org/whl/cpu
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
On first use, pseudopros downloads three models:
|
|
144
|
+
|
|
145
|
+
- **InsightFace buffalo_l** (~280 MB, cached in `~/.insightface/`) - ArcFace face detection and embedding
|
|
146
|
+
- **dactylroot/pseudopros-fomm** (~695 MB, cached in `~/.cache/huggingface/`) - First Order Motion Model checkpoint
|
|
147
|
+
- **CompVis/ldm-celebahq-256** (~1.3 GB, cached in `~/.cache/huggingface/`) - Latent Diffusion Model for synthetic face generation (only when using `RosterSynthesizer`)
|
|
148
|
+
|
|
149
|
+
## Usage
|
|
150
|
+
|
|
151
|
+
### Common usage
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import pseudopros
|
|
155
|
+
|
|
156
|
+
# Build the pipeline (FOMM weights auto-download ~695 MB on first call)
|
|
157
|
+
anon = pseudopros.Anonymizer.build(
|
|
158
|
+
secret=b"your-secret-key-min-16-bytes",
|
|
159
|
+
salt=b"your-deployment-salt",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Enroll subjects from representative frames
|
|
163
|
+
anon.enroll(frame_of_alice, "alice")
|
|
164
|
+
anon.enroll(frame_of_bob, "bob")
|
|
165
|
+
|
|
166
|
+
# Process frames — every enrolled subject found is anonymized
|
|
167
|
+
for frame in video_frames:
|
|
168
|
+
output = anon.process(frame) # HWC uint8 RGB, same shape as input
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Each subject keeps their own motion baseline across frames. Faces not matching any
|
|
172
|
+
enrolled subject are left unchanged. To remove a subject mid-stream:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
anon.unenroll("alice")
|
|
176
|
+
print(anon.enrolled_ids) # ["bob"]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
To use a local FOMM checkpoint instead of downloading:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
anon = pseudopros.Anonymizer.build(
|
|
183
|
+
secret=b"your-secret-key-min-16-bytes",
|
|
184
|
+
salt=b"your-deployment-salt",
|
|
185
|
+
checkpoint_path="path/to/fomm.pth.tar",
|
|
186
|
+
config_path="path/to/fomm-config.yml",
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Security validation
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
import pseudopros
|
|
194
|
+
|
|
195
|
+
audit = pseudopros.SecurityAudit(embedder)
|
|
196
|
+
result = audit.audit_frame(original_frame, anonymized_frame)
|
|
197
|
+
|
|
198
|
+
print(result.is_secure) # True if similarity < threshold
|
|
199
|
+
print(result.similarity) # ArcFace cosine similarity (lower is more secure)
|
|
200
|
+
|
|
201
|
+
# Audit a sequence
|
|
202
|
+
summary = audit.audit_sequence(original_frames, anonymized_frames)
|
|
203
|
+
print(summary)
|
|
204
|
+
# SecurityAudit - 30 frame(s)
|
|
205
|
+
# All secure: True
|
|
206
|
+
# Threshold: 0.280
|
|
207
|
+
# Max similarity: 0.0031
|
|
208
|
+
# Mean similarity: -0.0018
|
|
209
|
+
# Face detection: 30/30 anonymized frames
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Configuring the synthesizer
|
|
213
|
+
|
|
214
|
+
The default synthesizer is `NoiseSynthesizer` - a fast placeholder that produces non-face-like output. Use it during development to skip the LDM model download.
|
|
215
|
+
|
|
216
|
+
For production anonymization, use `RosterSynthesizer` (downloads ~1.3 GB on first call):
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
import pseudopros
|
|
220
|
+
|
|
221
|
+
anon = pseudopros.Anonymizer.build(
|
|
222
|
+
secret=b"your-secret-key",
|
|
223
|
+
salt=b"your-salt",
|
|
224
|
+
synthesizer=pseudopros.RosterSynthesizer(num_steps=50),
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For large known subject populations, `RapidSynthesizer` uses a pre-generated face bank for O(1) enrollment with no per-subject model inference:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
anon = pseudopros.Anonymizer.build(
|
|
232
|
+
secret=b"your-secret-key",
|
|
233
|
+
salt=b"your-salt",
|
|
234
|
+
synthesizer=pseudopros.RapidSynthesizer("path/to/face_bank.npy"),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Build a face bank offline (one-time, ~20s per face on CPU)
|
|
238
|
+
pseudopros.build_face_bank(n=10_000, output_path="face_bank.npy")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Advanced / custom components
|
|
242
|
+
|
|
243
|
+
Lower-level components are importable from their submodules when you need to customize the pipeline:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
# Custom embedder (e.g. swap InsightFace for another ArcFace backend)
|
|
247
|
+
from pseudopros.embedding import InsightFaceEmbedder, FaceEmbedder, DetectedFace
|
|
248
|
+
|
|
249
|
+
embedder = InsightFaceEmbedder(model_pack="buffalo_sc") # lighter model
|
|
250
|
+
anon = pseudopros.Anonymizer(model, key, embedder)
|
|
251
|
+
|
|
252
|
+
# Custom synthesizer (implement SynthesizerBackend protocol)
|
|
253
|
+
from pseudopros.synthesizer import SynthesizerBackend
|
|
254
|
+
|
|
255
|
+
class MyGANSynthesizer:
|
|
256
|
+
def synthesize(self, seed: bytes) -> np.ndarray:
|
|
257
|
+
... # return (256, 256, 3) float32 [0, 1]
|
|
258
|
+
|
|
259
|
+
# Access FOMM directly
|
|
260
|
+
from pseudopros.fomm import FommModel, FommSubjectState
|
|
261
|
+
|
|
262
|
+
model = FommModel.from_pretrained(device="cpu") # auto-download
|
|
263
|
+
model = FommModel.from_checkpoint("model.pth.tar", "config.yml", device="cpu") # local
|
|
264
|
+
|
|
265
|
+
# Per-subject stateless API — safe to interleave subjects on a shared model
|
|
266
|
+
state_alice = model.prepare_subject(reference_image_alice)
|
|
267
|
+
state_bob = model.prepare_subject(reference_image_bob)
|
|
268
|
+
|
|
269
|
+
output_alice, state_alice = model.animate_subject(driving_frame_alice, state_alice)
|
|
270
|
+
output_bob, state_bob = model.animate_subject(driving_frame_bob, state_bob)
|
|
271
|
+
|
|
272
|
+
# Audit result types
|
|
273
|
+
from pseudopros.security import AuditSummary, FrameAuditResult
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Command-line interface
|
|
277
|
+
|
|
278
|
+
Anonymize a video file directly from the terminal:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
pseudopros anonymize input.mp4 output.mp4 \
|
|
282
|
+
--enrollment face.jpg \
|
|
283
|
+
--secret-file /path/to/secret.bin \
|
|
284
|
+
--synthesizer roster
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Secret options (pick one):
|
|
288
|
+
|
|
289
|
+
| Flag | Description |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `--secret-file PATH` | Read raw secret bytes from a file. Recommended for production. |
|
|
292
|
+
| `--secret-hex HEX` | Secret as a hex string. |
|
|
293
|
+
| `PSEUDOPROS_SECRET` | Env var (hex). Used when no flag is provided. |
|
|
294
|
+
|
|
295
|
+
Additional options: `--salt-hex HEX`, `--device cpu\|cuda\|mps`, `--checkpoint PATH --config PATH` (to use a local FOMM checkpoint instead of downloading).
|
|
296
|
+
|
|
297
|
+
## GStreamer integration
|
|
298
|
+
|
|
299
|
+
`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.
|
|
300
|
+
|
|
301
|
+
### Requirements
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Debian / Ubuntu
|
|
305
|
+
sudo apt-get install python3-gi gstreamer1.0-python3-plugin-scanner \
|
|
306
|
+
gstreamer1.0-plugins-base gstreamer1.0-plugins-good
|
|
307
|
+
|
|
308
|
+
# macOS (Homebrew)
|
|
309
|
+
brew install pygobject3 gstreamer
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Usage
|
|
313
|
+
|
|
314
|
+
Register the element once after `Gst.init()`, then use it in any pipeline string:
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
import gi
|
|
318
|
+
gi.require_version("Gst", "1.0")
|
|
319
|
+
from gi.repository import Gst
|
|
320
|
+
|
|
321
|
+
import pseudopros.gst as ppgst
|
|
322
|
+
|
|
323
|
+
Gst.init(None)
|
|
324
|
+
ppgst.register()
|
|
325
|
+
|
|
326
|
+
pipeline = Gst.parse_launch(
|
|
327
|
+
"v4l2src"
|
|
328
|
+
" ! videoconvert ! video/x-raw,format=RGB"
|
|
329
|
+
" ! pseudopros"
|
|
330
|
+
" secret=BASE64_SECRET"
|
|
331
|
+
" salt=BASE64_SALT"
|
|
332
|
+
" enrollment-image=/path/to/face.jpg"
|
|
333
|
+
" synthesizer=noise"
|
|
334
|
+
" ! videoconvert ! autovideosink"
|
|
335
|
+
)
|
|
336
|
+
pipeline.set_state(Gst.State.PLAYING)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Element properties
|
|
340
|
+
|
|
341
|
+
| Property | Type | Description |
|
|
342
|
+
|---|---|---|
|
|
343
|
+
| `secret` | string | Base64-encoded secret key (min 16 bytes decoded) |
|
|
344
|
+
| `salt` | string | Base64-encoded deployment salt (default: `pseudopros`) |
|
|
345
|
+
| `enrollment-image` | string | Path to an image file for subject enrollment |
|
|
346
|
+
| `synthesizer` | string | `noise` (fast placeholder) or `roster` (full LDM, ~1.3 GB download) |
|
|
347
|
+
|
|
348
|
+
Secrets are base64-encoded for safe embedding in pipeline strings:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
import base64
|
|
352
|
+
secret_b64 = base64.b64encode(b"your-secret-key-min-16-bytes").decode()
|
|
353
|
+
salt_b64 = base64.b64encode(b"your-deployment-salt").decode()
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
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.
|
|
357
|
+
|
|
358
|
+
## Running tests
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# Fast tests only (no model downloads)
|
|
362
|
+
pytest -m "not slow"
|
|
363
|
+
|
|
364
|
+
# Full suite including security validation (~5 min)
|
|
365
|
+
pytest
|
|
366
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## Releasing a new version
|
|
2
|
+
|
|
3
|
+
1. Bump `version` in `pyproject.toml`
|
|
4
|
+
2. Commit and push:
|
|
5
|
+
3. Create a GitHub Release (e.g. tagged `vX.Y.Z`) — this triggers the publish workflow:
|
|
6
|
+
|
|
7
|
+
The workflow builds the wheel, attests SLSA provenance, and publishes to PyPI via OIDC Trusted Publisher (no API tokens required).
|
|
8
|
+
|
|
9
|
+
## README.md is the single source of truth
|
|
10
|
+
|
|
11
|
+
`pyproject.toml` embeds `README.md` as the PyPI long description. The package `__doc__`
|
|
12
|
+
is also read from it at import time via `importlib.metadata`. Do not duplicate
|
|
13
|
+
description content elsewhere.
|
|
14
|
+
|
|
15
|
+
## Development setup
|
|
16
|
+
|
|
17
|
+
Use `pip install -e .` for an editable install. Do not use `uv pip install -e .` — uv
|
|
18
|
+
builds a wheel and copies it to site-packages rather than creating a true editable link,
|
|
19
|
+
so source changes are not reflected until you reinstall.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install -e .
|
|
23
|
+
pytest -m "not slow" # fast suite (~30s, no model downloads)
|
|
24
|
+
pytest # full suite (~5 min, downloads models on first run)
|
|
25
|
+
```
|
|
26
|
+
|