abstract-identity 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.
- abstract_identity-0.1.0/LICENSE +59 -0
- abstract_identity-0.1.0/PKG-INFO +159 -0
- abstract_identity-0.1.0/README.md +138 -0
- abstract_identity-0.1.0/pyproject.toml +59 -0
- abstract_identity-0.1.0/setup.cfg +4 -0
- abstract_identity-0.1.0/src/abstract_identity/__init__.py +17 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/__init__.py +51 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/cli.py +58 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/geometry.py +66 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/pipeline.py +118 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/schemas.py +105 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/stage_cluster.py +116 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/stage_detect.py +75 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/stage_faces.py +80 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/stage_scenes.py +36 -0
- abstract_identity-0.1.0/src/abstract_identity/char360/stage_select.py +105 -0
- abstract_identity-0.1.0/src/abstract_identity/render/__init__.py +52 -0
- abstract_identity-0.1.0/src/abstract_identity/render/api_models.py +178 -0
- abstract_identity-0.1.0/src/abstract_identity/render/blender_turntable.py +207 -0
- abstract_identity-0.1.0/src/abstract_identity/render/capabilities.py +152 -0
- abstract_identity-0.1.0/src/abstract_identity/render/jobstate.py +280 -0
- abstract_identity-0.1.0/src/abstract_identity/render/mesh.py +531 -0
- abstract_identity-0.1.0/src/abstract_identity/render/models.py +42 -0
- abstract_identity-0.1.0/src/abstract_identity/render/service.py +1026 -0
- abstract_identity-0.1.0/src/abstract_identity/render/turntable.py +231 -0
- abstract_identity-0.1.0/src/abstract_identity.egg-info/PKG-INFO +159 -0
- abstract_identity-0.1.0/src/abstract_identity.egg-info/SOURCES.txt +37 -0
- abstract_identity-0.1.0/src/abstract_identity.egg-info/dependency_links.txt +1 -0
- abstract_identity-0.1.0/src/abstract_identity.egg-info/entry_points.txt +3 -0
- abstract_identity-0.1.0/src/abstract_identity.egg-info/requires.txt +11 -0
- abstract_identity-0.1.0/src/abstract_identity.egg-info/top_level.txt +3 -0
- abstract_identity-0.1.0/src/char360/__init__.py +36 -0
- abstract_identity-0.1.0/src/char360/__main__.py +5 -0
- abstract_identity-0.1.0/src/char360/cli.py +10 -0
- abstract_identity-0.1.0/src/identity_render/__init__.py +44 -0
- abstract_identity-0.1.0/src/identity_render/service.py +9 -0
- abstract_identity-0.1.0/tests/test_geometry.py +109 -0
- abstract_identity-0.1.0/tests/test_pipeline.py +175 -0
- abstract_identity-0.1.0/tests/test_schemas.py +133 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 putkoff (hugpy.ai)
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
THIRD-PARTY OBLIGATIONS NOTICE (not part of the MIT license above)
|
|
26
|
+
|
|
27
|
+
The MIT license covers abstract_identity's own source code only (both the
|
|
28
|
+
char360 and render subpackages). A DEPLOYED installation with the optional
|
|
29
|
+
runtime stacks present loads third-party components whose licenses impose
|
|
30
|
+
their own obligations on any combined or deployed work:
|
|
31
|
+
|
|
32
|
+
char360 subpackage (the [render] extra's detection stack):
|
|
33
|
+
|
|
34
|
+
* ultralytics (YOLO) — AGPL-3.0. A deployment of char360 with ultralytics
|
|
35
|
+
installed is an AGPL-governed combined work: offering it as a network
|
|
36
|
+
service or distributing it triggers AGPL source obligations. Ultralytics
|
|
37
|
+
sells a commercial Enterprise license; permissive alternatives exist
|
|
38
|
+
(e.g. Apache-2.0 RT-DETR).
|
|
39
|
+
* insightface — the library code is MIT, but the pretrained model packs it
|
|
40
|
+
downloads (e.g. "buffalo_l") are published for non-commercial research
|
|
41
|
+
use only. No license file accompanies the packs on disk; the restriction
|
|
42
|
+
is real regardless.
|
|
43
|
+
|
|
44
|
+
render subpackage (a deployed identity-render service, installed via
|
|
45
|
+
install/install_identity_render_ae.sh — never pip deps):
|
|
46
|
+
|
|
47
|
+
* Hunyuan3D-2 (hy3dgen code AND weights) — Tencent Hunyuan 3D 2.0
|
|
48
|
+
Community License: does not apply in the EU/UK/South Korea (including
|
|
49
|
+
generated OUTPUTS), >1M-MAU products need a separate Tencent license,
|
|
50
|
+
hosted services must disclose the operating entity, and outputs may not
|
|
51
|
+
be used to improve other AI models.
|
|
52
|
+
* pymeshlab — GPL-3.0 (pulled in by Hunyuan3D's requirements). No
|
|
53
|
+
obligation for hosted use; distributing an environment containing it
|
|
54
|
+
triggers GPL source obligations.
|
|
55
|
+
* Blender (invoked as a subprocess; GPL) and the installer-vendored copy
|
|
56
|
+
of it — redistributing the install root includes a GPL program.
|
|
57
|
+
|
|
58
|
+
Nothing in this notice modifies the MIT grant over abstract_identity's own
|
|
59
|
+
code.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: abstract_identity
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Identity pipeline for hugpy: char360 (video -> character face detection/clustering/360-view selection) + render (identity-render GPU service: Hunyuan3D-2mv mesh, Blender turntable, video_extract, video_characters_glb)
|
|
5
|
+
Author: putkoff
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: flask<4.0,>=3.0
|
|
11
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
12
|
+
Provides-Extra: render
|
|
13
|
+
Requires-Dist: scenedetect[opencv]; extra == "render"
|
|
14
|
+
Requires-Dist: ultralytics; extra == "render"
|
|
15
|
+
Requires-Dist: insightface; extra == "render"
|
|
16
|
+
Requires-Dist: onnxruntime-gpu==1.18.1; extra == "render"
|
|
17
|
+
Requires-Dist: opencv-python; extra == "render"
|
|
18
|
+
Requires-Dist: numpy; extra == "render"
|
|
19
|
+
Requires-Dist: scikit-learn; extra == "render"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# abstract_identity
|
|
23
|
+
|
|
24
|
+
Identity pipeline for hugpy, merged from the former standalone `char360` and
|
|
25
|
+
`identity-render` dists into one src-layout package with two subpackages:
|
|
26
|
+
|
|
27
|
+
| Subpackage | What it is |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `abstract_identity.char360` | Video → per-character 360° view sets: scenes → person detect+track (YOLO) → face embed+pose (insightface) → identity cluster (agglomerative) → yaw-bin (12×30°) → best-per-bin crops + `manifest.json`. Library + CLI (`char360` console script / `python -m char360`). |
|
|
30
|
+
| `abstract_identity.render` | The identity-render GPU service (`identity-render` console script): reference photos → Hunyuan3D-2mv mesh (+ headless-Blender turntable), plus the char360-powered `video_extract` and `video_characters_glb` job kinds, behind a tokened Flask job API on :9750. |
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
Base (any box — coordinator, dev VM, CI):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install abstract_identity
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Base is deliberately thin and wheels-only: `flask` + `pydantic`, nothing else.
|
|
41
|
+
Every module still imports cleanly with no heavy deps present — heavy imports
|
|
42
|
+
are lazy, inside the stage/job that needs them — so the service can boot
|
|
43
|
+
anywhere and honestly report capabilities as not-ready.
|
|
44
|
+
|
|
45
|
+
ae render host (GPU box): run the installer instead of pip —
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bash /srv/share/projects/hugpy/dev/py/abstract_identity/install/install_identity_render_ae.sh \
|
|
49
|
+
--token <TOKEN>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
It is idempotent (safe to re-run), refuses to run without `--token` or as
|
|
53
|
+
root, and sets up: CUDA torch, the Hunyuan3D-2 checkout + weights, rembg,
|
|
54
|
+
portable Blender 4.2, the char360 heavy deps, an editable install of this
|
|
55
|
+
package into the venv, and the `identity-render.service` systemd **user**
|
|
56
|
+
unit (`ExecStart=venv/bin/python -m abstract_identity.render.service`). See
|
|
57
|
+
the script header and its `PASS:`/`FAIL:`/`NOTE:` output for details. One-time
|
|
58
|
+
firewall opening on ae (verified 2026-07-14):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
sudo ufw allow from 192.168.1.0/24 to any port 9750 proto tcp comment 'identity-render (hugpy central relay)'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Why the heavy deps are an extra, not base
|
|
65
|
+
|
|
66
|
+
`pip install abstract_identity[render]` pulls the char360 detection stack:
|
|
67
|
+
`scenedetect[opencv]`, `ultralytics`, `insightface`, `onnxruntime-gpu==1.18.1`,
|
|
68
|
+
`opencv-python`, `numpy`, `scikit-learn`. They are not in base because they are
|
|
69
|
+
|
|
70
|
+
- **heavy/native/CUDA** — pointless on a coordinator box, and
|
|
71
|
+
- **restrictively licensed** — `ultralytics` is AGPL-3.0 (a deployed
|
|
72
|
+
combination is an AGPL combined work) and the insightface pretrained packs
|
|
73
|
+
(`buffalo_l`) are non-commercial research only. See the THIRD-PARTY
|
|
74
|
+
OBLIGATIONS NOTICE in `LICENSE` before any commercial use or redistribution.
|
|
75
|
+
|
|
76
|
+
**Hunyuan3D-2 is never a pip dep** at all (restrictive Tencent community
|
|
77
|
+
license — excludes EU/UK/South Korea including outputs). It is installed
|
|
78
|
+
out-of-band, only on the render box, by
|
|
79
|
+
`install/install_identity_render_ae.sh` (git checkout + `pip install -e` +
|
|
80
|
+
weight pre-download); the code only reaches `hy3dgen` lazily inside a running
|
|
81
|
+
mesh job. Same for torch, trimesh, and Blender.
|
|
82
|
+
|
|
83
|
+
## Compat shims
|
|
84
|
+
|
|
85
|
+
The old import paths keep working — the wheel ships thin `char360` and
|
|
86
|
+
`identity_render` shim packages that re-export from their new homes:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import char360 # -> abstract_identity.char360
|
|
90
|
+
from char360.pipeline import run_char360
|
|
91
|
+
import identity_render # -> abstract_identity.render
|
|
92
|
+
from identity_render.jobstate import STAGE_DONE
|
|
93
|
+
python -m char360.cli <video> -o <outdir>
|
|
94
|
+
python -m identity_render.service # old systemd ExecStart still boots
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
New code should import `abstract_identity.char360` / `abstract_identity.render`
|
|
98
|
+
directly. The log-capture contract is unchanged: service log records still
|
|
99
|
+
flow through the literal `"identity_render"` logger name.
|
|
100
|
+
|
|
101
|
+
## Service API (summary)
|
|
102
|
+
|
|
103
|
+
Every request, `/health` included, requires `X-Identity-Render-Token: <TOKEN>`
|
|
104
|
+
(401 otherwise). Jobs run one at a time (single GPU, single worker thread).
|
|
105
|
+
|
|
106
|
+
| Method | Path | What |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| GET | `/health` | Service + honest capability report |
|
|
109
|
+
| POST | `/jobs` | Queue a job (`mesh_build`, `turntable`, `mesh_and_turntable`, `video_extract`, `video_characters_glb`); returns `202 {"job_id": ...}` |
|
|
110
|
+
| GET | `/jobs/<job_id>` | `{job_id, status, error?, files?}` |
|
|
111
|
+
| GET | `/jobs/<job_id>/files/<name>` | Fetch an artifact's bytes |
|
|
112
|
+
| DELETE | `/jobs/<job_id>` | Best-effort cleanup of a job's files |
|
|
113
|
+
|
|
114
|
+
Artifacts land under `$IDENTITY_RENDER_HOME/<job_id>/`; nothing is cleaned up
|
|
115
|
+
automatically. Env vars (`IDENTITY_RENDER_TOKEN/PORT/HOME`, `BLENDER_BIN`,
|
|
116
|
+
`FFMPEG_BIN`, `HUNYUAN3D_*`, `HF_HOME`, ...) are written to
|
|
117
|
+
`~/identity-render/env` by the installer. Background removal
|
|
118
|
+
(`mesh_params.remove_background`) is ON by default; originals are never
|
|
119
|
+
modified.
|
|
120
|
+
|
|
121
|
+
On ae: `systemctl --user status identity-render.service`,
|
|
122
|
+
`journalctl --user -u identity-render.service -f`. Code-only update: re-run
|
|
123
|
+
the installer (editable install — a restart alone picks up share changes),
|
|
124
|
+
then `systemctl --user restart identity-render.service`.
|
|
125
|
+
|
|
126
|
+
## char360 CLI
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
char360 <video> -o <outdir> [--stride 8] [--yolo yolov8m.pt] \
|
|
130
|
+
[--min-h-frac 0.15] [--cluster-dist 0.55] [--min-faces 4]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Output: `<outdir>/manifest.json` + `char_NN/view_MM_yaw±DDD.png` crops (bin 0
|
|
134
|
+
frontal, 30° bins CCW; rear bins are `yaw_source=rear_inferred` via
|
|
135
|
+
track-level identity propagation). Missing heavy deps fail loud at stage-run
|
|
136
|
+
time with a `pip install` hint, never at import time.
|
|
137
|
+
|
|
138
|
+
## Tests
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python -m pytest tests/ -q # or: python -m unittest discover tests
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The suite never imports cv2/torch/insightface/sklearn/scenedetect — heavy
|
|
145
|
+
stages are mocked — so it runs on a base install (plus numpy for the pipeline
|
|
146
|
+
test's fixtures).
|
|
147
|
+
|
|
148
|
+
## Publishing
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
cd py/abstract_identity
|
|
152
|
+
python -m build # sdist + wheel
|
|
153
|
+
twine upload dist/* # public PyPI
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Order matters: publish `abstract_identity` **before** publishing
|
|
157
|
+
`abstract_hugpy_dev` 0.1.234, which now base-depends on
|
|
158
|
+
`abstract_identity>=0.1.0`. Version is single-sourced from
|
|
159
|
+
`src/abstract_identity/__init__.py:__version__` — bump it there.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# abstract_identity
|
|
2
|
+
|
|
3
|
+
Identity pipeline for hugpy, merged from the former standalone `char360` and
|
|
4
|
+
`identity-render` dists into one src-layout package with two subpackages:
|
|
5
|
+
|
|
6
|
+
| Subpackage | What it is |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| `abstract_identity.char360` | Video → per-character 360° view sets: scenes → person detect+track (YOLO) → face embed+pose (insightface) → identity cluster (agglomerative) → yaw-bin (12×30°) → best-per-bin crops + `manifest.json`. Library + CLI (`char360` console script / `python -m char360`). |
|
|
9
|
+
| `abstract_identity.render` | The identity-render GPU service (`identity-render` console script): reference photos → Hunyuan3D-2mv mesh (+ headless-Blender turntable), plus the char360-powered `video_extract` and `video_characters_glb` job kinds, behind a tokened Flask job API on :9750. |
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Base (any box — coordinator, dev VM, CI):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install abstract_identity
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Base is deliberately thin and wheels-only: `flask` + `pydantic`, nothing else.
|
|
20
|
+
Every module still imports cleanly with no heavy deps present — heavy imports
|
|
21
|
+
are lazy, inside the stage/job that needs them — so the service can boot
|
|
22
|
+
anywhere and honestly report capabilities as not-ready.
|
|
23
|
+
|
|
24
|
+
ae render host (GPU box): run the installer instead of pip —
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bash /srv/share/projects/hugpy/dev/py/abstract_identity/install/install_identity_render_ae.sh \
|
|
28
|
+
--token <TOKEN>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It is idempotent (safe to re-run), refuses to run without `--token` or as
|
|
32
|
+
root, and sets up: CUDA torch, the Hunyuan3D-2 checkout + weights, rembg,
|
|
33
|
+
portable Blender 4.2, the char360 heavy deps, an editable install of this
|
|
34
|
+
package into the venv, and the `identity-render.service` systemd **user**
|
|
35
|
+
unit (`ExecStart=venv/bin/python -m abstract_identity.render.service`). See
|
|
36
|
+
the script header and its `PASS:`/`FAIL:`/`NOTE:` output for details. One-time
|
|
37
|
+
firewall opening on ae (verified 2026-07-14):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
sudo ufw allow from 192.168.1.0/24 to any port 9750 proto tcp comment 'identity-render (hugpy central relay)'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Why the heavy deps are an extra, not base
|
|
44
|
+
|
|
45
|
+
`pip install abstract_identity[render]` pulls the char360 detection stack:
|
|
46
|
+
`scenedetect[opencv]`, `ultralytics`, `insightface`, `onnxruntime-gpu==1.18.1`,
|
|
47
|
+
`opencv-python`, `numpy`, `scikit-learn`. They are not in base because they are
|
|
48
|
+
|
|
49
|
+
- **heavy/native/CUDA** — pointless on a coordinator box, and
|
|
50
|
+
- **restrictively licensed** — `ultralytics` is AGPL-3.0 (a deployed
|
|
51
|
+
combination is an AGPL combined work) and the insightface pretrained packs
|
|
52
|
+
(`buffalo_l`) are non-commercial research only. See the THIRD-PARTY
|
|
53
|
+
OBLIGATIONS NOTICE in `LICENSE` before any commercial use or redistribution.
|
|
54
|
+
|
|
55
|
+
**Hunyuan3D-2 is never a pip dep** at all (restrictive Tencent community
|
|
56
|
+
license — excludes EU/UK/South Korea including outputs). It is installed
|
|
57
|
+
out-of-band, only on the render box, by
|
|
58
|
+
`install/install_identity_render_ae.sh` (git checkout + `pip install -e` +
|
|
59
|
+
weight pre-download); the code only reaches `hy3dgen` lazily inside a running
|
|
60
|
+
mesh job. Same for torch, trimesh, and Blender.
|
|
61
|
+
|
|
62
|
+
## Compat shims
|
|
63
|
+
|
|
64
|
+
The old import paths keep working — the wheel ships thin `char360` and
|
|
65
|
+
`identity_render` shim packages that re-export from their new homes:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import char360 # -> abstract_identity.char360
|
|
69
|
+
from char360.pipeline import run_char360
|
|
70
|
+
import identity_render # -> abstract_identity.render
|
|
71
|
+
from identity_render.jobstate import STAGE_DONE
|
|
72
|
+
python -m char360.cli <video> -o <outdir>
|
|
73
|
+
python -m identity_render.service # old systemd ExecStart still boots
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
New code should import `abstract_identity.char360` / `abstract_identity.render`
|
|
77
|
+
directly. The log-capture contract is unchanged: service log records still
|
|
78
|
+
flow through the literal `"identity_render"` logger name.
|
|
79
|
+
|
|
80
|
+
## Service API (summary)
|
|
81
|
+
|
|
82
|
+
Every request, `/health` included, requires `X-Identity-Render-Token: <TOKEN>`
|
|
83
|
+
(401 otherwise). Jobs run one at a time (single GPU, single worker thread).
|
|
84
|
+
|
|
85
|
+
| Method | Path | What |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| GET | `/health` | Service + honest capability report |
|
|
88
|
+
| POST | `/jobs` | Queue a job (`mesh_build`, `turntable`, `mesh_and_turntable`, `video_extract`, `video_characters_glb`); returns `202 {"job_id": ...}` |
|
|
89
|
+
| GET | `/jobs/<job_id>` | `{job_id, status, error?, files?}` |
|
|
90
|
+
| GET | `/jobs/<job_id>/files/<name>` | Fetch an artifact's bytes |
|
|
91
|
+
| DELETE | `/jobs/<job_id>` | Best-effort cleanup of a job's files |
|
|
92
|
+
|
|
93
|
+
Artifacts land under `$IDENTITY_RENDER_HOME/<job_id>/`; nothing is cleaned up
|
|
94
|
+
automatically. Env vars (`IDENTITY_RENDER_TOKEN/PORT/HOME`, `BLENDER_BIN`,
|
|
95
|
+
`FFMPEG_BIN`, `HUNYUAN3D_*`, `HF_HOME`, ...) are written to
|
|
96
|
+
`~/identity-render/env` by the installer. Background removal
|
|
97
|
+
(`mesh_params.remove_background`) is ON by default; originals are never
|
|
98
|
+
modified.
|
|
99
|
+
|
|
100
|
+
On ae: `systemctl --user status identity-render.service`,
|
|
101
|
+
`journalctl --user -u identity-render.service -f`. Code-only update: re-run
|
|
102
|
+
the installer (editable install — a restart alone picks up share changes),
|
|
103
|
+
then `systemctl --user restart identity-render.service`.
|
|
104
|
+
|
|
105
|
+
## char360 CLI
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
char360 <video> -o <outdir> [--stride 8] [--yolo yolov8m.pt] \
|
|
109
|
+
[--min-h-frac 0.15] [--cluster-dist 0.55] [--min-faces 4]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Output: `<outdir>/manifest.json` + `char_NN/view_MM_yaw±DDD.png` crops (bin 0
|
|
113
|
+
frontal, 30° bins CCW; rear bins are `yaw_source=rear_inferred` via
|
|
114
|
+
track-level identity propagation). Missing heavy deps fail loud at stage-run
|
|
115
|
+
time with a `pip install` hint, never at import time.
|
|
116
|
+
|
|
117
|
+
## Tests
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python -m pytest tests/ -q # or: python -m unittest discover tests
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The suite never imports cv2/torch/insightface/sklearn/scenedetect — heavy
|
|
124
|
+
stages are mocked — so it runs on a base install (plus numpy for the pipeline
|
|
125
|
+
test's fixtures).
|
|
126
|
+
|
|
127
|
+
## Publishing
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
cd py/abstract_identity
|
|
131
|
+
python -m build # sdist + wheel
|
|
132
|
+
twine upload dist/* # public PyPI
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Order matters: publish `abstract_identity` **before** publishing
|
|
136
|
+
`abstract_hugpy_dev` 0.1.234, which now base-depends on
|
|
137
|
+
`abstract_identity>=0.1.0`. Version is single-sourced from
|
|
138
|
+
`src/abstract_identity/__init__.py:__version__` — bump it there.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# abstract_identity — identity pipeline for hugpy (char360 + identity-render),
|
|
2
|
+
# merged from the former standalone `char360` and `identity-render` dists.
|
|
3
|
+
# Published to public PyPI as its own dist; abstract_hugpy_dev base-depends on it.
|
|
4
|
+
|
|
5
|
+
[build-system]
|
|
6
|
+
requires = ["setuptools>=68"]
|
|
7
|
+
build-backend = "setuptools.build_meta"
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "abstract_identity"
|
|
11
|
+
# Single-sourced from src/abstract_identity/__init__.py:__version__ (read
|
|
12
|
+
# statically by setuptools — see [tool.setuptools.dynamic] below). Bump it THERE.
|
|
13
|
+
dynamic = ["version"]
|
|
14
|
+
description = "Identity pipeline for hugpy: char360 (video -> character face detection/clustering/360-view selection) + render (identity-render GPU service: Hunyuan3D-2mv mesh, Blender turntable, video_extract, video_characters_glb)"
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
requires-python = ">=3.10"
|
|
17
|
+
license = { text = "MIT" }
|
|
18
|
+
authors = [{ name = "putkoff" }]
|
|
19
|
+
|
|
20
|
+
# Base = thin, wheels-only: enough to install, import everything, and boot the
|
|
21
|
+
# service in honest not-ready mode. Every heavy/native/CUDA dep is imported
|
|
22
|
+
# lazily inside the stage or job that needs it and lives in the [render] extra
|
|
23
|
+
# (or is installed out-of-band by install/install_identity_render_ae.sh —
|
|
24
|
+
# torch, hy3dgen/Hunyuan3D-2, trimesh, Blender are NEVER pip deps of this dist).
|
|
25
|
+
dependencies = [
|
|
26
|
+
"flask>=3.0,<4.0",
|
|
27
|
+
"pydantic>=2.0,<3.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
# The char360 detection/clustering stack (GPU box). NOTE the licenses:
|
|
32
|
+
# ultralytics is AGPL-3.0 and insightface's pretrained packs are
|
|
33
|
+
# non-commercial — see LICENSE's THIRD-PARTY OBLIGATIONS NOTICE.
|
|
34
|
+
# onnxruntime-gpu pin matches the cu12 torch builds the installer sets up;
|
|
35
|
+
# on a cu11 box use 1.17.x, or plain onnxruntime for CPU-only.
|
|
36
|
+
render = [
|
|
37
|
+
"scenedetect[opencv]",
|
|
38
|
+
"ultralytics",
|
|
39
|
+
"insightface",
|
|
40
|
+
"onnxruntime-gpu==1.18.1",
|
|
41
|
+
"opencv-python",
|
|
42
|
+
"numpy",
|
|
43
|
+
"scikit-learn",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
char360 = "abstract_identity.char360.cli:main"
|
|
48
|
+
identity-render = "abstract_identity.render.service:main"
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
# char360* and identity_render* are thin compat shims so old import paths
|
|
53
|
+
# (`import char360`, `python -m identity_render.service`) keep working.
|
|
54
|
+
include = ["abstract_identity*", "char360*", "identity_render*"]
|
|
55
|
+
|
|
56
|
+
[tool.setuptools.dynamic]
|
|
57
|
+
# Read statically (AST) out of __init__.py — the package is NOT imported at
|
|
58
|
+
# build time, so this stays cheap and cannot fail on missing build-env deps.
|
|
59
|
+
version = { attr = "abstract_identity.__version__" }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""abstract_identity: identity pipeline for hugpy.
|
|
2
|
+
|
|
3
|
+
Two subpackages, merged from the former standalone `char360` and
|
|
4
|
+
`identity-render` dists:
|
|
5
|
+
|
|
6
|
+
- :mod:`abstract_identity.char360` — video → per-character face
|
|
7
|
+
detection, identity clustering, and 360-degree view selection.
|
|
8
|
+
- :mod:`abstract_identity.render` — the identity-render HTTP service
|
|
9
|
+
(tokened Flask job API): mesh (Hunyuan3D-2mv), turntable (headless
|
|
10
|
+
Blender), video_extract, and video_characters_glb job kinds.
|
|
11
|
+
|
|
12
|
+
Heavy deps stay lazy: importing this package (and every submodule)
|
|
13
|
+
needs only the base install; GPU/native stacks are imported inside the
|
|
14
|
+
stage or job that uses them.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""char360: extract per-character 360-degree view sets from a video.
|
|
2
|
+
|
|
3
|
+
Pipeline: scenes -> person detect+track (YOLO/ultralytics) -> face
|
|
4
|
+
embed+pose (insightface) -> identity cluster (sklearn agglomerative) ->
|
|
5
|
+
yaw-bin (12x30 degrees) -> best-per-bin selection -> crops + manifest.json.
|
|
6
|
+
|
|
7
|
+
Ported from the standalone ``char360_extract.py`` script into this
|
|
8
|
+
modular package (S1 of the char360 feature plan). The algorithm — scoring,
|
|
9
|
+
yaw-binning, and especially the track-level identity-propagation that
|
|
10
|
+
harvests rear/back-of-head views — is unchanged; only the structure is
|
|
11
|
+
new: each stage is its own module with an explicit typed input/output
|
|
12
|
+
(:mod:`char360.stage_scenes`, :mod:`char360.stage_detect`,
|
|
13
|
+
:mod:`char360.stage_faces`, :mod:`char360.stage_cluster`,
|
|
14
|
+
:mod:`char360.stage_select`), and :func:`char360.pipeline.run_char360` is
|
|
15
|
+
the one-shot orchestrator that runs them all with the script's original
|
|
16
|
+
defaults.
|
|
17
|
+
|
|
18
|
+
None of scenedetect/ultralytics/insightface/onnxruntime/scikit-learn/cv2
|
|
19
|
+
are imported at package-import time -- every module here (including this
|
|
20
|
+
one) is safe to import on a box with none of those installed. They are
|
|
21
|
+
only imported inside the specific stage function that needs them (see
|
|
22
|
+
each stage module's docstring), same discipline as
|
|
23
|
+
``identity_render_service/identity_render`` and
|
|
24
|
+
``abstract_hugpy_dev.phone_brick``: pure helpers stay dependency-light and
|
|
25
|
+
unit-testable; heavy deps are paid for only by the box that actually runs
|
|
26
|
+
an extraction, and only at the moment a stage runs, not at import time.
|
|
27
|
+
|
|
28
|
+
This package is a library + CLI only -- no job-bus wrapper, no service
|
|
29
|
+
plumbing. A later slice decides how it's consumed (worker-side runner vs.
|
|
30
|
+
a standalone service in the vein of ``identity_render_service``); it does
|
|
31
|
+
not decide that here.
|
|
32
|
+
"""
|
|
33
|
+
from .schemas import (
|
|
34
|
+
CharacterResult,
|
|
35
|
+
Char360Result,
|
|
36
|
+
Char360Spec,
|
|
37
|
+
Det,
|
|
38
|
+
FaceObs,
|
|
39
|
+
Pick,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"CharacterResult",
|
|
44
|
+
"Char360Result",
|
|
45
|
+
"Char360Spec",
|
|
46
|
+
"Det",
|
|
47
|
+
"FaceObs",
|
|
48
|
+
"Pick",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""``char360`` CLI — the original script's argparse UX, unchanged.
|
|
2
|
+
|
|
3
|
+
Every flag, default, and help string here matches
|
|
4
|
+
``char360_extract.py``'s ``main()`` exactly: ``video`` positional,
|
|
5
|
+
``-o/--outdir`` required, ``--stride`` (default 8), ``--yolo`` (default
|
|
6
|
+
"yolov8m.pt"), ``--min-h-frac`` (default 0.15), ``--cluster-dist``
|
|
7
|
+
(default 0.55), ``--min-faces`` (default 4). Only the body changed: it now
|
|
8
|
+
builds a :class:`char360.schemas.Char360Spec` and calls
|
|
9
|
+
:func:`char360.pipeline.run_char360` instead of inlining the stage calls.
|
|
10
|
+
|
|
11
|
+
This module imports only :mod:`char360.pipeline` (which itself defers all
|
|
12
|
+
heavy deps), so ``python -m char360.cli --help`` works with none of
|
|
13
|
+
scenedetect/ultralytics/insightface/sklearn/cv2/numpy installed — only
|
|
14
|
+
actually running an extraction requires them.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
|
|
20
|
+
from .pipeline import run_char360
|
|
21
|
+
from .schemas import Char360Spec
|
|
22
|
+
|
|
23
|
+
_DESCRIPTION = "extract per-character 360-degree view sets from a video."
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
27
|
+
ap = argparse.ArgumentParser(prog="char360", description=_DESCRIPTION)
|
|
28
|
+
ap.add_argument("video")
|
|
29
|
+
ap.add_argument("-o", "--outdir", required=True)
|
|
30
|
+
ap.add_argument("--stride", type=int, default=8, help="sample every Nth frame (default 8)")
|
|
31
|
+
ap.add_argument("--yolo", default="yolov8m.pt")
|
|
32
|
+
ap.add_argument("--min-h-frac", type=float, default=0.15,
|
|
33
|
+
help="ignore persons shorter than this fraction of frame height")
|
|
34
|
+
ap.add_argument("--cluster-dist", type=float, default=0.55)
|
|
35
|
+
ap.add_argument("--min-faces", type=int, default=4,
|
|
36
|
+
help="min face obs for a cluster to count as a character")
|
|
37
|
+
return ap
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main(argv=None) -> int:
|
|
41
|
+
ap = build_parser()
|
|
42
|
+
args = ap.parse_args(argv)
|
|
43
|
+
|
|
44
|
+
spec = Char360Spec(
|
|
45
|
+
video=args.video,
|
|
46
|
+
outdir=args.outdir,
|
|
47
|
+
stride=args.stride,
|
|
48
|
+
yolo_model=args.yolo,
|
|
49
|
+
min_h_frac=args.min_h_frac,
|
|
50
|
+
cluster_dist=args.cluster_dist,
|
|
51
|
+
min_faces=args.min_faces,
|
|
52
|
+
)
|
|
53
|
+
run_char360(spec)
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Pure geometry/scoring helpers — no heavy deps at import time.
|
|
2
|
+
|
|
3
|
+
``N_BINS``, ``BIN_DEG``, ``REAR_BINS``, ``yaw_to_bin``, and ``iou`` are
|
|
4
|
+
stdlib-only (no numpy, no cv2) so they unit-test on any box, including one
|
|
5
|
+
with none of char360's runtime deps installed. This is the module that S1's
|
|
6
|
+
acceptance criterion — "import char360 cleanly with no heavy deps present"
|
|
7
|
+
— hinges on most directly, so nothing here may gain a module-level
|
|
8
|
+
``import numpy`` / ``import cv2``.
|
|
9
|
+
|
|
10
|
+
``sharpness`` is the one exception: computing a Laplacian variance is
|
|
11
|
+
inherently a cv2 operation on real pixel data, so it lazily imports cv2
|
|
12
|
+
inside the function body (never at module scope) per the same discipline
|
|
13
|
+
``phone_brick`` uses (see that package's ``analyze.py``/``detector.py``
|
|
14
|
+
docstrings) — this module still *imports* cleanly without cv2 installed;
|
|
15
|
+
only *calling* ``sharpness`` requires it.
|
|
16
|
+
|
|
17
|
+
Ported verbatim from ``char360_extract.py``'s top-level constants and
|
|
18
|
+
``iou``/``yaw_to_bin``/``sharpness`` functions — logic unchanged.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Bin geometry (parameterizable: change these two constants, nothing else,
|
|
24
|
+
# to retune bin granularity — do not hardcode 12/30 elsewhere).
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
N_BINS = 12
|
|
27
|
+
BIN_DEG = 360 // N_BINS # 30
|
|
28
|
+
REAR_BINS = (5, 6, 7) # yaw ~ [135..225): back-left, back, back-right
|
|
29
|
+
PERSON_CLS = 0 # COCO person
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def yaw_to_bin(yaw: float) -> int:
|
|
33
|
+
"""yaw in degrees, 0 = facing camera, positive = subject's left turn.
|
|
34
|
+
|
|
35
|
+
Bin 0 centered on 0; bins increase CCW. Pure arithmetic — verbatim from
|
|
36
|
+
the original script.
|
|
37
|
+
"""
|
|
38
|
+
a = (yaw + BIN_DEG / 2) % 360
|
|
39
|
+
return int(a // BIN_DEG)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def iou(a, b) -> float:
|
|
43
|
+
"""Intersection-over-union of two ``(x1, y1, x2, y2)`` boxes.
|
|
44
|
+
|
|
45
|
+
Pure arithmetic — verbatim from the original script.
|
|
46
|
+
"""
|
|
47
|
+
ix1, iy1 = max(a[0], b[0]), max(a[1], b[1])
|
|
48
|
+
ix2, iy2 = min(a[2], b[2]), min(a[3], b[3])
|
|
49
|
+
iw, ih = max(0, ix2 - ix1), max(0, iy2 - iy1)
|
|
50
|
+
inter = iw * ih
|
|
51
|
+
ua = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter
|
|
52
|
+
return inter / ua if ua > 0 else 0.0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def sharpness(img) -> float:
|
|
56
|
+
"""Laplacian-variance sharpness of an image (higher = sharper).
|
|
57
|
+
|
|
58
|
+
Lazily imports cv2 — this is the one geometry helper that cannot be
|
|
59
|
+
made pure (it operates on decoded pixel data), so it keeps the cv2 call
|
|
60
|
+
but defers the import to call time so *importing* this module never
|
|
61
|
+
requires cv2. Verbatim from the original script.
|
|
62
|
+
"""
|
|
63
|
+
import cv2
|
|
64
|
+
|
|
65
|
+
g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
66
|
+
return float(cv2.Laplacian(g, cv2.CV_64F).var())
|