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.
Files changed (39) hide show
  1. abstract_identity-0.1.0/LICENSE +59 -0
  2. abstract_identity-0.1.0/PKG-INFO +159 -0
  3. abstract_identity-0.1.0/README.md +138 -0
  4. abstract_identity-0.1.0/pyproject.toml +59 -0
  5. abstract_identity-0.1.0/setup.cfg +4 -0
  6. abstract_identity-0.1.0/src/abstract_identity/__init__.py +17 -0
  7. abstract_identity-0.1.0/src/abstract_identity/char360/__init__.py +51 -0
  8. abstract_identity-0.1.0/src/abstract_identity/char360/cli.py +58 -0
  9. abstract_identity-0.1.0/src/abstract_identity/char360/geometry.py +66 -0
  10. abstract_identity-0.1.0/src/abstract_identity/char360/pipeline.py +118 -0
  11. abstract_identity-0.1.0/src/abstract_identity/char360/schemas.py +105 -0
  12. abstract_identity-0.1.0/src/abstract_identity/char360/stage_cluster.py +116 -0
  13. abstract_identity-0.1.0/src/abstract_identity/char360/stage_detect.py +75 -0
  14. abstract_identity-0.1.0/src/abstract_identity/char360/stage_faces.py +80 -0
  15. abstract_identity-0.1.0/src/abstract_identity/char360/stage_scenes.py +36 -0
  16. abstract_identity-0.1.0/src/abstract_identity/char360/stage_select.py +105 -0
  17. abstract_identity-0.1.0/src/abstract_identity/render/__init__.py +52 -0
  18. abstract_identity-0.1.0/src/abstract_identity/render/api_models.py +178 -0
  19. abstract_identity-0.1.0/src/abstract_identity/render/blender_turntable.py +207 -0
  20. abstract_identity-0.1.0/src/abstract_identity/render/capabilities.py +152 -0
  21. abstract_identity-0.1.0/src/abstract_identity/render/jobstate.py +280 -0
  22. abstract_identity-0.1.0/src/abstract_identity/render/mesh.py +531 -0
  23. abstract_identity-0.1.0/src/abstract_identity/render/models.py +42 -0
  24. abstract_identity-0.1.0/src/abstract_identity/render/service.py +1026 -0
  25. abstract_identity-0.1.0/src/abstract_identity/render/turntable.py +231 -0
  26. abstract_identity-0.1.0/src/abstract_identity.egg-info/PKG-INFO +159 -0
  27. abstract_identity-0.1.0/src/abstract_identity.egg-info/SOURCES.txt +37 -0
  28. abstract_identity-0.1.0/src/abstract_identity.egg-info/dependency_links.txt +1 -0
  29. abstract_identity-0.1.0/src/abstract_identity.egg-info/entry_points.txt +3 -0
  30. abstract_identity-0.1.0/src/abstract_identity.egg-info/requires.txt +11 -0
  31. abstract_identity-0.1.0/src/abstract_identity.egg-info/top_level.txt +3 -0
  32. abstract_identity-0.1.0/src/char360/__init__.py +36 -0
  33. abstract_identity-0.1.0/src/char360/__main__.py +5 -0
  34. abstract_identity-0.1.0/src/char360/cli.py +10 -0
  35. abstract_identity-0.1.0/src/identity_render/__init__.py +44 -0
  36. abstract_identity-0.1.0/src/identity_render/service.py +9 -0
  37. abstract_identity-0.1.0/tests/test_geometry.py +109 -0
  38. abstract_identity-0.1.0/tests/test_pipeline.py +175 -0
  39. 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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())