Release evaluated Forma3D Tiny 2 continuation
Browse files- ATTRIBUTIONS.json +0 -0
- COMMANDS.md +160 -0
- DATASETS.md +32 -0
- LICENSE +202 -0
- NOTICE +11 -0
- README.md +68 -0
- SHA256SUMS +33 -0
- THIRD_PARTY.md +13 -0
- audit_data.py +59 -0
- config.json +4 -0
- data-audit.json +9 -0
- environment.txt +33 -0
- evaluation.json +40 -0
- licenses/Idol-APACHE-2.0.txt +202 -0
- metrics.jsonl +41 -0
- model.pt +3 -0
- posttrain.sh +56 -0
- preparation.json +36 -0
- prepare_v2.py +42 -0
- publish_v2.py +35 -0
- pyproject.toml +28 -0
- requirements-tested-macos.txt +32 -0
- scripts/prepare_hf.py +463 -0
- scripts/release.py +228 -0
- src/image3d_student/__init__.py +5 -0
- src/image3d_student/cli.py +126 -0
- src/image3d_student/data.py +149 -0
- src/image3d_student/demo.py +31 -0
- src/image3d_student/engine.py +204 -0
- src/image3d_student/model.py +76 -0
- src/image3d_student/splats.py +108 -0
- src/image3d_student/teacher_idol.py +141 -0
- stage_v2.py +159 -0
- training.json +914 -0
ATTRIBUTIONS.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
COMMANDS.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Forma3D Tiny 2 on an M4 MacBook Air with 16 GB RAM
|
| 2 |
+
|
| 3 |
+
This is a proposed weights-only continuation of `Lucasgitguy-co/forma3d-tiny`,
|
| 4 |
+
named `Lucasgitguy-co/forma3d-tiny-2`. It preserves the small FP32 architecture:
|
| 5 |
+
306,321 parameters, grid 16 cubed, decoder width 64. It aims to improve coarse
|
| 6 |
+
proxy reconstruction through more object diversity. No v2 training has been run
|
| 7 |
+
as part of preparing this package, and improvement is not guaranteed.
|
| 8 |
+
|
| 9 |
+
## Run the commands
|
| 10 |
+
|
| 11 |
+
Extract the kit into a local folder. Open macOS Terminal in that folder. Use a
|
| 12 |
+
folder stored locally rather than an offloaded iCloud folder. Keep the Mac on
|
| 13 |
+
power and allow cooling during a sustained run.
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
# If uv is not installed, install it with Homebrew first.
|
| 17 |
+
brew install uv
|
| 18 |
+
|
| 19 |
+
# From the extracted forma3d-tiny-2-kit folder:
|
| 20 |
+
bash posttrain.sh setup
|
| 21 |
+
bash posttrain.sh prepare
|
| 22 |
+
bash posttrain.sh train
|
| 23 |
+
bash posttrain.sh evaluate
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
Run each command only after the previous one succeeds. Setup downloads the source
|
| 27 |
+
and current v1 weights at the fixed revision
|
| 28 |
+
`e80b8d895cc2e405894473abe4ce40b3472eaca7`. It installs Python 3.11 and the
|
| 29 |
+
publisher's tested dependency lock in an isolated environment under
|
| 30 |
+
`~/Library/Application Support/Forma3D-tiny-2/venv`. Setup does not train or upload.
|
| 31 |
+
`image3d doctor` must report Metal available. These public source files execute
|
| 32 |
+
locally during preparation/training; inspect the scripts before running them.
|
| 33 |
+
|
| 34 |
+
Preparation requests 128 eligible Scenario meshes plus 128 CC0 Objaverse meshes,
|
| 35 |
+
four views each. It excludes known v1 asset IDs, paths, source groups, and exact
|
| 36 |
+
mesh hashes. It keeps every view of each related object in the same group. A
|
| 37 |
+
separate 15 percent test split is frozen before training. Validation uses 20
|
| 38 |
+
percent of the remaining development groups. Counts depend on duplicate groups;
|
| 39 |
+
read `data/forma3d-v2/preparation.json` and `v2-audit.json` for the realized counts.
|
| 40 |
+
|
| 41 |
+
The mesh-download budget is 4,096 MiB per preparation invocation, excluding dataset
|
| 42 |
+
metadata and Python packages. It reserves 8 GiB of free disk while fetching meshes.
|
| 43 |
+
Have comfortably more than 12 GiB available before setup and preparation; this is
|
| 44 |
+
a planning allowance, not a measured total. RAM and duration have not been
|
| 45 |
+
benchmarked for this exact v2 recipe. No pretrained teacher is loaded.
|
| 46 |
+
|
| 47 |
+
Training initializes from **v1 model.pt**, rather than its original ellipsoid
|
| 48 |
+
checkpoint. It uses fresh AdamW, batch size 2, learning rate 0.00005, 8,000 optimizer
|
| 49 |
+
steps, weight decay 0.0001, gradient clipping at norm 1, seed 43, and validation
|
| 50 |
+
every 200 steps. This lower learning rate is a proposed conservative setting, not
|
| 51 |
+
a measured optimum. The best validation loss selects `best.pt`.
|
| 52 |
+
|
| 53 |
+
The underlying training command is:
|
| 54 |
+
|
| 55 |
+
```bash
|
| 56 |
+
PY="$HOME/Library/Application Support/Forma3D-tiny-2/venv/bin/python"
|
| 57 |
+
caffeinate -i "$PY" -m image3d_student.cli train \
|
| 58 |
+
--manifest data/forma3d-v2/manifest.jsonl \
|
| 59 |
+
--init-from upstream/model.pt \
|
| 60 |
+
--out runs/forma3d-v2 --device mps --width 64 \
|
| 61 |
+
--steps 8000 --batch-size 2 --lr 0.00005 \
|
| 62 |
+
--eval-every 200 --validation-fraction 0.2 --seed 43
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
Do not substitute grid 32 in this recipe: it increases query cells eightfold and
|
| 66 |
+
changes the evaluation representation. A width change is incompatible with the
|
| 67 |
+
starting learned weights.
|
| 68 |
+
|
| 69 |
+
## Inspect and optionally publish
|
| 70 |
+
|
| 71 |
+
Evaluation compares v1 and the selected v2 checkpoint on **the same new test
|
| 72 |
+
objects**, requiring lower test loss and higher test occupancy IoU, plus lower
|
| 73 |
+
validation loss, a completed run, source separation, and nonempty qualitative
|
| 74 |
+
predictions. It writes local comparisons to `runs/forma3d-v2/inspection/` and
|
| 75 |
+
stages `release/forma3d-tiny-2/` only if the gates pass. Inspect these PNGs.
|
| 76 |
+
The release card uses the actual run measurements and correctly records v1 as
|
| 77 |
+
its parent. No scores are prefilled.
|
| 78 |
+
|
| 79 |
+
The test set is for final assessment. If a release gate fails, do not repeatedly
|
| 80 |
+
tune hyperparameters against that test set. Develop on validation data and reserve
|
| 81 |
+
another genuinely unused object set for a later final claim.
|
| 82 |
+
|
| 83 |
+
To publish after inspection:
|
| 84 |
+
|
| 85 |
+
```bash
|
| 86 |
+
"$HOME/Library/Application Support/Forma3D-tiny-2/venv/bin/hf" auth login
|
| 87 |
+
bash posttrain.sh publish
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
Use your own Hugging Face write token in Terminal, not chat. This command creates
|
| 91 |
+
`Lucasgitguy-co/forma3d-tiny-2` privately, uploads the evaluated package, and makes
|
| 92 |
+
it public after the upload succeeds. It refuses to overwrite an existing repo.
|
| 93 |
+
A failed upload may leave a private partial repo; inspect that repo before
|
| 94 |
+
attempting recovery. Only the staged release folder is uploaded. Raw meshes,
|
| 95 |
+
download caches, credentials, and optimizer state are excluded.
|
| 96 |
+
|
| 97 |
+
## Generate a Gaussian PLY
|
| 98 |
+
|
| 99 |
+
Use a centered object cutout on black. Replace the example path with your image:
|
| 100 |
+
|
| 101 |
+
```bash
|
| 102 |
+
PY="$HOME/Library/Application Support/Forma3D-tiny-2/venv/bin/python"
|
| 103 |
+
"$PY" -m image3d_student.cli generate \
|
| 104 |
+
--checkpoint runs/forma3d-v2/best.pt --image /path/to/your-cutout.png \
|
| 105 |
+
--out results/object.ply --device mps
|
| 106 |
+
"$PY" -m image3d_student.cli preview \
|
| 107 |
+
--ply results/object.ply --out results/preview.png
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
This model outputs Gaussian PLY, not a GLB or triangle mesh. It has no text
|
| 111 |
+
conditioner, automatic background remover, or verified natural-photo training.
|
| 112 |
+
The training images are renders of mesh-derived Gaussian proxies. The metadata
|
| 113 |
+
flag `contains_synthetic: false` means no procedural smoke-test fixtures; it does
|
| 114 |
+
not mean the input images are photographs or that all upstream meshes are human
|
| 115 |
+
made. Scenario includes generated 3D assets.
|
| 116 |
+
|
| 117 |
+
## Recovery and adjustments
|
| 118 |
+
|
| 119 |
+
Dataset preparation can resume: repeat `bash posttrain.sh prepare`. If there are
|
| 120 |
+
not enough eligible assets, edit `--objaverse-shards 16` to 32 in `posttrain.sh`
|
| 121 |
+
and repeat preparation; the upstream parser permits up to 160. If the download
|
| 122 |
+
budget is exhausted, the completed objects remain saved; rerun or increase the
|
| 123 |
+
budget after checking free space. Budgets apply per invocation.
|
| 124 |
+
|
| 125 |
+
Training requires an empty run directory. Ctrl-C saves completed steps to
|
| 126 |
+
`last.pt`, but the trainer does not implement exact optimizer-state resume.
|
| 127 |
+
The provided release pipeline intentionally expects initialization from pinned
|
| 128 |
+
v1, so a run restarted from an interrupted v2 checkpoint requires a separately
|
| 129 |
+
implemented evaluation/release path and is not supported here. Preserve the old
|
| 130 |
+
run and start a new output directory for a fresh attempt.
|
| 131 |
+
|
| 132 |
+
For memory pressure, close other large apps and use batch size 1 in a **fresh**
|
| 133 |
+
run. Keep training/evaluation devices consistent. Do not disable the MPS memory
|
| 134 |
+
limit. Do not promise a training duration: the fanless M4 Air may slow under load.
|
| 135 |
+
If evaluation fails, its diagnostic report remains in the run directory and
|
| 136 |
+
nothing is uploaded. Every evaluation staging attempt needs a fresh release path.
|
| 137 |
+
|
| 138 |
+
## Files and validation
|
| 139 |
+
|
| 140 |
+
`prepare_v2.py` wraps the pinned upstream preprocessing without changing its
|
| 141 |
+
renderer or labels. `audit_data.py` checks known v1 overlap, cache hashes, and
|
| 142 |
+
split separation. `stage_v2.py` reuses upstream evaluation and writes correct v2
|
| 143 |
+
provenance. `publish_v2.py` verifies a complete SHA256 inventory before upload.
|
| 144 |
+
|
| 145 |
+
The kit was syntax-checked; its exclusion and data-audit logic was tested with
|
| 146 |
+
small local fixtures. Flag compatibility was checked against the pinned parser.
|
| 147 |
+
The full dataset download, MPS training, and Hugging Face publication were not
|
| 148 |
+
executed. This is a runnable recipe requiring validation on your Mac, not a
|
| 149 |
+
completed training result.
|
| 150 |
+
|
| 151 |
+
## Primary sources
|
| 152 |
+
|
| 153 |
+
- [Pinned model and code](https://huggingface.co/Lucasgitguy-co/forma3d-tiny/tree/e80b8d895cc2e405894473abe4ce40b3472eaca7)
|
| 154 |
+
- [Original model commands](https://huggingface.co/Lucasgitguy-co/forma3d-tiny/blob/e80b8d895cc2e405894473abe4ce40b3472eaca7/COMMANDS.md)
|
| 155 |
+
- [Scenario dataset](https://huggingface.co/datasets/scenario-labs/3d-collection/tree/55b4c002b4c1529fc48da3dff98516c3069bfa3f)
|
| 156 |
+
- [Objaverse dataset](https://huggingface.co/datasets/allenai/objaverse/tree/556637099bf4fa79ea7b239d0d9c328b8a2e9ac8)
|
| 157 |
+
|
| 158 |
+
Preserve per-asset attributions. This package uses the existing Apache 2.0
|
| 159 |
+
implementation. Its source/data filtering additions are provided under Apache
|
| 160 |
+
2.0; the original NOTICE and third-party/data license terms still apply.
|
DATASETS.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Data selection and attribution
|
| 2 |
+
|
| 3 |
+
Reviewed 2026-11-02. User requested commercial-use-compatible data and exclusion
|
| 4 |
+
of noncommercial datasets. This recipe uses only the sources explicitly marked
|
| 5 |
+
included below. Repository-level labels do not replace individual asset terms.
|
| 6 |
+
|
| 7 |
+
| Source | Status | Reason |
|
| 8 |
+
|---|---|---|
|
| 9 |
+
| [scenario-labs/3d-collection](https://huggingface.co/datasets/scenario-labs/3d-collection) | Included, bounded GLB subset | Explicit CC-BY-4.0 row filter; Hunyuan-labeled models excluded; checksums verified against metadata. Attribution: Scenario; modifications: normalization, surface sampling, Gaussian proxy rendering. |
|
| 10 |
+
| [scenario-labs/3d-collection-hunyuan](https://huggingface.co/datasets/scenario-labs/3d-collection-hunyuan) | Excluded | Dataset expressly prohibits using assets to train or improve AI models. |
|
| 11 |
+
| [3dlg-hcvc/MultiScan](https://huggingface.co/datasets/3dlg-hcvc/MultiScan) | Excluded | CC-BY-NC-4.0, gated scene data. |
|
| 12 |
+
| [huanngzh/3D-Front](https://huggingface.co/datasets/huanngzh/3D-Front) | Excluded | CC-BY-NC-4.0, indoor scenes rather than directly aligned object targets. |
|
| 13 |
+
| [facebook/show3d-dataset](https://huggingface.co/datasets/facebook/show3d-dataset) | Excluded | CC-BY-NC-4.0; interaction/video data needs a separate task-specific pipeline. |
|
| 14 |
+
| [Voxel51/gaussian_splatting](https://huggingface.co/datasets/Voxel51/gaussian_splatting) | Deferred; not used | Card labels Apache-2.0 and describes a few scene reconstructions. This object-centered recipe does not establish camera alignment, object crops, or source-scene rights from the card alone. No importer or download is provided. |
|
| 15 |
+
| [allenai/objaverse](https://huggingface.co/datasets/allenai/objaverse) | Added: CC0 assets only | Dataset compilation is ODC-By; assets have separate licenses. Script accepts only individual `cc0` metadata entries, retains author/title/source links, and excludes animated assets. |
|
| 16 |
+
| [allenai/objaverse-xl](https://huggingface.co/datasets/allenai/objaverse-xl) | Future candidate; not used | Heterogeneous per-file licensing; no blanket commercial-use assumption or importer in this kit. |
|
| 17 |
+
|
| 18 |
+
Pinned revisions:
|
| 19 |
+
|
| 20 |
+
- Scenario: `55b4c002b4c1529fc48da3dff98516c3069bfa3f`
|
| 21 |
+
- Objaverse: `556637099bf4fa79ea7b239d0d9c328b8a2e9ac8`
|
| 22 |
+
- Original model: `1738cdfaf1bb940bf576a96d11b742ce014b9139`
|
| 23 |
+
|
| 24 |
+
The actual asset inventory is generated as `sources.json`, with asset IDs, source
|
| 25 |
+
URLs, authors, licenses, revision IDs and SHA-256 hashes. Published releases copy
|
| 26 |
+
this file to `ATTRIBUTIONS.json`. Keep it with the released model. Rejected assets
|
| 27 |
+
are recorded in `skipped.json`. The model card must not list excluded or merely
|
| 28 |
+
considered datasets as training sources.
|
| 29 |
+
|
| 30 |
+
License references: [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/),
|
| 31 |
+
[CC0](https://creativecommons.org/publicdomain/zero/1.0/),
|
| 32 |
+
[ODC-By](https://opendatacommons.org/licenses/by/1-0/).
|
LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
NOTICE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Forma3D-Tiny v0.2.0, derived from Image3D Student v0.1.0
|
| 2 |
+
Copyright 2026 Lucasgitguy-co.
|
| 3 |
+
|
| 4 |
+
Original project source, documentation and procedural demonstration artifacts are released under Apache License 2.0.
|
| 5 |
+
|
| 6 |
+
The optional Idol integration and Gaussian attribute conventions were developed with reference to
|
| 7 |
+
speridlabs/idol-sdk, revision 4702a7f00850e34b6e83a2170cc123c3e9570744 (Apache-2.0).
|
| 8 |
+
Its architecture is imported as an optional dependency; pretrained teacher weights are not redistributed.
|
| 9 |
+
Adaptations in this project: staged loading, query-chunk attention, explicit real-valued RoPE selection,
|
| 10 |
+
independent student training, coordinate-preserving Gaussian IO, and diagnostic rendering.
|
| 11 |
+
Upstream attribution is retained in licenses/Idol-APACHE-2.0.txt and THIRD_PARTY.md.
|
README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
pipeline_tag: image-to-3d
|
| 4 |
+
base_model: Lucasgitguy-co/forma3d-tiny
|
| 5 |
+
base_model_relation: finetune
|
| 6 |
+
model_name: Forma3D Tiny 2
|
| 7 |
+
datasets:
|
| 8 |
+
- allenai/objaverse
|
| 9 |
+
- scenario-labs/3d-collection
|
| 10 |
+
tags:
|
| 11 |
+
- pytorch
|
| 12 |
+
- gaussian-splatting
|
| 13 |
+
- mesh-proxy-supervision
|
| 14 |
+
- research-prototype
|
| 15 |
+
---
|
| 16 |
+
# Forma3D Tiny 2
|
| 17 |
+
|
| 18 |
+
A 306,321-parameter image-to-Gaussian research model continued from
|
| 19 |
+
Lucasgitguy-co/forma3d-tiny at revision `e80b8d895cc2e405894473abe4ce40b3472eaca7`. The optimizer starts fresh. It uses a
|
| 20 |
+
16 cubed grid and width 64.
|
| 21 |
+
Selected checkpoint step 7800 from 8000 completed steps.
|
| 22 |
+
|
| 23 |
+
## Training
|
| 24 |
+
|
| 25 |
+
See training.json and metrics.jsonl for actual optimizer settings and validation
|
| 26 |
+
history. Inputs are orthographic renders of centered mesh-derived Gaussian proxy
|
| 27 |
+
clouds on black. Targets are isotropic surface samples compressed to one Gaussian
|
| 28 |
+
per occupied grid cell. No pretrained 3D teacher was run. No natural-photo training
|
| 29 |
+
or differentiable rendering is claimed. Asset IDs, known source groups, and exact
|
| 30 |
+
mesh hashes from all 96 known v1 assets were excluded. Near duplicates can remain.
|
| 31 |
+
The separate 15 percent object-group test split does not enter checkpoint selection;
|
| 32 |
+
20 percent of development groups supply validation. Counts are realized counts in
|
| 33 |
+
preparation.json and data-audit.json, not assumed independent asset counts.
|
| 34 |
+
|
| 35 |
+
## Evaluation
|
| 36 |
+
|
| 37 |
+
Both checkpoints were evaluated on the same new held-out proxy data.
|
| 38 |
+
|
| 39 |
+
| Metric | Starting Forma3D Tiny | Selected Forma3D Tiny 2 |
|
| 40 |
+
|---|---:|---:|
|
| 41 |
+
| Mean test loss | 0.616513 | 0.570490 |
|
| 42 |
+
| Mean view occupancy IoU at threshold 0.5 | 0.286327 | 0.327870 |
|
| 43 |
+
|
| 44 |
+
Test groups: 36; views: 156.
|
| 45 |
+
See evaluation.json for hashes and release gates. Metrics are view-averaged over
|
| 46 |
+
proxy renders. They do not establish photo realism, novel-view image quality,
|
| 47 |
+
mesh quality, texture fidelity, gameplay, or general-purpose 3D reconstruction.
|
| 48 |
+
|
| 49 |
+
## Use
|
| 50 |
+
|
| 51 |
+
Install this source with Python 3.11 and `uv pip install ".[data]"` in a virtual
|
| 52 |
+
environment, then run:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
image3d generate --checkpoint model.pt --image your-centered-cutout.png \
|
| 56 |
+
--out results/object.ply --device mps
|
| 57 |
+
image3d preview --ply results/object.ply --out results/preview.png
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
Output is Gaussian PLY. Inputs should match centered objects on black. See
|
| 61 |
+
COMMANDS.md for the complete continuation recipe. This model retains the same
|
| 62 |
+
architecture; no text-prompt conditioning or new mesh decoder was added.
|
| 63 |
+
|
| 64 |
+
## License
|
| 65 |
+
|
| 66 |
+
Publisher source and weights are offered under Apache 2.0. Retain NOTICE,
|
| 67 |
+
THIRD_PARTY.md and ATTRIBUTIONS.json. CC BY Scenario assets and CC0 Objaverse
|
| 68 |
+
assets have their own attribution and license records in DATASETS.md.
|
SHA256SUMS
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
78da3c349a48a35a9377f255731816f6305b9f9bd6a4a1bd2e8558f730b235bc ATTRIBUTIONS.json
|
| 2 |
+
65c732b729f360fc635af852cbf9a806cade0ddd39fb8796d438b93ce6afdedc COMMANDS.md
|
| 3 |
+
4c09bfeb821d41b9da58c9a20c28c4000f40956f4359a04274556436a4ed009f DATASETS.md
|
| 4 |
+
cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 LICENSE
|
| 5 |
+
05f38086626c05b43280173a453858ff2eb4273d63b554c94492c64f09a20ab3 NOTICE
|
| 6 |
+
345b746672faf2fcc79e7936de113ef7c16ce3c2e4d65220c2c7e5a0aaafc083 README.md
|
| 7 |
+
ef7c689f184a7921b02887eeb8d859e8cca77da6d2082999bb19bf29beda61f1 THIRD_PARTY.md
|
| 8 |
+
171d8c1bbcbdff4c7e1e30c139a38ee213de626c25efc94e109eed53a052eb3a audit_data.py
|
| 9 |
+
25d53c6500d9e45aca8a2ce1fe54030e05a0822c554ab24cfcbfeaa631ee9c91 config.json
|
| 10 |
+
91e4d34b8eb0ebffd9ba574780df886dd2e2a27fcc860d4456477e62a71a676e data-audit.json
|
| 11 |
+
c3bb8be792cef70bbb0f2db68a7008d63770ebaf935f79064157705b06630872 environment.txt
|
| 12 |
+
27bdd414748a7e0ceccfce35b7f3c6dbc5398c5c35ef1bfc7f4538d0d437ada0 evaluation.json
|
| 13 |
+
cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 licenses/Idol-APACHE-2.0.txt
|
| 14 |
+
cabfa52ac7194eff926410d8c1d009eae46eb0baf5ec9d8702a16155d503d242 metrics.jsonl
|
| 15 |
+
c051b3f93d874f7c638588c9d0381754d3f954b1cd271121a033b2478d36bb1a model.pt
|
| 16 |
+
ac809ac48df5c9227cf88e723d3be0befd719902924254e577bf82622558994c posttrain.sh
|
| 17 |
+
74f287ea799dbcad69fe61ad6bc176d890883a7ac0c1489a72ab8348d2947439 preparation.json
|
| 18 |
+
c4c25055a96becb3996885e204e9a70e94b706c1d5dd5e58bd2ade038f3e80cd prepare_v2.py
|
| 19 |
+
5f87c57440379f6de332b75450c0a60e7e5c287f2ef4147cc69e1b77e70341ff publish_v2.py
|
| 20 |
+
05b58f19668b5c7778ac9aa9d1aee03eb60771b9850a138533fda97498dd5e46 pyproject.toml
|
| 21 |
+
53dc8f90461d899ed8c15574c3e96f9bf07da4d3ee783a47ec3a3b077b6ee9ca requirements-tested-macos.txt
|
| 22 |
+
03325cb525f9e0562865f8df123678a147db38680b024f0845482dc2d5bc1651 scripts/prepare_hf.py
|
| 23 |
+
96f7df49a214e05b86c2d86023d6500986a4d17b2dfe8b23ab7d213d75375fad scripts/release.py
|
| 24 |
+
c09ce8cb78cd68ae32b618ba49f97508b6ce14bc89e88c5b80cc3e2487bd919c src/image3d_student/__init__.py
|
| 25 |
+
b4be53a864458a84009e91277ff64767dffc6139ca3afcc50526747743722c02 src/image3d_student/cli.py
|
| 26 |
+
d34f9fa50d574764e5e2c1c31c1ca477f2bc4a1887e103c5c47b754f1f1d8c72 src/image3d_student/data.py
|
| 27 |
+
e01d1b42929c993d92cad1bde8944b14c6a8e325fc279d8fd3e9c7287523ad69 src/image3d_student/demo.py
|
| 28 |
+
7335dcc99fcd9cea5c217841376f0ca8c0ca179809b4df34e344bf929d11da51 src/image3d_student/engine.py
|
| 29 |
+
a18005f75a66846446b2574b4f1b085ebde5321e835c4e5b4507e15e812ee5ef src/image3d_student/model.py
|
| 30 |
+
41961fae62d2819c1e3f455e74539a66b8c4f57bedd9fb9379497b3a5764c692 src/image3d_student/splats.py
|
| 31 |
+
2e2439bc0a48c52e32a8e273f879ed7328fdb82632a996ed3e239e0391ca3a59 src/image3d_student/teacher_idol.py
|
| 32 |
+
78f2c8dbf24c7b2eaba4bc203e7b15e2fd8d043c5509b788bd1565773d31478a stage_v2.py
|
| 33 |
+
0148fd204dd45f37ed53746859cf1d953ac49d16aa9cf3bcd77946a4c4f8c613 training.json
|
THIRD_PARTY.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Third-party scope
|
| 2 |
+
|
| 3 |
+
This training kit extends Lucasgitguy-co/image3d-student, Apache-2.0. The original
|
| 4 |
+
checkpoint is downloaded separately. No new trained weights are bundled in the kit.
|
| 5 |
+
|
| 6 |
+
Optional Idol SDK integration is inherited from the original project, pinned to
|
| 7 |
+
`4702a7f00850e34b6e83a2170cc123c3e9570744`; its Apache notice is retained in
|
| 8 |
+
`licenses/Idol-APACHE-2.0.txt`. The Mac recipe does not install or run Idol, Hi3D,
|
| 9 |
+
InstantMesh, Hunyuan, or gated encoders. Their weights are not redistributed.
|
| 10 |
+
|
| 11 |
+
Dependencies retain their respective licenses. Third-party dataset terms are
|
| 12 |
+
documented in DATASETS.md; per-asset attribution is generated during preparation
|
| 13 |
+
and copied into any published release. Code licensing does not relicense datasets.
|
audit_data.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Check published source identities plus split and exact-byte separation."""
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
import sys
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def identity_sets(rows):
|
| 9 |
+
result = {'asset': set(), 'path': set(), 'group': set(), 'sha256': set()}
|
| 10 |
+
for r in rows:
|
| 11 |
+
repo = r['repo']
|
| 12 |
+
if r.get('asset_id'): result['asset'].add((repo, r['asset_id']))
|
| 13 |
+
if r.get('path'): result['path'].add((repo, r['path']))
|
| 14 |
+
for key in ('object_id', 'source_object_id'):
|
| 15 |
+
if r.get(key): result['group'].add((repo, r[key]))
|
| 16 |
+
if r.get('sha256'): result['sha256'].add(r['sha256'])
|
| 17 |
+
return result
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def conflicts(row, blocked):
|
| 21 |
+
current = identity_sets([row])
|
| 22 |
+
return any(current[k] & blocked[k] for k in blocked)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def audit(data, original):
|
| 26 |
+
data, original = Path(data), Path(original)
|
| 27 |
+
old = json.loads(original.read_text())
|
| 28 |
+
new = json.loads((data / 'sources.json').read_text())
|
| 29 |
+
blocked = identity_sets(old)
|
| 30 |
+
if any(conflicts(r, blocked) for r in new):
|
| 31 |
+
raise ValueError('v1 identity or mesh-byte overlap detected')
|
| 32 |
+
dev, test = [[json.loads(s) for s in (data / f).read_text().splitlines() if s.strip()]
|
| 33 |
+
for f in ('manifest.jsonl', 'test-manifest.jsonl')]
|
| 34 |
+
if not dev or not test:
|
| 35 |
+
raise ValueError('Both development and test data must be nonempty')
|
| 36 |
+
for rows in (dev, test):
|
| 37 |
+
for row in rows:
|
| 38 |
+
if conflicts(row['metadata'], blocked):
|
| 39 |
+
raise ValueError('Manifest includes a known v1 source')
|
| 40 |
+
for kind in ('image', 'target'):
|
| 41 |
+
p = (data / row[kind]).resolve()
|
| 42 |
+
if not p.is_relative_to(data.resolve()):
|
| 43 |
+
raise ValueError('Manifest path escapes dataset directory')
|
| 44 |
+
if hashlib.sha256(p.read_bytes()).hexdigest() != row[kind + '_sha256']:
|
| 45 |
+
raise ValueError('Cached file checksum mismatch')
|
| 46 |
+
for getter in (lambda r:r['metadata']['object_id'], lambda r:r['image_sha256'], lambda r:r['target_sha256']):
|
| 47 |
+
if {getter(r) for r in dev} & {getter(r) for r in test}:
|
| 48 |
+
raise ValueError('Development/test overlap detected')
|
| 49 |
+
report = {'known_v1_sources_excluded': len(old), 'new_mesh_assets':len(new),
|
| 50 |
+
'development_samples':len(dev), 'test_samples':len(test),
|
| 51 |
+
'test_object_groups':len({r['metadata']['object_id'] for r in test}),
|
| 52 |
+
'checks':'source IDs, groups, source hashes, cache hashes, split groups and exact bytes',
|
| 53 |
+
'limitation':'Semantic near duplicates and unknown upstream overlap can remain.'}
|
| 54 |
+
(data / 'v2-audit.json').write_text(json.dumps(report, indent=2))
|
| 55 |
+
print(json.dumps(report, indent=2))
|
| 56 |
+
return report
|
| 57 |
+
|
| 58 |
+
if __name__ == '__main__':
|
| 59 |
+
audit(*sys.argv[1:])
|
config.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"grid": 16,
|
| 3 |
+
"width": 64
|
| 4 |
+
}
|
data-audit.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"known_v1_sources_excluded": 96,
|
| 3 |
+
"new_mesh_assets": 256,
|
| 4 |
+
"development_samples": 868,
|
| 5 |
+
"test_samples": 156,
|
| 6 |
+
"test_object_groups": 36,
|
| 7 |
+
"checks": "source IDs, groups, source hashes, cache hashes, split groups and exact bytes",
|
| 8 |
+
"limitation": "Semantic near duplicates and unknown upstream overlap can remain."
|
| 9 |
+
}
|
environment.txt
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
anyio==4.16.0
|
| 2 |
+
click==8.5.0
|
| 3 |
+
filelock==4.2.7
|
| 4 |
+
fsspec==2026.9.0
|
| 5 |
+
h11==0.16.0
|
| 6 |
+
hf-xet==1.8.0
|
| 7 |
+
httpcore2==2.16.0
|
| 8 |
+
httpx2==2.16.0
|
| 9 |
+
huggingface-hub==2.4.0
|
| 10 |
+
idna==3.20
|
| 11 |
+
iniconfig==2.3.1
|
| 12 |
+
jinja2==3.1.6
|
| 13 |
+
markupsafe==3.0.4
|
| 14 |
+
mpmath==1.3.0
|
| 15 |
+
networkx==3.6.1
|
| 16 |
+
numpy==2.4.6
|
| 17 |
+
packaging==26.3
|
| 18 |
+
pillow==13.0.0
|
| 19 |
+
pluggy==1.7.0
|
| 20 |
+
plyfile==1.1.5
|
| 21 |
+
pyarrow==26.0.0
|
| 22 |
+
pygments==2.21.0
|
| 23 |
+
pytest==9.2.0
|
| 24 |
+
pyyaml==6.0.3
|
| 25 |
+
scipy==1.17.1
|
| 26 |
+
setuptools==84.2.1
|
| 27 |
+
sympy==1.14.0
|
| 28 |
+
torch==2.15.0
|
| 29 |
+
tqdm==4.70.1
|
| 30 |
+
trimesh==5.2.0
|
| 31 |
+
truststore==0.10.5
|
| 32 |
+
typing-extensions==4.16.0
|
| 33 |
+
# Local forma3d-tiny 0.2.0 source: Lucasgitguy-co/forma3d-tiny @ e80b8d895cc2e405894473abe4ce40b3472eaca7
|
evaluation.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"checkpoint_sha256": "e0fd24520297a11102412ba8cf5a0fcc1f1103100368d0a438ca63507f581665",
|
| 3 |
+
"base_sha256": "83c065d2a50cf086928542cf8d40dc4416b379e38b9aa61d842dc9106d4b3306",
|
| 4 |
+
"test_manifest_sha256": "5d93ad4fb6c0b9047d06aeae0bd8df76bedcbb3a591d39e00f112c2d7cabfcb1",
|
| 5 |
+
"test_objects": 36,
|
| 6 |
+
"test_samples": 156,
|
| 7 |
+
"baseline_test": {
|
| 8 |
+
"occupancy": 0.45381076557513994,
|
| 9 |
+
"position": 0.09375727468003066,
|
| 10 |
+
"colour": 0.03423368351128048,
|
| 11 |
+
"scale": 0.0023920839437498497,
|
| 12 |
+
"rotation": 9.140115868761383e-05,
|
| 13 |
+
"opacity": 0.00022972543981781293,
|
| 14 |
+
"geometry": 0.0,
|
| 15 |
+
"loss": 0.6165134836083803,
|
| 16 |
+
"occupancy_iou": 0.28632665831500137
|
| 17 |
+
},
|
| 18 |
+
"posttrained_test": {
|
| 19 |
+
"occupancy": 0.4105527498401128,
|
| 20 |
+
"position": 0.093269259071885,
|
| 21 |
+
"colour": 0.033224766163561396,
|
| 22 |
+
"scale": 0.001095225648150051,
|
| 23 |
+
"rotation": 5.7498695502416895e-06,
|
| 24 |
+
"opacity": 0.00010812017677754062,
|
| 25 |
+
"geometry": 0.0,
|
| 26 |
+
"loss": 0.5704897603927515,
|
| 27 |
+
"occupancy_iou": 0.32786990109926617
|
| 28 |
+
},
|
| 29 |
+
"checks": {
|
| 30 |
+
"validation_loss_improved": true,
|
| 31 |
+
"test_loss_improved": true,
|
| 32 |
+
"test_occupancy_iou_improved": true,
|
| 33 |
+
"test_occupancy_iou_at_least_005": true
|
| 34 |
+
},
|
| 35 |
+
"passed": true,
|
| 36 |
+
"scope": "Rendered mesh-proxy evaluation only; does not establish real-photo or mesh reconstruction quality",
|
| 37 |
+
"base_model": "Lucasgitguy-co/forma3d-tiny",
|
| 38 |
+
"base_revision": "e80b8d895cc2e405894473abe4ce40b3472eaca7",
|
| 39 |
+
"published_model_sha256": "c051b3f93d874f7c638588c9d0381754d3f954b1cd271121a033b2478d36bb1a"
|
| 40 |
+
}
|
licenses/Idol-APACHE-2.0.txt
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
metrics.jsonl
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"step": 1, "seconds": 2.226, "train_loss": 0.7118504047393799, "occupancy": 0.5707302093505859, "position": 0.09329640120267868, "colour": 0.02375972457230091, "scale": 0.00139362714253366, "rotation": 0.00013228011084720492, "opacity": 0.00015177681052591652, "geometry": 0.0, "validation": {"occupancy": 0.4518862460222509, "position": 0.0943518355074856, "colour": 0.033528136679928544, "scale": 0.00237332220422104, "rotation": 0.00010222768609714371, "opacity": 0.000220453495194306, "geometry": 0.0, "loss": 0.6137623639570342, "occupancy_iou": 0.2452177435159683}}
|
| 2 |
+
{"step": 200, "seconds": 9.907, "train_loss": 0.6102267503738403, "occupancy": 0.3629882037639618, "position": 0.1010059267282486, "colour": 0.07303024083375931, "scale": 0.001368745812214911, "rotation": 3.1407173082698137e-05, "opacity": 3.210359864169732e-05, "geometry": 0.0, "validation": {"occupancy": 0.4392792442606555, "position": 0.09434974806176291, "colour": 0.03353508860131519, "scale": 0.0019467268908758544, "rotation": 7.532406785281233e-05, "opacity": 0.0001515494514706006, "geometry": 0.0, "loss": 0.6010529238316747, "occupancy_iou": 0.2503086172665159}}
|
| 3 |
+
{"step": 400, "seconds": 17.151, "train_loss": 0.36874061822891235, "occupancy": 0.23984085023403168, "position": 0.09525543451309204, "colour": 0.016660282388329506, "scale": 0.0022550125140696764, "rotation": 5.368763959268108e-05, "opacity": 9.286287968279794e-05, "geometry": 0.0, "validation": {"occupancy": 0.4414640225470066, "position": 0.09427301647762458, "colour": 0.03249235675450311, "scale": 0.0021028166339318788, "rotation": 5.6945074523658455e-05, "opacity": 0.00013960947958366888, "geometry": 0.0, "loss": 0.6010773383908802, "occupancy_iou": 0.24863199333970745}}
|
| 4 |
+
{"step": 600, "seconds": 26.22, "train_loss": 0.6636115312576294, "occupancy": 0.557167649269104, "position": 0.09432454407215118, "colour": 0.005989818833768368, "scale": 0.0010824797209352255, "rotation": 2.2256899683270603e-05, "opacity": 2.9281885872478597e-05, "geometry": 0.0, "validation": {"occupancy": 0.44066500614086784, "position": 0.09423391732076804, "colour": 0.03306895266545729, "scale": 0.0017604425968278923, "rotation": 5.2143132658481285e-05, "opacity": 0.00013570198060885256, "geometry": 0.0, "loss": 0.6013537890381283, "occupancy_iou": 0.26753482399508355}}
|
| 5 |
+
{"step": 800, "seconds": 33.596, "train_loss": 0.709161102771759, "occupancy": 0.5743151307106018, "position": 0.09445250779390335, "colour": 0.019850170239806175, "scale": 0.004002443514764309, "rotation": 3.6538607673719525e-05, "opacity": 0.0002892622142098844, "geometry": 0.0, "validation": {"occupancy": 0.4343525725106398, "position": 0.09426615482403172, "colour": 0.031267428182117224, "scale": 0.0022783598438965986, "rotation": 5.059475087768482e-05, "opacity": 0.00017218820794874369, "geometry": 0.0, "loss": 0.5915586706664827, "occupancy_iou": 0.25203666733577845}}
|
| 6 |
+
{"step": 1000, "seconds": 39.104, "train_loss": 0.478383868932724, "occupancy": 0.3632516860961914, "position": 0.0865977555513382, "colour": 0.014173795469105244, "scale": 0.00096982711693272, "rotation": 5.3548519645119086e-05, "opacity": 8.451077155768871e-05, "geometry": 0.0, "validation": {"occupancy": 0.42965368785791924, "position": 0.09419222561021647, "colour": 0.03181575125264418, "scale": 0.0019403017205250864, "rotation": 4.755257740119608e-05, "opacity": 0.00015420117577680988, "geometry": 0.0, "loss": 0.5878304019570351, "occupancy_iou": 0.2599639593209657}}
|
| 7 |
+
{"step": 1200, "seconds": 43.034, "train_loss": 0.3406582474708557, "occupancy": 0.18636450171470642, "position": 0.09451016783714294, "colour": 0.02972484566271305, "scale": 0.0024863339494913816, "rotation": 2.946602580777835e-05, "opacity": 8.229353261413053e-05, "geometry": 0.0, "validation": {"occupancy": 0.42523774239752027, "position": 0.09409754392173555, "colour": 0.03096643993986719, "scale": 0.0020236761727978267, "rotation": 4.635460904864885e-05, "opacity": 0.0002172642122205717, "geometry": 0.0, "loss": 0.5816924356751971, "occupancy_iou": 0.25514045654692585}}
|
| 8 |
+
{"step": 1400, "seconds": 46.627, "train_loss": 0.5402199029922485, "occupancy": 0.40528902411460876, "position": 0.09599009901285172, "colour": 0.01943030022084713, "scale": 0.0004655070079024881, "rotation": 1.4336257663671859e-05, "opacity": 3.219417340005748e-05, "geometry": 0.0, "validation": {"occupancy": 0.4270147377418147, "position": 0.09427343706289927, "colour": 0.0308504894980514, "scale": 0.0019938124537778397, "rotation": 3.838655797052424e-05, "opacity": 0.00015617236028649334, "geometry": 0.0, "loss": 0.5833485422862901, "occupancy_iou": 0.2615635176499685}}
|
| 9 |
+
{"step": 1600, "seconds": 50.953, "train_loss": 0.4305267035961151, "occupancy": 0.31603896617889404, "position": 0.08737245947122574, "colour": 0.013378691859543324, "scale": 0.0014701660256832838, "rotation": 1.3187261174607556e-05, "opacity": 0.0002095560048473999, "geometry": 0.0, "validation": {"occupancy": 0.4317949041724205, "position": 0.09412531273232566, "colour": 0.03117768721001792, "scale": 0.0017864460874180723, "rotation": 3.7251114496130966e-05, "opacity": 0.00014699193042259947, "geometry": 0.0, "loss": 0.5886049575275845, "occupancy_iou": 0.25071487617161536}}
|
| 10 |
+
{"step": 1800, "seconds": 54.626, "train_loss": 0.6337854266166687, "occupancy": 0.45424965023994446, "position": 0.09028906375169754, "colour": 0.044515978544950485, "scale": 0.0011866714339703321, "rotation": 4.060083665535785e-05, "opacity": 9.203815716318786e-05, "geometry": 0.0, "validation": {"occupancy": 0.42853521431485814, "position": 0.09408483128580782, "colour": 0.030684983537907506, "scale": 0.0018963307412276562, "rotation": 3.8867297088371643e-05, "opacity": 0.00013767477012152085, "geometry": 0.0, "loss": 0.5843212089604801, "occupancy_iou": 0.26198453576200537}}
|
| 11 |
+
{"step": 2000, "seconds": 58.299, "train_loss": 0.4911178946495056, "occupancy": 0.3473135232925415, "position": 0.09329692274332047, "colour": 0.02516920119524002, "scale": 0.0009039711439982057, "rotation": 1.2793458154192194e-05, "opacity": 7.736129919067025e-05, "geometry": 0.0, "validation": {"occupancy": 0.4308644436299801, "position": 0.09404570534825325, "colour": 0.03126794581443796, "scale": 0.001913158190695362, "rotation": 3.8796406690582645e-05, "opacity": 0.000161123547008578, "geometry": 0.0, "loss": 0.5878023596273528, "occupancy_iou": 0.26729344308583275}}
|
| 12 |
+
{"step": 2200, "seconds": 62.423, "train_loss": 0.49473559856414795, "occupancy": 0.39490997791290283, "position": 0.09424534440040588, "colour": 0.0026967048179358244, "scale": 0.001267485087737441, "rotation": 2.2478219761978835e-05, "opacity": 5.78755461901892e-05, "geometry": -0.0, "validation": {"occupancy": 0.42324845335549777, "position": 0.09407929811212752, "colour": 0.030908581189255022, "scale": 0.001734178906190209, "rotation": 2.9401834669645017e-05, "opacity": 0.00013252917611781515, "geometry": 0.0, "loss": 0.5794538059168391, "occupancy_iou": 0.26256160982367066}}
|
| 13 |
+
{"step": 2400, "seconds": 66.234, "train_loss": 0.5510505437850952, "occupancy": 0.3978094458580017, "position": 0.09358540922403336, "colour": 0.029724668711423874, "scale": 0.001308542676270008, "rotation": 2.4227834728662856e-05, "opacity": 7.308263593586162e-05, "geometry": 0.0, "validation": {"occupancy": 0.4158506722913848, "position": 0.0939772955659363, "colour": 0.03368225522701525, "scale": 0.0016470129029282057, "rotation": 3.0246070622928934e-05, "opacity": 0.0001484720899245278, "geometry": 0.0, "loss": 0.5775086745619774, "occupancy_iou": 0.26686940054512687}}
|
| 14 |
+
{"step": 2600, "seconds": 69.678, "train_loss": 0.5078648924827576, "occupancy": 0.4009518027305603, "position": 0.09459930658340454, "colour": 0.006019631866365671, "scale": 0.00107279303483665, "rotation": 2.1116564312251285e-05, "opacity": 0.00016517315816599876, "geometry": 0.0, "validation": {"occupancy": 0.4215878984166516, "position": 0.0941099154866404, "colour": 0.030351898755503093, "scale": 0.002087128064886201, "rotation": 3.372074203424644e-05, "opacity": 0.0001546000525498433, "geometry": 0.0, "loss": 0.5767682999372482, "occupancy_iou": 0.26642631568635505}}
|
| 15 |
+
{"step": 2800, "seconds": 73.936, "train_loss": 0.5524424314498901, "occupancy": 0.45049646496772766, "position": 0.08658759295940399, "colour": 0.00761753274127841, "scale": 0.0006960614700801671, "rotation": 2.2003196136211045e-05, "opacity": 5.14989260409493e-05, "geometry": 0.0, "validation": {"occupancy": 0.41681441391507784, "position": 0.09399882223871019, "colour": 0.030614230668612033, "scale": 0.0018567735597672355, "rotation": 2.7371126323790427e-05, "opacity": 0.0001512159914960244, "geometry": 0.0, "loss": 0.5723813250660896, "occupancy_iou": 0.2721572133091589}}
|
| 16 |
+
{"step": 3000, "seconds": 77.85, "train_loss": 0.47857388854026794, "occupancy": 0.32654982805252075, "position": 0.0985688716173172, "colour": 0.026567257940769196, "scale": 0.0018755781929939985, "rotation": 1.0815320820256602e-05, "opacity": 0.00013207261508796364, "geometry": 0.0, "validation": {"occupancy": 0.4110039444433318, "position": 0.09397411880393823, "colour": 0.03051911240812236, "scale": 0.0018324703995151342, "rotation": 2.6987933491303314e-05, "opacity": 0.00015093530021456698, "geometry": 0.0, "loss": 0.5663531646132469, "occupancy_iou": 0.26360250164030324}}
|
| 17 |
+
{"step": 3200, "seconds": 82.182, "train_loss": 0.5254156589508057, "occupancy": 0.3551676273345947, "position": 0.09234742075204849, "colour": 0.038872893899679184, "scale": 0.0010524942772462964, "rotation": 1.719896317808889e-05, "opacity": 4.7851517592789605e-05, "geometry": 0.0, "validation": {"occupancy": 0.4228256072435114, "position": 0.09399812126325237, "colour": 0.0307030633609328, "scale": 0.0016338224047406887, "rotation": 2.9768100080218574e-05, "opacity": 0.0001273464747100661, "geometry": 0.0, "loss": 0.5785235651665264, "occupancy_iou": 0.2846426756017738}}
|
| 18 |
+
{"step": 3400, "seconds": 87.183, "train_loss": 0.4364839792251587, "occupancy": 0.3158171772956848, "position": 0.09616897255182266, "colour": 0.012188517488539219, "scale": 0.0007229623151943088, "rotation": 1.076408716471633e-05, "opacity": 4.740383883472532e-05, "geometry": -0.0, "validation": {"occupancy": 0.4094659690227773, "position": 0.09397175216840373, "colour": 0.03179174260003492, "scale": 0.001720584945966645, "rotation": 2.1549060081977384e-05, "opacity": 0.00012737575575051273, "geometry": 0.0, "loss": 0.5673228002256817, "occupancy_iou": 0.2701680043194857}}
|
| 19 |
+
{"step": 3600, "seconds": 91.249, "train_loss": 0.49549779295921326, "occupancy": 0.34523046016693115, "position": 0.08855268359184265, "colour": 0.030767172574996948, "scale": 0.0006670482689514756, "rotation": 2.0991266865166835e-05, "opacity": 0.00011153484228998423, "geometry": 0.0, "validation": {"occupancy": 0.41149804004364543, "position": 0.09396016506685151, "colour": 0.031038793543120845, "scale": 0.0016648945602355525, "rotation": 2.195297413436492e-05, "opacity": 0.00015779983397502898, "geometry": 0.0, "loss": 0.5678622765673531, "occupancy_iou": 0.27745231499688494}}
|
| 20 |
+
{"step": 3800, "seconds": 95.935, "train_loss": 0.45338520407676697, "occupancy": 0.2673555910587311, "position": 0.09842601418495178, "colour": 0.043742645531892776, "scale": 0.0007127065327949822, "rotation": 1.5850951967877336e-05, "opacity": 4.546590207610279e-05, "geometry": 0.0, "validation": {"occupancy": 0.4185141497188144, "position": 0.09392823518978224, "colour": 0.030558659026994266, "scale": 0.0016994932399635825, "rotation": 2.1529511650442144e-05, "opacity": 0.0001690847278748859, "geometry": 0.0, "loss": 0.5739008857972092, "occupancy_iou": 0.28688534133964116}}
|
| 21 |
+
{"step": 4000, "seconds": 99.234, "train_loss": 0.557262122631073, "occupancy": 0.41419923305511475, "position": 0.1001129150390625, "colour": 0.021421322599053383, "scale": 0.0008524729055352509, "rotation": 8.493616405758075e-06, "opacity": 2.1267600459395908e-05, "geometry": 0.0, "validation": {"occupancy": 0.4045540635784467, "position": 0.09390666542781724, "colour": 0.030530943222887193, "scale": 0.0015784881295985542, "rotation": 1.953120323605617e-05, "opacity": 0.00014177847832696797, "geometry": 0.0, "loss": 0.5598241958353255, "occupancy_iou": 0.27434742067837053}}
|
| 22 |
+
{"step": 4200, "seconds": 103.185, "train_loss": 0.6259624361991882, "occupancy": 0.3919915556907654, "position": 0.09183152765035629, "colour": 0.07100241631269455, "scale": 0.0011714748106896877, "rotation": 1.2915494153276086e-05, "opacity": 1.6088246411527507e-05, "geometry": 0.0, "validation": {"occupancy": 0.40893695917394424, "position": 0.09394071011079683, "colour": 0.031102682888417298, "scale": 0.0016566178788909585, "rotation": 2.15832443422591e-05, "opacity": 0.0001230442787598602, "geometry": 0.0, "loss": 0.5653738988770379, "occupancy_iou": 0.2786702503139774}}
|
| 23 |
+
{"step": 4400, "seconds": 107.226, "train_loss": 0.6597180366516113, "occupancy": 0.4113847017288208, "position": 0.09409773349761963, "colour": 0.07705115526914597, "scale": 0.0007961074006743729, "rotation": 1.7088928871089593e-05, "opacity": 5.190773299545981e-05, "geometry": 0.0, "validation": {"occupancy": 0.40834645562701755, "position": 0.09393176059756014, "colour": 0.03059931747460117, "scale": 0.001580410228537706, "rotation": 1.9219402352316846e-05, "opacity": 0.00011282105082904713, "geometry": 0.0, "loss": 0.5637496335638894, "occupancy_iou": 0.28006390310409995}}
|
| 24 |
+
{"step": 4600, "seconds": 111.711, "train_loss": 0.469024121761322, "occupancy": 0.25593164563179016, "position": 0.09084726125001907, "colour": 0.06106134131550789, "scale": 0.0006797174573875964, "rotation": 2.7496407710714266e-05, "opacity": 5.18183151143603e-05, "geometry": -0.0, "validation": {"occupancy": 0.4052144769165251, "position": 0.09393522159920799, "colour": 0.03133135766385951, "scale": 0.0014389909564569178, "rotation": 1.879761312137715e-05, "opacity": 0.00015053203554392288, "geometry": 0.0, "loss": 0.5621087267994881, "occupancy_iou": 0.2752341095565094}}
|
| 25 |
+
{"step": 4800, "seconds": 118.367, "train_loss": 0.4867999255657196, "occupancy": 0.3837716579437256, "position": 0.09228860586881638, "colour": 0.005275615490972996, "scale": 0.0010446624364703894, "rotation": 2.072399547614623e-05, "opacity": 8.185891783796251e-05, "geometry": 0.0, "validation": {"occupancy": 0.4076536825133695, "position": 0.09397915200226836, "colour": 0.030453578963836967, "scale": 0.0015870701287187532, "rotation": 1.7866719237089355e-05, "opacity": 0.00014790454023467546, "geometry": 0.0, "loss": 0.5628483845955796, "occupancy_iou": 0.2745954434387386}}
|
| 26 |
+
{"step": 5000, "seconds": 126.698, "train_loss": 0.5417315363883972, "occupancy": 0.3388543426990509, "position": 0.0951608270406723, "colour": 0.053798988461494446, "scale": 0.0008181341690942645, "rotation": 1.32617697090609e-05, "opacity": 3.5288030630908906e-05, "geometry": 0.0, "validation": {"occupancy": 0.40258289178212486, "position": 0.09389113494091564, "colour": 0.030977234217183045, "scale": 0.0015717818333844966, "rotation": 1.7154699538776994e-05, "opacity": 0.00012996606800596864, "geometry": 0.0, "loss": 0.5587173554632399, "occupancy_iou": 0.278588663517601}}
|
| 27 |
+
{"step": 5200, "seconds": 136.791, "train_loss": 0.4986310303211212, "occupancy": 0.39215320348739624, "position": 0.09362593293190002, "colour": 0.0063873776234686375, "scale": 0.0005547901382669806, "rotation": 1.3301656508701853e-05, "opacity": 2.0309231331339106e-05, "geometry": 0.0, "validation": {"occupancy": 0.40034629594948556, "position": 0.0939166457288795, "colour": 0.0316381377083922, "scale": 0.0013407457458217524, "rotation": 1.5355411326507036e-05, "opacity": 9.307003478069318e-05, "geometry": 0.0, "loss": 0.5577678953607877, "occupancy_iou": 0.28428437142736385}}
|
| 28 |
+
{"step": 5400, "seconds": 147.671, "train_loss": 0.5063523054122925, "occupancy": 0.37988996505737305, "position": 0.09676655381917953, "colour": 0.014790681190788746, "scale": 0.000719499948900193, "rotation": 1.0230924090137705e-05, "opacity": 4.14770474890247e-05, "geometry": 0.0, "validation": {"occupancy": 0.406855690644847, "position": 0.094014210999012, "colour": 0.030220710265954645, "scale": 0.0014052534357890383, "rotation": 1.3710945721514792e-05, "opacity": 0.00012029510183108667, "geometry": 0.0, "loss": 0.5615735145906607, "occupancy_iou": 0.2955626644194126}}
|
| 29 |
+
{"step": 5600, "seconds": 158.543, "train_loss": 0.4793046712875366, "occupancy": 0.31926438212394714, "position": 0.09948806464672089, "colour": 0.030066760256886482, "scale": 0.0016735820099711418, "rotation": 3.527296030370053e-06, "opacity": 0.00025098398327827454, "geometry": 0.0, "validation": {"occupancy": 0.4096036763654815, "position": 0.0940052620238728, "colour": 0.030777640448003594, "scale": 0.0017037645920128044, "rotation": 1.2175207258577655e-05, "opacity": 9.953662891146249e-05, "geometry": 0.0, "loss": 0.5654353524247805, "occupancy_iou": 0.29424606011145643}}
|
| 30 |
+
{"step": 5800, "seconds": 169.06, "train_loss": 0.5487256646156311, "occupancy": 0.4067179262638092, "position": 0.08838052302598953, "colour": 0.026750687509775162, "scale": 0.0008282989729195833, "rotation": 6.190408385009505e-06, "opacity": 4.235693268128671e-05, "geometry": 0.0, "validation": {"occupancy": 0.4043719614545504, "position": 0.0938253727224138, "colour": 0.03032789862126164, "scale": 0.0014254298906760394, "rotation": 1.2701534732665701e-05, "opacity": 0.0001225527391397918, "geometry": 0.0, "loss": 0.5591194975707267, "occupancy_iou": 0.28225652354045044}}
|
| 31 |
+
{"step": 6000, "seconds": 178.463, "train_loss": 0.7709605693817139, "occupancy": 0.6320652365684509, "position": 0.10362475365400314, "colour": 0.017524851486086845, "scale": 0.0013552490854635835, "rotation": 1.1179777175129857e-05, "opacity": 8.425069972872734e-05, "geometry": -0.0, "validation": {"occupancy": 0.40365004216631256, "position": 0.09382433742284775, "colour": 0.030227608712933337, "scale": 0.0013439785144227143, "rotation": 1.3150008344887789e-05, "opacity": 0.00011520726076115453, "geometry": 0.0, "loss": 0.5581805180344317, "occupancy_iou": 0.289635895875593}}
|
| 32 |
+
{"step": 6200, "seconds": 188.743, "train_loss": 0.4993472695350647, "occupancy": 0.3844979405403137, "position": 0.0935329720377922, "colour": 0.010608968324959278, "scale": 0.0006603781948797405, "rotation": 5.173298632143997e-06, "opacity": 3.185215246048756e-05, "geometry": 0.0, "validation": {"occupancy": 0.39319998257690003, "position": 0.093823934180869, "colour": 0.03010780170184767, "scale": 0.0013350714630279172, "rotation": 1.0495416969054835e-05, "opacity": 8.886457434932203e-05, "geometry": 0.0, "loss": 0.5474629404644171, "occupancy_iou": 0.28641757027556497}}
|
| 33 |
+
{"step": 6400, "seconds": 198.688, "train_loss": 0.4189801514148712, "occupancy": 0.2826651334762573, "position": 0.09773959964513779, "colour": 0.019192663952708244, "scale": 0.0007928699487820268, "rotation": 7.733145139354747e-06, "opacity": 0.00011002655810443684, "geometry": -0.0, "validation": {"occupancy": 0.4008911026020845, "position": 0.09377840674585766, "colour": 0.030930962648967074, "scale": 0.0013093957671016041, "rotation": 1.0876036795555894e-05, "opacity": 9.485433270128851e-05, "geometry": 0.0, "loss": 0.5567583110597398, "occupancy_iou": 0.2885369167766637}}
|
| 34 |
+
{"step": 6600, "seconds": 207.607, "train_loss": 0.5430406928062439, "occupancy": 0.40279972553253174, "position": 0.0946291834115982, "colour": 0.02274857833981514, "scale": 0.00046750501496717334, "rotation": 3.858339823636925e-06, "opacity": 6.750710599590093e-05, "geometry": 0.0, "validation": {"occupancy": 0.3953916298846404, "position": 0.09387033122281234, "colour": 0.029838704274799156, "scale": 0.001385571199514541, "rotation": 1.1168679466461779e-05, "opacity": 0.00012542731134696322, "geometry": 0.0, "loss": 0.549204474604792, "occupancy_iou": 0.29114852864295243}}
|
| 35 |
+
{"step": 6800, "seconds": 217.242, "train_loss": 0.42363783717155457, "occupancy": 0.3095293641090393, "position": 0.07285671681165695, "colour": 0.020542502403259277, "scale": 0.0011710914550349116, "rotation": 6.151235993456794e-06, "opacity": 4.898784391116351e-05, "geometry": 0.0, "validation": {"occupancy": 0.3905657473537657, "position": 0.09377354122698307, "colour": 0.030009023019293736, "scale": 0.0013380147245091697, "rotation": 9.336464271806714e-06, "opacity": 9.46656024098047e-05, "geometry": 0.0, "loss": 0.5445867335630787, "occupancy_iou": 0.28344639133041105}}
|
| 36 |
+
{"step": 7000, "seconds": 226.267, "train_loss": 0.3683210015296936, "occupancy": 0.2541470527648926, "position": 0.0920795351266861, "colour": 0.011000635102391243, "scale": 0.00044253666419535875, "rotation": 8.873310434864834e-06, "opacity": 4.801345494342968e-05, "geometry": -0.0, "validation": {"occupancy": 0.38817514338427117, "position": 0.09379746023979452, "colour": 0.030292692528908243, "scale": 0.0013068688216864958, "rotation": 9.695720117027425e-06, "opacity": 0.00010475461714476195, "geometry": 0.0, "loss": 0.5427944011158413, "occupancy_iou": 0.28759642725603446}}
|
| 37 |
+
{"step": 7200, "seconds": 236.128, "train_loss": 0.5596817135810852, "occupancy": 0.4352259635925293, "position": 0.09255465120077133, "colour": 0.015911228954792023, "scale": 0.0004941098159179091, "rotation": 5.525091637537116e-06, "opacity": 2.8721509806928225e-05, "geometry": 0.0, "validation": {"occupancy": 0.3937354901184638, "position": 0.09382040939397282, "colour": 0.029826522268639463, "scale": 0.0012667472010232611, "rotation": 9.296284631293223e-06, "opacity": 0.00012043797467716406, "geometry": 0.0, "loss": 0.5474569886922837, "occupancy_iou": 0.2878855074652367}}
|
| 38 |
+
{"step": 7400, "seconds": 245.65, "train_loss": 0.4626730978488922, "occupancy": 0.3224141299724579, "position": 0.09337978810071945, "colour": 0.02338295243680477, "scale": 0.0007951300358399749, "rotation": 3.5533946629584534e-06, "opacity": 3.340890907566063e-05, "geometry": 0.0, "validation": {"occupancy": 0.3877126969397068, "position": 0.0938801984820101, "colour": 0.030403074356986002, "scale": 0.00118190329085337, "rotation": 7.93976443813133e-06, "opacity": 9.667932798341402e-05, "geometry": 0.0, "loss": 0.5426147082613574, "occupancy_iou": 0.2788545132097271}}
|
| 39 |
+
{"step": 7600, "seconds": 255.315, "train_loss": 0.3988212049007416, "occupancy": 0.27856338024139404, "position": 0.0932641476392746, "colour": 0.013418144546449184, "scale": 0.0009028896456584334, "rotation": 3.5423447570792632e-06, "opacity": 6.672731979051605e-05, "geometry": 0.0, "validation": {"occupancy": 0.3907282780028052, "position": 0.09378029331564904, "colour": 0.03218639403607489, "scale": 0.0011547023844387797, "rotation": 8.038774109309087e-06, "opacity": 9.087624960102403e-05, "geometry": 0.0, "loss": 0.5490885093808174, "occupancy_iou": 0.29333951137959957}}
|
| 40 |
+
{"step": 7800, "seconds": 264.733, "train_loss": 0.43082183599472046, "occupancy": 0.3145463764667511, "position": 0.0849197506904602, "colour": 0.015606578439474106, "scale": 0.0010680370032787323, "rotation": 3.66301378562639e-06, "opacity": 3.537732845870778e-05, "geometry": -0.0, "validation": {"occupancy": 0.3866305548283789, "position": 0.09380314374963443, "colour": 0.02985135869692183, "scale": 0.0011766884298089685, "rotation": 7.77004361667524e-06, "opacity": 0.00013522217013309677, "geometry": 0.0, "loss": 0.5403900866707166, "occupancy_iou": 0.2947235658764839}}
|
| 41 |
+
{"step": 8000, "seconds": 274.229, "train_loss": 0.4784364402294159, "occupancy": 0.35019439458847046, "position": 0.09252085536718369, "colour": 0.01773099973797798, "scale": 0.0023379335179924965, "rotation": 4.9215291255677585e-06, "opacity": 2.4880051569198258e-05, "geometry": -0.0, "validation": {"occupancy": 0.3940945436557134, "position": 0.09373144254916244, "colour": 0.031111064605000946, "scale": 0.0012088158114541632, "rotation": 7.342030562540458e-06, "opacity": 9.256118964913184e-05, "geometry": 0.0, "loss": 0.5502622892459234, "occupancy_iou": 0.2887043085984058}}
|
model.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c051b3f93d874f7c638588c9d0381754d3f954b1cd271121a033b2478d36bb1a
|
| 3 |
+
size 1753931
|
posttrain.sh
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Source and weights are pinned; no training or upload starts during setup.
|
| 3 |
+
set -euo pipefail
|
| 4 |
+
cd "$(dirname "$0")"
|
| 5 |
+
STAGE="${1:-help}"
|
| 6 |
+
RUNTIME="${FORMA3D_V2_RUNTIME:-$HOME/Library/Application Support/Forma3D-tiny-2/venv}"
|
| 7 |
+
PY="$RUNTIME/bin/python"
|
| 8 |
+
export HF_HUB_DISABLE_TELEMETRY=1
|
| 9 |
+
export OMP_NUM_THREADS=4
|
| 10 |
+
export VECLIB_MAXIMUM_THREADS=4
|
| 11 |
+
REV=e80b8d895cc2e405894473abe4ce40b3472eaca7
|
| 12 |
+
case "$STAGE" in
|
| 13 |
+
setup)
|
| 14 |
+
if ! command -v uv >/dev/null; then
|
| 15 |
+
echo 'Install uv first: brew install uv'; exit 1
|
| 16 |
+
fi
|
| 17 |
+
[ -x "$PY" ] || uv venv --python 3.11 "$RUNTIME"
|
| 18 |
+
uv pip install --python "$PY" 'huggingface_hub[cli]>=0.34'
|
| 19 |
+
"$RUNTIME/bin/hf" download Lucasgitguy-co/forma3d-tiny --revision "$REV" --local-dir upstream
|
| 20 |
+
uv pip install --python "$PY" -r upstream/requirements-tested-macos.txt
|
| 21 |
+
uv pip install --python "$PY" './upstream[data]'
|
| 22 |
+
"$PY" -m image3d_student.cli doctor
|
| 23 |
+
"$PY" -c 'import torch; assert torch.backends.mps.is_available(), "Metal unavailable; run in macOS Terminal"'
|
| 24 |
+
mkdir -p runs
|
| 25 |
+
uv pip freeze --python "$PY" > runs/environment.txt
|
| 26 |
+
;;
|
| 27 |
+
prepare)
|
| 28 |
+
[ -x "$PY" ] || { echo 'Run bash posttrain.sh setup first'; exit 1; }
|
| 29 |
+
"$PY" prepare_v2.py --out data/forma3d-v2 --cache data/downloads \
|
| 30 |
+
--scenario 128 --objaverse 128 --objaverse-shards 16 --resume \
|
| 31 |
+
--grid 16 --views 4 --surface-points 4096 --seed 43 \
|
| 32 |
+
--max-file-mb 16 --max-download-mb 4096 --reserve-gb 8
|
| 33 |
+
;;
|
| 34 |
+
train)
|
| 35 |
+
[ -x "$PY" ] || { echo 'Run bash posttrain.sh setup first'; exit 1; }
|
| 36 |
+
"$PY" audit_data.py data/forma3d-v2 upstream/ATTRIBUTIONS.json
|
| 37 |
+
# A fresh output directory is required. Ctrl-C saves last.pt, not optimizer resume.
|
| 38 |
+
caffeinate -i "$PY" -m image3d_student.cli train \
|
| 39 |
+
--manifest data/forma3d-v2/manifest.jsonl --init-from upstream/model.pt \
|
| 40 |
+
--out runs/forma3d-v2 --device mps --width 64 \
|
| 41 |
+
--steps 8000 --batch-size 2 --lr 0.00005 \
|
| 42 |
+
--eval-every 200 --validation-fraction 0.2 --seed 43
|
| 43 |
+
;;
|
| 44 |
+
evaluate)
|
| 45 |
+
"$PY" stage_v2.py --run runs/forma3d-v2 --data data/forma3d-v2 \
|
| 46 |
+
--base upstream/model.pt --release release/forma3d-tiny-2 --device mps
|
| 47 |
+
;;
|
| 48 |
+
publish)
|
| 49 |
+
# Explicit command only. Creates a private repo; makes it public after upload.
|
| 50 |
+
"$PY" publish_v2.py release/forma3d-tiny-2
|
| 51 |
+
;;
|
| 52 |
+
*)
|
| 53 |
+
echo 'Run stages in order: setup, prepare, train, evaluate, publish (optional).'
|
| 54 |
+
echo 'Example: bash posttrain.sh setup'
|
| 55 |
+
;;
|
| 56 |
+
esac
|
preparation.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"counts": {
|
| 3 |
+
"scenario-labs/3d-collection": {
|
| 4 |
+
"requested": 128,
|
| 5 |
+
"prepared": 128
|
| 6 |
+
},
|
| 7 |
+
"allenai/objaverse": {
|
| 8 |
+
"requested": 128,
|
| 9 |
+
"prepared": 128
|
| 10 |
+
}
|
| 11 |
+
},
|
| 12 |
+
"samples": 1024,
|
| 13 |
+
"objects": 256,
|
| 14 |
+
"distinct_groups": 239,
|
| 15 |
+
"settings": {
|
| 16 |
+
"scenario": 128,
|
| 17 |
+
"objaverse": 128,
|
| 18 |
+
"objaverse_shards": 16,
|
| 19 |
+
"resume": true,
|
| 20 |
+
"grid": 16,
|
| 21 |
+
"views": 4,
|
| 22 |
+
"surface_points": 4096,
|
| 23 |
+
"seed": 43,
|
| 24 |
+
"max_file_mb": 16,
|
| 25 |
+
"max_download_mb": 4096,
|
| 26 |
+
"reserve_gb": 8.0
|
| 27 |
+
},
|
| 28 |
+
"rendered_proxy_data": true,
|
| 29 |
+
"excluded": [
|
| 30 |
+
"scenario-labs/3d-collection-hunyuan",
|
| 31 |
+
"3dlg-hcvc/MultiScan",
|
| 32 |
+
"huanngzh/3D-Front",
|
| 33 |
+
"facebook/show3d-dataset",
|
| 34 |
+
"Voxel51/gaussian_splatting"
|
| 35 |
+
]
|
| 36 |
+
}
|
prepare_v2.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Use pinned upstream preprocessing while excluding every known v1 asset/group."""
|
| 2 |
+
import importlib.util
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from audit_data import audit, identity_sets, conflicts
|
| 6 |
+
|
| 7 |
+
ROOT = Path(__file__).resolve().parent
|
| 8 |
+
|
| 9 |
+
def main():
|
| 10 |
+
upstream = ROOT / 'upstream'
|
| 11 |
+
old = json.loads((upstream / 'ATTRIBUTIONS.json').read_text())
|
| 12 |
+
blocked = identity_sets(old)
|
| 13 |
+
spec = importlib.util.spec_from_file_location('pinned_prepare', upstream / 'scripts/prepare_hf.py')
|
| 14 |
+
module = importlib.util.module_from_spec(spec)
|
| 15 |
+
spec.loader.exec_module(module)
|
| 16 |
+
for name in ('scenario_candidates', 'objaverse_candidates'):
|
| 17 |
+
original = getattr(module, name)
|
| 18 |
+
def filtered(*args, _original=original, **kwargs):
|
| 19 |
+
return [r for r in _original(*args, **kwargs) if not conflicts(r, blocked)]
|
| 20 |
+
setattr(module, name, filtered)
|
| 21 |
+
downloader = module.Downloader
|
| 22 |
+
class FilteredDownloader(downloader):
|
| 23 |
+
def get(self, *args, **kwargs):
|
| 24 |
+
path = super().get(*args, **kwargs)
|
| 25 |
+
if module.digest(path) in blocked['sha256']:
|
| 26 |
+
raise ValueError('Excluded byte-identical v1 mesh')
|
| 27 |
+
return path
|
| 28 |
+
module.Downloader = FilteredDownloader
|
| 29 |
+
original_prepare = module.prepare
|
| 30 |
+
def checked_prepare(args):
|
| 31 |
+
out = Path(args.out)
|
| 32 |
+
if (out / 'sources.json').exists():
|
| 33 |
+
for row in json.loads((out / 'sources.json').read_text()):
|
| 34 |
+
if conflicts(row, blocked):
|
| 35 |
+
raise ValueError('Existing dataset overlaps v1; choose a fresh --out directory')
|
| 36 |
+
original_prepare(args)
|
| 37 |
+
audit(out, upstream / 'ATTRIBUTIONS.json')
|
| 38 |
+
module.prepare = checked_prepare
|
| 39 |
+
module.main()
|
| 40 |
+
|
| 41 |
+
if __name__ == '__main__':
|
| 42 |
+
main()
|
publish_v2.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Explicit optional upload; creates a private repository before making it public."""
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
import sys
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from huggingface_hub import HfApi
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
root = Path(sys.argv[1])
|
| 10 |
+
report = json.loads((root/'evaluation.json').read_text())
|
| 11 |
+
if not report.get('passed') or not all(report['checks'].values()):
|
| 12 |
+
raise ValueError('Evaluation gate failed')
|
| 13 |
+
lines = (root/'SHA256SUMS').read_text().splitlines()
|
| 14 |
+
expected = {}
|
| 15 |
+
for line in lines:
|
| 16 |
+
digest, filename = line.split(' ',1)
|
| 17 |
+
path = (root/filename).resolve()
|
| 18 |
+
if not path.is_relative_to(root.resolve()):
|
| 19 |
+
raise ValueError('Invalid checksum path')
|
| 20 |
+
expected[filename] = digest
|
| 21 |
+
if hashlib.sha256(path.read_bytes()).hexdigest() != digest:
|
| 22 |
+
raise ValueError('Changed release file: '+filename)
|
| 23 |
+
actual = {str(p.relative_to(root)) for p in root.rglob('*') if p.is_file() and p.name != 'SHA256SUMS'}
|
| 24 |
+
if actual != set(expected):
|
| 25 |
+
raise ValueError('Unlisted or missing release files')
|
| 26 |
+
repo = 'Lucasgitguy-co/forma3d-tiny-2'
|
| 27 |
+
api = HfApi()
|
| 28 |
+
api.create_repo(repo,repo_type='model',private=True,exist_ok=False)
|
| 29 |
+
api.upload_folder(repo_id=repo,repo_type='model',folder_path=str(root),
|
| 30 |
+
commit_message='Release evaluated Forma3D Tiny 2 continuation')
|
| 31 |
+
api.update_repo_settings(repo_id=repo,repo_type='model',private=False)
|
| 32 |
+
print('Published https://huggingface.co/'+repo)
|
| 33 |
+
|
| 34 |
+
if __name__ == '__main__':
|
| 35 |
+
main()
|
pyproject.toml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=77"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "forma3d-tiny-2"
|
| 7 |
+
version = "0.3.0"
|
| 8 |
+
license = "Apache-2.0"
|
| 9 |
+
license-files = ["LICENSE", "NOTICE", "THIRD_PARTY.md", "licenses/*.txt"]
|
| 10 |
+
readme = "README.md"
|
| 11 |
+
authors = [{name = "Lucasgitguy-co"}]
|
| 12 |
+
description = "Experimental local image-to-Gaussian student with cached teacher supervision"
|
| 13 |
+
requires-python = ">=3.11,<3.14"
|
| 14 |
+
dependencies = ["torch>=2.5", "numpy>=1.26", "pillow>=10", "plyfile>=1", "trimesh>=4", "scipy>=1.11"]
|
| 15 |
+
|
| 16 |
+
[project.optional-dependencies]
|
| 17 |
+
test = ["pytest>=8"]
|
| 18 |
+
data = ["huggingface_hub>=0.34", "pyarrow>=17"]
|
| 19 |
+
teacher = ["idol @ git+https://github.com/speridlabs/idol-sdk.git@4702a7f00850e34b6e83a2170cc123c3e9570744"]
|
| 20 |
+
|
| 21 |
+
[project.scripts]
|
| 22 |
+
image3d = "image3d_student.cli:main"
|
| 23 |
+
|
| 24 |
+
[tool.setuptools.packages.find]
|
| 25 |
+
where = ["src"]
|
| 26 |
+
|
| 27 |
+
[tool.pytest.ini_options]
|
| 28 |
+
testpaths = ["tests"]
|
requirements-tested-macos.txt
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
anyio==4.16.0
|
| 2 |
+
click==8.5.0
|
| 3 |
+
filelock==4.2.7
|
| 4 |
+
fsspec==2026.9.0
|
| 5 |
+
h11==0.16.0
|
| 6 |
+
hf-xet==1.8.0
|
| 7 |
+
httpcore2==2.16.0
|
| 8 |
+
httpx2==2.16.0
|
| 9 |
+
huggingface-hub==2.4.0
|
| 10 |
+
idna==3.20
|
| 11 |
+
iniconfig==2.3.1
|
| 12 |
+
jinja2==3.1.6
|
| 13 |
+
markupsafe==3.0.4
|
| 14 |
+
mpmath==1.3.0
|
| 15 |
+
networkx==3.6.1
|
| 16 |
+
numpy==2.4.6
|
| 17 |
+
packaging==26.3
|
| 18 |
+
pillow==13.0.0
|
| 19 |
+
pluggy==1.7.0
|
| 20 |
+
plyfile==1.1.5
|
| 21 |
+
pyarrow==26.0.0
|
| 22 |
+
pygments==2.21.0
|
| 23 |
+
pytest==9.2.0
|
| 24 |
+
pyyaml==6.0.3
|
| 25 |
+
scipy==1.17.1
|
| 26 |
+
setuptools==84.2.1
|
| 27 |
+
sympy==1.14.0
|
| 28 |
+
torch==2.15.0
|
| 29 |
+
tqdm==4.70.1
|
| 30 |
+
trimesh==5.2.0
|
| 31 |
+
truststore==0.10.5
|
| 32 |
+
typing-extensions==4.16.0
|
scripts/prepare_hf.py
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Prepare paired, canonical rendered images and coarse Gaussian targets.
|
| 2 |
+
|
| 3 |
+
These are mesh-derived proxy targets, NOT pretrained-teacher outputs or photos.
|
| 4 |
+
Only public, explicitly allowed assets are downloaded. Source meshes stay local.
|
| 5 |
+
"""
|
| 6 |
+
import argparse
|
| 7 |
+
import gzip
|
| 8 |
+
import hashlib
|
| 9 |
+
import json
|
| 10 |
+
import random
|
| 11 |
+
import shutil
|
| 12 |
+
import time
|
| 13 |
+
import urllib.error
|
| 14 |
+
import urllib.request
|
| 15 |
+
from pathlib import Path, PurePosixPath
|
| 16 |
+
|
| 17 |
+
import numpy as np
|
| 18 |
+
import pyarrow.parquet as pq
|
| 19 |
+
import trimesh
|
| 20 |
+
from huggingface_hub import hf_hub_download
|
| 21 |
+
|
| 22 |
+
from image3d_student.data import cache_sample, splat_targets
|
| 23 |
+
from image3d_student.splats import Splats, preview
|
| 24 |
+
from image3d_student.engine import split_rows
|
| 25 |
+
|
| 26 |
+
SCENARIO = ("scenario-labs/3d-collection", "55b4c002b4c1529fc48da3dff98516c3069bfa3f")
|
| 27 |
+
OBJAVERSE = ("allenai/objaverse", "556637099bf4fa79ea7b239d0d9c328b8a2e9ac8")
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def digest(path):
|
| 31 |
+
h = hashlib.sha256()
|
| 32 |
+
with Path(path).open("rb") as f:
|
| 33 |
+
for block in iter(lambda: f.read(1024 * 1024), b""):
|
| 34 |
+
h.update(block)
|
| 35 |
+
return h.hexdigest()
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class Downloader:
|
| 39 |
+
def __init__(self, root, max_file_mb, max_total_mb, reserve_gb):
|
| 40 |
+
self.root = Path(root)
|
| 41 |
+
self.root.mkdir(parents=True, exist_ok=True)
|
| 42 |
+
self.max_file = int(max_file_mb * 1024**2)
|
| 43 |
+
self.max_total = int(max_total_mb * 1024**2)
|
| 44 |
+
self.reserve = int(reserve_gb * 1024**3)
|
| 45 |
+
self.total = 0
|
| 46 |
+
|
| 47 |
+
def get(self, repo, revision, filename, expected=None):
|
| 48 |
+
parts = PurePosixPath(filename)
|
| 49 |
+
if parts.is_absolute() or ".." in parts.parts or not filename.endswith(".glb"):
|
| 50 |
+
raise ValueError("Only relative GLB paths are allowed")
|
| 51 |
+
out = self.root / repo.replace("/", "--") / revision / filename
|
| 52 |
+
if out.exists():
|
| 53 |
+
if expected and digest(out) != expected:
|
| 54 |
+
raise ValueError("Cached source checksum mismatch")
|
| 55 |
+
if out.stat().st_size > self.max_file:
|
| 56 |
+
raise ValueError("Cached asset exceeds per-file limit")
|
| 57 |
+
return out
|
| 58 |
+
if self.total >= self.max_total:
|
| 59 |
+
raise RuntimeError("Download budget exhausted")
|
| 60 |
+
out.parent.mkdir(parents=True, exist_ok=True)
|
| 61 |
+
url = f"https://huggingface.co/datasets/{repo}/resolve/{revision}/{filename}"
|
| 62 |
+
for attempt in range(4):
|
| 63 |
+
tmp = out.with_suffix(".part")
|
| 64 |
+
try:
|
| 65 |
+
# Public URL only: credentials are never forwarded to asset hosts.
|
| 66 |
+
with urllib.request.urlopen(url, timeout=60) as response, tmp.open("wb") as f:
|
| 67 |
+
length = int(response.headers.get("Content-Length", 0))
|
| 68 |
+
if length > self.max_file:
|
| 69 |
+
raise ValueError("Asset exceeds per-file limit")
|
| 70 |
+
count = 0
|
| 71 |
+
while block := response.read(1024 * 1024):
|
| 72 |
+
count += len(block)
|
| 73 |
+
self.total += len(block)
|
| 74 |
+
if count > self.max_file:
|
| 75 |
+
raise ValueError("Asset exceeds per-file limit")
|
| 76 |
+
if self.total > self.max_total:
|
| 77 |
+
raise RuntimeError("Download budget exhausted")
|
| 78 |
+
if shutil.disk_usage(self.root).free < self.reserve + len(block):
|
| 79 |
+
raise RuntimeError("Free disk space below reserve")
|
| 80 |
+
f.write(block)
|
| 81 |
+
if expected and digest(tmp) != expected:
|
| 82 |
+
raise ValueError("Source checksum mismatch")
|
| 83 |
+
tmp.replace(out)
|
| 84 |
+
return out
|
| 85 |
+
except urllib.error.HTTPError as exc:
|
| 86 |
+
if exc.code not in (429, 500, 502, 503, 504) or attempt == 3:
|
| 87 |
+
raise
|
| 88 |
+
time.sleep(min(2**attempt, 8))
|
| 89 |
+
finally:
|
| 90 |
+
tmp.unlink(missing_ok=True)
|
| 91 |
+
raise RuntimeError("Download retries exhausted")
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def scenario_candidates(cache, seed):
|
| 95 |
+
repo, revision = SCENARIO
|
| 96 |
+
path = hf_hub_download(repo, "data/train-00000-of-00001.parquet", repo_type="dataset",
|
| 97 |
+
revision=revision, local_dir=Path(cache) / "metadata/scenario")
|
| 98 |
+
rows = pq.read_table(path, columns=["id", "license", "model", "inputs", "outputs"]).to_pylist()
|
| 99 |
+
# Connected components keep alternate meshes generated from shared inputs together.
|
| 100 |
+
parent = {}
|
| 101 |
+
|
| 102 |
+
def find(x):
|
| 103 |
+
parent.setdefault(x, x)
|
| 104 |
+
if parent[x] != x:
|
| 105 |
+
parent[x] = find(parent[x])
|
| 106 |
+
return parent[x]
|
| 107 |
+
|
| 108 |
+
candidates = []
|
| 109 |
+
for row in rows:
|
| 110 |
+
if row["license"] != "cc-by-4.0" or "hunyuan" in (row["model"] or "").lower():
|
| 111 |
+
continue
|
| 112 |
+
assets = (row.get("inputs") or {}).get("assets") or []
|
| 113 |
+
keys = ["input:" + (a.get("sha256") or a["path"]) for a in assets if a.get("sha256") or a.get("path")]
|
| 114 |
+
keys += ["output:" + o["sha256"] for o in row["outputs"] if o.get("sha256")]
|
| 115 |
+
keys += [row["id"]]
|
| 116 |
+
for key in keys[1:]:
|
| 117 |
+
parent[find(key)] = find(keys[0])
|
| 118 |
+
for output in row["outputs"]:
|
| 119 |
+
if output.get("format") == "glb":
|
| 120 |
+
candidates.append({"repo": repo, "revision": revision, "path": output["path"],
|
| 121 |
+
"sha256": output.get("sha256"), "asset_id": row["id"], "license": "cc-by-4.0",
|
| 122 |
+
"author": "Scenario", "title": row["id"], "source_model": row["model"],
|
| 123 |
+
"source_url": f"https://huggingface.co/datasets/{repo}", "group_key": keys[0]})
|
| 124 |
+
break
|
| 125 |
+
for row in candidates:
|
| 126 |
+
row["object_id"] = "scenario-" + hashlib.sha256(find(row.pop("group_key")).encode()).hexdigest()[:20]
|
| 127 |
+
candidates.sort(key=lambda r: r["asset_id"])
|
| 128 |
+
random.Random(seed).shuffle(candidates)
|
| 129 |
+
return candidates
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def objaverse_candidates(cache, seed, shards):
|
| 133 |
+
repo, revision = OBJAVERSE
|
| 134 |
+
candidates = []
|
| 135 |
+
for shard in range(shards):
|
| 136 |
+
part = f"000-{shard:03d}"
|
| 137 |
+
path = hf_hub_download(repo, f"metadata/{part}.json.gz", repo_type="dataset", revision=revision,
|
| 138 |
+
local_dir=Path(cache) / "metadata/objaverse")
|
| 139 |
+
with gzip.open(path) as f:
|
| 140 |
+
records = json.load(f)
|
| 141 |
+
for uid, row in records.items():
|
| 142 |
+
# Start with the more conservative CC0 subset; no NC/SA/unknown assets.
|
| 143 |
+
if row.get("license") != "cc0" or row.get("isAgeRestricted") or row.get("animationCount", 0):
|
| 144 |
+
continue
|
| 145 |
+
candidates.append({"repo": repo, "revision": revision, "path": f"glbs/{part}/{uid}.glb",
|
| 146 |
+
"asset_id": uid, "object_id": "objaverse-" + uid, "license": "cc0-1.0",
|
| 147 |
+
"author": row.get("user", {}).get("displayName", "Unknown"), "title": row.get("name", uid),
|
| 148 |
+
"source_url": row.get("viewerUrl", f"https://sketchfab.com/3d-models/{uid}")})
|
| 149 |
+
candidates.sort(key=lambda r: r["asset_id"])
|
| 150 |
+
random.Random(seed).shuffle(candidates)
|
| 151 |
+
return candidates
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def mesh_splats(path, count, grid, seed):
|
| 155 |
+
scene = trimesh.load_scene(path, process=False)
|
| 156 |
+
meshes = []
|
| 157 |
+
for node in scene.graph.nodes_geometry:
|
| 158 |
+
matrix, name = scene.graph[node]
|
| 159 |
+
mesh = scene.geometry[name].copy()
|
| 160 |
+
if not isinstance(mesh, trimesh.Trimesh) or not len(mesh.faces):
|
| 161 |
+
continue
|
| 162 |
+
mesh.apply_transform(matrix)
|
| 163 |
+
meshes.append(mesh)
|
| 164 |
+
if not meshes or sum(len(m.faces) for m in meshes) > 500_000:
|
| 165 |
+
raise ValueError("Missing mesh or more than 500,000 triangles")
|
| 166 |
+
bounds = np.array([m.bounds for m in meshes])
|
| 167 |
+
low, high = bounds[:, 0].min(axis=0), bounds[:, 1].max(axis=0)
|
| 168 |
+
extent = float((high - low).max())
|
| 169 |
+
if not np.isfinite(bounds).all() or extent < 1e-8:
|
| 170 |
+
raise ValueError("Degenerate mesh")
|
| 171 |
+
center = (low + high) / 2
|
| 172 |
+
factor = .9 / extent
|
| 173 |
+
areas = np.array([m.area for m in meshes])
|
| 174 |
+
if not np.isfinite(areas).all() or areas.sum() <= 0:
|
| 175 |
+
raise ValueError("Invalid surface area")
|
| 176 |
+
counts = np.random.default_rng(seed).multinomial(count, areas / areas.sum())
|
| 177 |
+
points, colours = [], []
|
| 178 |
+
for mesh, n in zip(meshes, counts):
|
| 179 |
+
if not n:
|
| 180 |
+
continue
|
| 181 |
+
xyz, faces = trimesh.sample.sample_surface(mesh, int(n), seed=seed)
|
| 182 |
+
bary = trimesh.triangles.points_to_barycentric(mesh.triangles[faces], xyz)
|
| 183 |
+
vis = mesh.visual
|
| 184 |
+
if vis.kind == "texture" and vis.uv is not None:
|
| 185 |
+
uv = (vis.uv[mesh.faces[faces]] * bary[:, :, None]).sum(axis=1)
|
| 186 |
+
sampled = np.asarray(vis.material.to_color(uv))
|
| 187 |
+
if sampled.ndim == 0:
|
| 188 |
+
raise ValueError("Material has no usable texture or base colour")
|
| 189 |
+
# Untextured PBR materials can return one constant RGBA value.
|
| 190 |
+
if sampled.ndim == 1:
|
| 191 |
+
sampled = np.broadcast_to(sampled, (n, len(sampled)))
|
| 192 |
+
colour = sampled[:, :3] / 255.
|
| 193 |
+
elif vis.kind == "vertex":
|
| 194 |
+
colour = (vis.vertex_colors[mesh.faces[faces], :3] * bary[:, :, None]).sum(axis=1) / 255.
|
| 195 |
+
elif vis.kind == "face":
|
| 196 |
+
colour = vis.face_colors[faces, :3] / 255.
|
| 197 |
+
else:
|
| 198 |
+
colour = np.full((n, 3), .65)
|
| 199 |
+
points.append((xyz - center) * factor)
|
| 200 |
+
colours.append(colour)
|
| 201 |
+
xyz = np.concatenate(points).astype(np.float32)
|
| 202 |
+
rgb = np.concatenate(colours).astype(np.float32)
|
| 203 |
+
n = len(xyz)
|
| 204 |
+
splats = Splats(xyz, rgb, np.full((n, 3), .6 / grid),
|
| 205 |
+
np.tile([1, 0, 0, 0], (n, 1)), np.full((n, 1), .9))
|
| 206 |
+
transform = np.eye(4)
|
| 207 |
+
transform[:3, :3] *= factor
|
| 208 |
+
transform[:3, 3] = -center * factor
|
| 209 |
+
return splats, transform.tolist()
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def save_progress(out, args, rows, provenance, failures):
|
| 213 |
+
state = {"version": 1, "settings": vars(args), "rows": rows,
|
| 214 |
+
"sources": provenance, "skipped": failures}
|
| 215 |
+
temporary = out / "progress.json.tmp"
|
| 216 |
+
temporary.write_text(json.dumps(state))
|
| 217 |
+
temporary.replace(out / "progress.json")
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def check_resume_settings(previous, args):
|
| 221 |
+
for key in ("grid", "views", "surface_points", "seed", "scenario", "objaverse"):
|
| 222 |
+
if previous[key] != getattr(args, key):
|
| 223 |
+
raise ValueError(f"Cannot change {key} while resuming; use a new dataset directory")
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def recover_legacy(out, args):
|
| 227 |
+
"""Recover the old writer's completed files without rendering or downloading.
|
| 228 |
+
|
| 229 |
+
Old runs saved sources and arrays, but lost manifest rows on insufficient
|
| 230 |
+
candidates. Recreate those rows and verify targets against cached GLBs.
|
| 231 |
+
"""
|
| 232 |
+
previous = json.loads((out / "preparation.json").read_text())["settings"]
|
| 233 |
+
check_resume_settings(previous, args)
|
| 234 |
+
provenance = json.loads((out / "sources.json").read_text())
|
| 235 |
+
failures = json.loads((out / "skipped.json").read_text())
|
| 236 |
+
rows = []
|
| 237 |
+
for index, item in enumerate(provenance):
|
| 238 |
+
path = Path(args.cache) / item["repo"].replace("/", "--") / item["revision"] / item["path"]
|
| 239 |
+
if not path.is_file() or digest(path) != item["sha256"]:
|
| 240 |
+
raise ValueError(f"Missing or corrupt cached source for {item['asset_id']}")
|
| 241 |
+
splats, transform = mesh_splats(path, args.surface_points, args.grid,
|
| 242 |
+
int(item["sha256"][:8], 16) ^ args.seed)
|
| 243 |
+
stem = hashlib.sha256((item["repo"] + item["asset_id"]).encode()).hexdigest()[:20]
|
| 244 |
+
for view in range(args.views):
|
| 245 |
+
sample_id = f"{stem}-v{view}"
|
| 246 |
+
image, target = f"images/{sample_id}.png", f"targets/{sample_id}.npz"
|
| 247 |
+
yaw = 2 * np.pi * view / args.views
|
| 248 |
+
c, s = np.cos(yaw), np.sin(yaw)
|
| 249 |
+
rotation = np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]], np.float32)
|
| 250 |
+
rotated = Splats(splats.xyz @ rotation.T, splats.rgb, splats.scale, splats.quat, splats.opacity)
|
| 251 |
+
expected = splat_targets(rotated, args.grid)
|
| 252 |
+
with np.load(out / target, allow_pickle=False) as cached:
|
| 253 |
+
if set(cached.files) != set(expected):
|
| 254 |
+
raise ValueError(f"Target fields differ for {sample_id}")
|
| 255 |
+
for key, value in expected.items():
|
| 256 |
+
if not np.array_equal(cached[key], value):
|
| 257 |
+
raise ValueError(f"Recovered target differs for {sample_id}: {key}")
|
| 258 |
+
from PIL import Image
|
| 259 |
+
with Image.open(out / image) as check:
|
| 260 |
+
check.verify()
|
| 261 |
+
rows.append({"id": sample_id, "image": image, "target": target,
|
| 262 |
+
"source": "mesh-proxy:" + item["repo"], "grid": args.grid,
|
| 263 |
+
"teachers": {"appearance": "mesh-proxy:" + item["repo"], "coarse": None, "fine": None},
|
| 264 |
+
"teacher_gaussians": len(splats.xyz), "retained_gaussians": int(expected["occ"].sum()),
|
| 265 |
+
"geometry_transforms": {"coarse": None, "fine": None},
|
| 266 |
+
"image_sha256": digest(out / image), "target_sha256": digest(out / target),
|
| 267 |
+
"metadata": {**item, "normalization_matrix": transform, "yaw_radians": float(yaw),
|
| 268 |
+
"render": "orthographic Gaussian proxy, camera +z",
|
| 269 |
+
"target": "isotropic surface-sampled mesh proxy; not a pretrained teacher"}})
|
| 270 |
+
if (index + 1) % 10 == 0 or index + 1 == len(provenance):
|
| 271 |
+
print(json.dumps({"recovered_objects": index + 1, "existing_objects": len(provenance)}), flush=True)
|
| 272 |
+
save_progress(out, args, rows, provenance, failures)
|
| 273 |
+
return rows, provenance, failures
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def load_progress(out, args):
|
| 277 |
+
if not (out / "progress.json").exists():
|
| 278 |
+
return recover_legacy(out, args)
|
| 279 |
+
state = json.loads((out / "progress.json").read_text())
|
| 280 |
+
if state.get("version") != 1:
|
| 281 |
+
raise ValueError("Unsupported preparation progress version")
|
| 282 |
+
check_resume_settings(state["settings"], args)
|
| 283 |
+
for row in state["rows"]:
|
| 284 |
+
for key in ("image", "target"):
|
| 285 |
+
path = (out / row[key]).resolve()
|
| 286 |
+
if not path.is_relative_to(out.resolve()) or digest(path) != row[key + "_sha256"]:
|
| 287 |
+
raise ValueError(f"Prepared file checksum mismatch: {row['id']} {key}")
|
| 288 |
+
return state["rows"], state["sources"], state["skipped"]
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
def prune_invisible(out, rows, provenance, failures):
|
| 292 |
+
from PIL import Image
|
| 293 |
+
bad = set()
|
| 294 |
+
for row in rows:
|
| 295 |
+
with Image.open(out / row["image"]) as image:
|
| 296 |
+
if image.convert("RGB").getbbox() is None:
|
| 297 |
+
bad.add((row["metadata"]["repo"], row["metadata"]["asset_id"]))
|
| 298 |
+
for repo, asset in sorted(bad):
|
| 299 |
+
failures.append({"repo": repo, "asset": asset,
|
| 300 |
+
"error": "Rendered view has no visible pixels", "terminal": True})
|
| 301 |
+
if bad:
|
| 302 |
+
print(json.dumps({"excluded_invisible_objects": len(bad), "existing_files_preserved": True}), flush=True)
|
| 303 |
+
return ([r for r in rows if (r["metadata"]["repo"], r["metadata"]["asset_id"]) not in bad],
|
| 304 |
+
[p for p in provenance if (p["repo"], p["asset_id"]) not in bad])
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def group_duplicates(rows, provenance):
|
| 308 |
+
"""Link entire objects when any views or target files are byte-identical."""
|
| 309 |
+
parent = {}
|
| 310 |
+
def find(x):
|
| 311 |
+
parent.setdefault(x, x)
|
| 312 |
+
if parent[x] != x:
|
| 313 |
+
parent[x] = find(parent[x])
|
| 314 |
+
return parent[x]
|
| 315 |
+
hashes = {}
|
| 316 |
+
for row in rows:
|
| 317 |
+
meta = row["metadata"]
|
| 318 |
+
original = meta.setdefault("source_object_id", meta["object_id"])
|
| 319 |
+
find(original)
|
| 320 |
+
for field in ("image_sha256", "target_sha256"):
|
| 321 |
+
key = (field, row[field])
|
| 322 |
+
if key in hashes:
|
| 323 |
+
a, b = find(original), find(hashes[key])
|
| 324 |
+
parent[max(a, b)] = min(a, b)
|
| 325 |
+
else:
|
| 326 |
+
hashes[key] = original
|
| 327 |
+
for row in rows:
|
| 328 |
+
row["metadata"]["object_id"] = find(row["metadata"]["source_object_id"])
|
| 329 |
+
for item in provenance:
|
| 330 |
+
original = item.setdefault("source_object_id", item["object_id"])
|
| 331 |
+
item["object_id"] = find(original)
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def prepare(args):
|
| 335 |
+
out = Path(args.out)
|
| 336 |
+
occupied = out.exists() and any(out.iterdir())
|
| 337 |
+
if occupied and not getattr(args, "resume", False):
|
| 338 |
+
raise ValueError("Output is not empty. Use --resume to retain completed objects.")
|
| 339 |
+
if args.scenario + args.objaverse < 4:
|
| 340 |
+
raise ValueError("Request at least four objects")
|
| 341 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 342 |
+
if occupied:
|
| 343 |
+
all_rows, provenance, failures = load_progress(out, args)
|
| 344 |
+
else:
|
| 345 |
+
all_rows, provenance, failures = [], [], []
|
| 346 |
+
save_progress(out, args, all_rows, provenance, failures)
|
| 347 |
+
all_rows, provenance = prune_invisible(out, all_rows, provenance, failures)
|
| 348 |
+
save_progress(out, args, all_rows, provenance, failures)
|
| 349 |
+
seen = {item["sha256"] for item in provenance}
|
| 350 |
+
completed = {(item["repo"], item["asset_id"]) for item in provenance}
|
| 351 |
+
rejected = {(f["repo"], f["asset"]) for f in failures if f.get("terminal")}
|
| 352 |
+
counts = {}
|
| 353 |
+
fetch = Downloader(args.cache, args.max_file_mb, args.max_download_mb, args.reserve_gb)
|
| 354 |
+
sources = []
|
| 355 |
+
if args.scenario:
|
| 356 |
+
ready = sum(item["repo"] == SCENARIO[0] for item in provenance)
|
| 357 |
+
candidates = scenario_candidates(args.cache, args.seed) if ready < args.scenario else []
|
| 358 |
+
sources.append((SCENARIO[0], candidates, args.scenario, ready))
|
| 359 |
+
if args.objaverse:
|
| 360 |
+
ready = sum(item["repo"] == OBJAVERSE[0] for item in provenance)
|
| 361 |
+
candidates = objaverse_candidates(args.cache, args.seed, args.objaverse_shards) if ready < args.objaverse else []
|
| 362 |
+
sources.append((OBJAVERSE[0], candidates, args.objaverse, ready))
|
| 363 |
+
for repo, candidates, wanted, accepted in sources:
|
| 364 |
+
for item in candidates:
|
| 365 |
+
if accepted >= wanted:
|
| 366 |
+
break
|
| 367 |
+
if (item["repo"], item["asset_id"]) in completed | rejected:
|
| 368 |
+
continue
|
| 369 |
+
staging = out / "_staging"
|
| 370 |
+
try:
|
| 371 |
+
path = fetch.get(item["repo"], item["revision"], item["path"], item.get("sha256"))
|
| 372 |
+
checksum = digest(path)
|
| 373 |
+
if checksum in seen:
|
| 374 |
+
continue
|
| 375 |
+
item["sha256"] = checksum
|
| 376 |
+
item_seed = int(checksum[:8], 16) ^ args.seed
|
| 377 |
+
splats, transform = mesh_splats(path, args.surface_points, args.grid, item_seed)
|
| 378 |
+
stem = hashlib.sha256((item["repo"] + item["asset_id"]).encode()).hexdigest()[:20]
|
| 379 |
+
staging.mkdir(parents=True, exist_ok=True)
|
| 380 |
+
rows = []
|
| 381 |
+
for view in range(args.views):
|
| 382 |
+
yaw = 2 * np.pi * view / args.views
|
| 383 |
+
c, s = np.cos(yaw), np.sin(yaw)
|
| 384 |
+
rotation = np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]], np.float32)
|
| 385 |
+
rotated = Splats(splats.xyz @ rotation.T, splats.rgb, splats.scale, splats.quat, splats.opacity)
|
| 386 |
+
image = staging / "input.png"
|
| 387 |
+
rendered = preview(rotated, size=128)
|
| 388 |
+
if rendered.getbbox() is None:
|
| 389 |
+
raise ValueError("Rendered view has no visible pixels")
|
| 390 |
+
rendered.save(image)
|
| 391 |
+
metadata = {**item, "object_id": item["object_id"], "normalization_matrix": transform,
|
| 392 |
+
"yaw_radians": float(yaw), "render": "orthographic Gaussian proxy, camera +z",
|
| 393 |
+
"target": "isotropic surface-sampled mesh proxy; not a pretrained teacher"}
|
| 394 |
+
rows.append(cache_sample(staging, f"{stem}-v{view}", image, rotated, args.grid,
|
| 395 |
+
source="mesh-proxy:" + item["repo"], extra=metadata))
|
| 396 |
+
for folder in ("images", "targets"):
|
| 397 |
+
(out / folder).mkdir(exist_ok=True)
|
| 398 |
+
for file in (staging / folder).iterdir():
|
| 399 |
+
file.replace(out / folder / file.name)
|
| 400 |
+
all_rows.extend(rows)
|
| 401 |
+
seen.add(checksum)
|
| 402 |
+
provenance.append(item)
|
| 403 |
+
completed.add((item["repo"], item["asset_id"]))
|
| 404 |
+
accepted += 1
|
| 405 |
+
save_progress(out, args, all_rows, provenance, failures)
|
| 406 |
+
print(json.dumps({"repo": item["repo"], "accepted": accepted, "requested": wanted,
|
| 407 |
+
"asset": item["asset_id"]}), flush=True)
|
| 408 |
+
except RuntimeError:
|
| 409 |
+
raise # resource limits are a hard stop
|
| 410 |
+
except Exception as exc:
|
| 411 |
+
failures.append({"repo": item["repo"], "asset": item["asset_id"],
|
| 412 |
+
"error": type(exc).__name__ + ": " + str(exc)[:200],
|
| 413 |
+
"terminal": "no visible pixels" in str(exc)})
|
| 414 |
+
print(json.dumps({"skipped": item["asset_id"], "reason": failures[-1]["error"]}), flush=True)
|
| 415 |
+
finally:
|
| 416 |
+
shutil.rmtree(staging, ignore_errors=True)
|
| 417 |
+
counts[repo] = {"requested": wanted, "prepared": accepted}
|
| 418 |
+
group_duplicates(all_rows, provenance)
|
| 419 |
+
save_progress(out, args, all_rows, provenance, failures)
|
| 420 |
+
(out / "sources.json").write_text(json.dumps(provenance, indent=2))
|
| 421 |
+
(out / "skipped.json").write_text(json.dumps(failures, indent=2))
|
| 422 |
+
report = {"counts": counts, "samples": len(all_rows), "objects": len(provenance),
|
| 423 |
+
"distinct_groups": len({r["metadata"]["object_id"] for r in all_rows}),
|
| 424 |
+
"settings": vars(args), "rendered_proxy_data": True,
|
| 425 |
+
"excluded": ["scenario-labs/3d-collection-hunyuan", "3dlg-hcvc/MultiScan",
|
| 426 |
+
"huanngzh/3D-Front", "facebook/show3d-dataset", "Voxel51/gaussian_splatting"]}
|
| 427 |
+
(out / "preparation.json").write_text(json.dumps(report, indent=2))
|
| 428 |
+
if any(v["prepared"] < v["requested"] for v in counts.values()):
|
| 429 |
+
raise ValueError("Not enough eligible assets. Completed objects are saved. Rerun with --resume and a larger --objaverse-shards value.")
|
| 430 |
+
if report["distinct_groups"] < 4:
|
| 431 |
+
raise ValueError("Not enough independent object groups")
|
| 432 |
+
# The test partition never enters training or checkpoint selection.
|
| 433 |
+
development, test = split_rows(all_rows, .15, args.seed + 1)
|
| 434 |
+
for name, rows in [("manifest.jsonl", development), ("test-manifest.jsonl", test)]:
|
| 435 |
+
(out / name).write_text("".join(json.dumps(r) + "\n" for r in rows))
|
| 436 |
+
print(json.dumps(report, indent=2))
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
def main():
|
| 440 |
+
p = argparse.ArgumentParser(description=__doc__)
|
| 441 |
+
p.add_argument("--out", required=True)
|
| 442 |
+
p.add_argument("--cache", default="data/downloads")
|
| 443 |
+
p.add_argument("--scenario", type=int, default=64)
|
| 444 |
+
p.add_argument("--objaverse", type=int, default=32)
|
| 445 |
+
p.add_argument("--objaverse-shards", type=int, default=8)
|
| 446 |
+
p.add_argument("--resume", action="store_true", help="Reuse and verify completed objects, including older failed runs")
|
| 447 |
+
p.add_argument("--grid", type=int, choices=[8, 16, 32], default=16)
|
| 448 |
+
p.add_argument("--views", type=int, default=4)
|
| 449 |
+
p.add_argument("--surface-points", type=int, default=4096)
|
| 450 |
+
p.add_argument("--seed", type=int, default=42)
|
| 451 |
+
p.add_argument("--max-file-mb", type=int, default=16)
|
| 452 |
+
p.add_argument("--max-download-mb", type=int, default=2048)
|
| 453 |
+
p.add_argument("--reserve-gb", type=float, default=2)
|
| 454 |
+
args = p.parse_args()
|
| 455 |
+
if min(args.scenario, args.objaverse) < 0 or not 1 <= args.objaverse_shards <= 160:
|
| 456 |
+
p.error("Invalid object counts or shard count")
|
| 457 |
+
if min(args.views, args.surface_points, args.max_file_mb, args.max_download_mb, args.reserve_gb) <= 0:
|
| 458 |
+
p.error("Counts and limits must be positive")
|
| 459 |
+
prepare(args)
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
if __name__ == "__main__":
|
| 463 |
+
main()
|
scripts/release.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Evaluate a completed run, stage a self-contained release, optionally publish it."""
|
| 2 |
+
import argparse
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
import shutil
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from torch.utils.data import DataLoader
|
| 10 |
+
|
| 11 |
+
from image3d_student.data import CachedDataset, load_manifest
|
| 12 |
+
from image3d_student.engine import device_for, evaluate, generate
|
| 13 |
+
from image3d_student.model import Student
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def sha(path):
|
| 17 |
+
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def evaluate_release(args):
|
| 21 |
+
run, dataset = Path(args.run), Path(args.data)
|
| 22 |
+
meta = json.loads((run / "run.json").read_text())
|
| 23 |
+
checkpoint = torch.load(run / "best.pt", map_location="cpu", weights_only=True)
|
| 24 |
+
last = torch.load(run / "last.pt", map_location="cpu", weights_only=True)
|
| 25 |
+
if last["step"] != meta["steps_requested"]:
|
| 26 |
+
raise ValueError("Training did not complete the requested steps")
|
| 27 |
+
if checkpoint.get("step", 0) < 1 or not meta.get("initialization"):
|
| 28 |
+
raise ValueError("Expected completed post-training from a base checkpoint")
|
| 29 |
+
if sha(args.base) != meta["initialization"]["sha256"]:
|
| 30 |
+
raise ValueError("Base checkpoint differs from the training initialization")
|
| 31 |
+
if sha(dataset / "manifest.jsonl") != meta["manifest_sha256"]:
|
| 32 |
+
raise ValueError("Training manifest changed")
|
| 33 |
+
development = load_manifest(dataset / "manifest.jsonl")
|
| 34 |
+
test = load_manifest(dataset / "test-manifest.jsonl")
|
| 35 |
+
selected_ids = set(meta["train_ids"]) | set(meta["val_ids"])
|
| 36 |
+
if selected_ids != {r["id"] for r in development}:
|
| 37 |
+
raise ValueError("Run split does not match development manifest")
|
| 38 |
+
if {r["metadata"]["object_id"] for r in development} & {r["metadata"]["object_id"] for r in test}:
|
| 39 |
+
raise ValueError("Test object leakage")
|
| 40 |
+
for key in ["image_sha256", "target_sha256"]:
|
| 41 |
+
if {r[key] for r in development} & {r[key] for r in test}:
|
| 42 |
+
raise ValueError("Duplicate data across development and test")
|
| 43 |
+
device = device_for(args.device)
|
| 44 |
+
model = Student(**checkpoint["config"]).to(device)
|
| 45 |
+
model.load_state_dict(checkpoint["model"])
|
| 46 |
+
loader = DataLoader(CachedDataset(dataset / "test-manifest.jsonl"), batch_size=1)
|
| 47 |
+
final = evaluate(model, loader, device)
|
| 48 |
+
base = torch.load(args.base, map_location="cpu", weights_only=True)
|
| 49 |
+
initial = Student(**checkpoint["config"]).to(device)
|
| 50 |
+
result = initial.load_state_dict({k: v for k, v in base["model"].items() if k not in ("coords", "centers")}, strict=False)
|
| 51 |
+
if set(result.missing_keys) != {"coords", "centers"} or result.unexpected_keys:
|
| 52 |
+
raise ValueError("Base architecture mismatch")
|
| 53 |
+
baseline = evaluate(initial, loader, device)
|
| 54 |
+
best_validation = min(json.loads(line)["validation"]["loss"] for line in
|
| 55 |
+
(run / "metrics.jsonl").read_text().splitlines())
|
| 56 |
+
checks = {"validation_loss_improved": best_validation < meta["baseline_validation"]["loss"],
|
| 57 |
+
"test_loss_improved": final["loss"] < baseline["loss"],
|
| 58 |
+
"test_occupancy_iou_improved": final["occupancy_iou"] > baseline["occupancy_iou"],
|
| 59 |
+
"test_occupancy_iou_at_least_005": final["occupancy_iou"] >= .05}
|
| 60 |
+
report = {"checkpoint_sha256": sha(run / "best.pt"), "base_sha256": sha(args.base),
|
| 61 |
+
"test_manifest_sha256": sha(dataset / "test-manifest.jsonl"),
|
| 62 |
+
"test_objects": len({r["metadata"]["object_id"] for r in test}),
|
| 63 |
+
"test_samples": len(test), "baseline_test": baseline, "posttrained_test": final,
|
| 64 |
+
"checks": checks, "passed": all(checks.values()),
|
| 65 |
+
"scope": "Rendered mesh-proxy evaluation only; does not establish real-photo or mesh reconstruction quality"}
|
| 66 |
+
(run / "evaluation.json").write_text(json.dumps(report, indent=2))
|
| 67 |
+
print(json.dumps(report, indent=2), flush=True)
|
| 68 |
+
# Keep qualitative inspection files local; all held-out objects, first view.
|
| 69 |
+
example_dir = run / "inspection"
|
| 70 |
+
example_dir.mkdir(exist_ok=True)
|
| 71 |
+
seen = set()
|
| 72 |
+
from image3d_student.splats import preview, read_ply
|
| 73 |
+
from PIL import Image, ImageDraw
|
| 74 |
+
for row in test:
|
| 75 |
+
group = row["metadata"]["object_id"]
|
| 76 |
+
if group in seen:
|
| 77 |
+
continue
|
| 78 |
+
seen.add(group)
|
| 79 |
+
out = example_dir / (row["id"] + ".ply")
|
| 80 |
+
if not out.exists():
|
| 81 |
+
try:
|
| 82 |
+
generate(run / "best.pt", dataset / row["image"], out, args.device)
|
| 83 |
+
except ValueError as exc:
|
| 84 |
+
if "No occupied cells" not in str(exc):
|
| 85 |
+
raise
|
| 86 |
+
report["checks"]["nonempty_examples"] = False
|
| 87 |
+
report["passed"] = False
|
| 88 |
+
continue
|
| 89 |
+
sheet = Image.new("RGB", (512, 152), "black")
|
| 90 |
+
sheet.paste(Image.open(dataset / row["image"]).convert("RGB").resize((128, 128)), (0, 24))
|
| 91 |
+
splats = read_ply(out)
|
| 92 |
+
for i, angle in enumerate([0, 1.5707963, 3.1415927]):
|
| 93 |
+
sheet.paste(preview(splats, size=128, yaw=angle), (128 * (i + 1), 24))
|
| 94 |
+
ImageDraw.Draw(sheet).text((5, 5), "Input proxy | prediction front | side | back", fill="white")
|
| 95 |
+
sheet.save(out.with_suffix(".png"))
|
| 96 |
+
if len(seen) >= 8:
|
| 97 |
+
break
|
| 98 |
+
(run / "evaluation.json").write_text(json.dumps(report, indent=2))
|
| 99 |
+
if not report["passed"]:
|
| 100 |
+
raise ValueError("Evaluation gate failed. Nothing was published. Inspect evaluation.json and improve data/training.")
|
| 101 |
+
return checkpoint, meta, report
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def stage(args, checkpoint, meta, report):
|
| 105 |
+
source = Path(__file__).resolve().parents[1]
|
| 106 |
+
release = Path(args.release)
|
| 107 |
+
if release.exists():
|
| 108 |
+
raise ValueError("Release directory already exists; choose a new --release path")
|
| 109 |
+
release.mkdir(parents=True)
|
| 110 |
+
shutil.copytree(source / "src", release / "src", ignore=shutil.ignore_patterns("__pycache__", "*.egg-info"))
|
| 111 |
+
shutil.copytree(source / "scripts", release / "scripts", ignore=shutil.ignore_patterns("__pycache__"))
|
| 112 |
+
shutil.copytree(source / "licenses", release / "licenses")
|
| 113 |
+
for filename in ["pyproject.toml", "LICENSE", "NOTICE", "THIRD_PARTY.md", "DATASETS.md", "COMMANDS.md", "setup-mac.sh", "train-mac.sh", "requirements-tested-macos.txt"]:
|
| 114 |
+
shutil.copyfile(source / filename, release / filename)
|
| 115 |
+
# Publish inference weights only; optimizer and local absolute paths stay local.
|
| 116 |
+
public_meta = {k: v for k, v in meta.items() if k != "dataset"}
|
| 117 |
+
torch.save({"format": 1, "config": checkpoint["config"], "model": checkpoint["model"],
|
| 118 |
+
"step": checkpoint["step"], "metadata": public_meta}, release / "model.pt")
|
| 119 |
+
(release / "config.json").write_text(json.dumps(checkpoint["config"], indent=2))
|
| 120 |
+
(release / "training.json").write_text(json.dumps(public_meta, indent=2))
|
| 121 |
+
(release / "evaluation.json").write_text(json.dumps({**report,
|
| 122 |
+
"published_model_sha256": sha(release / "model.pt")}, indent=2))
|
| 123 |
+
shutil.copyfile(Path(args.data) / "sources.json", release / "ATTRIBUTIONS.json")
|
| 124 |
+
used = sorted({r["repo"] for r in json.loads((release / "ATTRIBUTIONS.json").read_text())})
|
| 125 |
+
datasets_yaml = "\n".join("- " + repo for repo in used)
|
| 126 |
+
text = f'''---
|
| 127 |
+
license: apache-2.0
|
| 128 |
+
pipeline_tag: image-to-3d
|
| 129 |
+
base_model: Lucasgitguy-co/image3d-student
|
| 130 |
+
model_name: Forma3D-Tiny v0.2
|
| 131 |
+
datasets:
|
| 132 |
+
{datasets_yaml}
|
| 133 |
+
tags:
|
| 134 |
+
- pytorch
|
| 135 |
+
- gaussian-splatting
|
| 136 |
+
- research-prototype
|
| 137 |
+
- mesh-proxy-supervision
|
| 138 |
+
---
|
| 139 |
+
# Forma3D-Tiny v0.2
|
| 140 |
+
|
| 141 |
+
An experimental compact image-to-Gaussian model post-trained from Image3D Student's
|
| 142 |
+
synthetic demonstration checkpoint. This is a coarse research baseline, not a
|
| 143 |
+
general-purpose photo-to-3D model. Its grid is {checkpoint["config"]["grid"]} cubed and
|
| 144 |
+
it has {meta["parameters"]:,} parameters. Selected checkpoint: step {checkpoint["step"]};
|
| 145 |
+
completed run: {meta["steps_requested"]} steps.
|
| 146 |
+
|
| 147 |
+
## Actual training data and preprocessing
|
| 148 |
+
|
| 149 |
+
Only the datasets listed above were used, with per-asset provenance in ATTRIBUTIONS.json.
|
| 150 |
+
Scenario assets are CC-BY-4.0; the Objaverse subset is CC0. Original objects were centered
|
| 151 |
+
and uniformly scaled into a 0.9-wide cube, surface sampled, and converted to isotropic
|
| 152 |
+
Gaussian proxy targets. Orthographic images were rendered from these same proxy clouds.
|
| 153 |
+
This is not teacher distillation from Idol, Hunyuan, InstantMesh, or Hi3D, and it did not
|
| 154 |
+
train on natural photographs, MultiScan, 3D-Front, Show3D, or Voxel51 scene splats.
|
| 155 |
+
|
| 156 |
+
Related source inputs and all views stay in the same object group. A separate 15% test
|
| 157 |
+
partition is excluded from training and validation-based checkpoint selection.
|
| 158 |
+
|
| 159 |
+
## Measured results
|
| 160 |
+
|
| 161 |
+
Independent test objects: {report["test_objects"]}; test views: {report["test_samples"]}.
|
| 162 |
+
Base test loss: {report["baseline_test"]["loss"]:.6f}; post-trained test loss:
|
| 163 |
+
{report["posttrained_test"]["loss"]:.6f}. Base occupancy IoU:
|
| 164 |
+
{report["baseline_test"]["occupancy_iou"]:.6f}; post-trained occupancy IoU:
|
| 165 |
+
{report["posttrained_test"]["occupancy_iou"]:.6f}.
|
| 166 |
+
See evaluation.json for the full results and checks. These scores measure coarse proxy
|
| 167 |
+
reconstruction on the rendered data distribution; they do not prove photo realism,
|
| 168 |
+
watertight meshes, production quality, or texture fidelity. Near duplicates can remain
|
| 169 |
+
despite source grouping and exact-byte deduplication.
|
| 170 |
+
|
| 171 |
+
## Run on a Mac
|
| 172 |
+
|
| 173 |
+
Download this repository, then run `bash setup-mac.sh` and
|
| 174 |
+
`source "$HOME/Library/Application Support/Forma3D/venv/bin/activate"`.
|
| 175 |
+
|
| 176 |
+
```sh
|
| 177 |
+
image3d generate --checkpoint model.pt --image your-centered-cutout.png \\
|
| 178 |
+
--out results/object.ply --device mps
|
| 179 |
+
image3d preview --ply results/object.ply --out results/preview.png
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
Inputs are centered objects on black, matching the training framing. Natural photos
|
| 183 |
+
are outside the verified distribution. Output is Gaussian PLY, not a triangle mesh.
|
| 184 |
+
CPU works with `--device cpu`; CUDA support is implemented but was not tested on the
|
| 185 |
+
Mac used to prepare these scripts.
|
| 186 |
+
|
| 187 |
+
## License and attribution
|
| 188 |
+
|
| 189 |
+
Source code and these weights are offered under Apache-2.0. Dataset and individual
|
| 190 |
+
asset licenses remain separate; retain ATTRIBUTIONS.json and consult DATASETS.md.
|
| 191 |
+
This license statement does not grant rights to third-party trademarks or likenesses.
|
| 192 |
+
See COMMANDS.md for reproduction commands.
|
| 193 |
+
'''
|
| 194 |
+
(release / "README.md").write_text(text)
|
| 195 |
+
sums = [sha(p) + " " + str(p.relative_to(release)) for p in sorted(release.rglob("*")) if p.is_file()]
|
| 196 |
+
(release / "SHA256SUMS").write_text("\n".join(sums) + "\n")
|
| 197 |
+
return release
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def main():
|
| 201 |
+
p = argparse.ArgumentParser(description=__doc__)
|
| 202 |
+
p.add_argument("--run", required=True)
|
| 203 |
+
p.add_argument("--data", required=True)
|
| 204 |
+
p.add_argument("--base", required=True)
|
| 205 |
+
p.add_argument("--release", default="release/forma3d-tiny")
|
| 206 |
+
p.add_argument("--repo", default="Lucasgitguy-co/forma3d-tiny")
|
| 207 |
+
p.add_argument("--device", choices=["mps", "cpu", "cuda", "auto"], default="mps")
|
| 208 |
+
p.add_argument("--publish", action="store_true")
|
| 209 |
+
args = p.parse_args()
|
| 210 |
+
checkpoint, meta, report = evaluate_release(args)
|
| 211 |
+
release = stage(args, checkpoint, meta, report)
|
| 212 |
+
if args.publish:
|
| 213 |
+
from huggingface_hub import HfApi
|
| 214 |
+
api = HfApi()
|
| 215 |
+
# Refuse accidental replacement of a previously published model.
|
| 216 |
+
if api.repo_exists(args.repo, repo_type="model"):
|
| 217 |
+
raise ValueError("Repository already exists. Choose a new --repo; no overwrite was performed.")
|
| 218 |
+
api.create_repo(args.repo, repo_type="model", private=True)
|
| 219 |
+
commit = api.upload_folder(repo_id=args.repo, folder_path=release,
|
| 220 |
+
commit_message="Publish evaluated Forma3D-Tiny v0.2")
|
| 221 |
+
api.update_repo_settings(args.repo, private=False)
|
| 222 |
+
print(f"Published https://huggingface.co/{args.repo} at {commit.oid}")
|
| 223 |
+
else:
|
| 224 |
+
print(f"Evaluated release staged at {release}; nothing published.")
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
if __name__ == "__main__":
|
| 228 |
+
main()
|
src/image3d_student/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
"""Research prototype; no pretrained general-purpose student weights are bundled."""
|
| 4 |
+
|
| 5 |
+
__version__ = "0.2.0"
|
src/image3d_student/cli.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
import argparse
|
| 4 |
+
import importlib.util
|
| 5 |
+
import json
|
| 6 |
+
import platform
|
| 7 |
+
import shutil
|
| 8 |
+
import sys
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def parser():
|
| 15 |
+
p = argparse.ArgumentParser(description="Local image-to-Gaussian student research prototype")
|
| 16 |
+
sub = p.add_subparsers(dest="command", required=True)
|
| 17 |
+
sub.add_parser("doctor", help="Check dependencies, Metal availability and disk space; no downloads")
|
| 18 |
+
demo = sub.add_parser("demo-data", help="Create synthetic code-test fixtures, NOT teacher data")
|
| 19 |
+
demo.add_argument("--out", required=True)
|
| 20 |
+
demo.add_argument("--count", type=int, default=12)
|
| 21 |
+
demo.add_argument("--grid", type=int, choices=[8,16,32], default=8)
|
| 22 |
+
cache = sub.add_parser("cache", help="Cache Idol PLY with optional aligned geometry teacher meshes")
|
| 23 |
+
cache.add_argument("--image", required=True, help="Exact prepared/framed input used by teacher")
|
| 24 |
+
cache.add_argument("--idol-ply", required=True)
|
| 25 |
+
cache.add_argument("--out", required=True)
|
| 26 |
+
cache.add_argument("--id", required=True)
|
| 27 |
+
cache.add_argument("--object-id", help="Shared ID for all images of the same object, to prevent validation leakage")
|
| 28 |
+
cache.add_argument("--grid", type=int, choices=[8,16,32], default=16)
|
| 29 |
+
cache.add_argument("--coarse-mesh", help="InstantMesh output; this command imports, does not run InstantMesh")
|
| 30 |
+
cache.add_argument("--coarse-transform", help="JSON 4x4 mesh-to-Idol-PLY matrix")
|
| 31 |
+
cache.add_argument("--fine-mesh", help="Hi3D output; this command imports, does not run Hi3D")
|
| 32 |
+
cache.add_argument("--fine-transform", help="JSON 4x4 mesh-to-Idol-PLY matrix")
|
| 33 |
+
train = sub.add_parser("train", help="Train the student on cached targets")
|
| 34 |
+
train.add_argument("--manifest", required=True)
|
| 35 |
+
train.add_argument("--out", required=True)
|
| 36 |
+
train.add_argument("--device", choices=["auto","cuda","mps","cpu"], default="auto")
|
| 37 |
+
train.add_argument("--init-from", help="Post-train checkpoint weights; optimizer starts fresh")
|
| 38 |
+
train.add_argument("--steps", type=int, default=1000)
|
| 39 |
+
train.add_argument("--batch-size", type=int, default=1)
|
| 40 |
+
train.add_argument("--width", type=int, default=128)
|
| 41 |
+
train.add_argument("--lr", type=float, default=3e-4)
|
| 42 |
+
train.add_argument("--seed", type=int, default=42)
|
| 43 |
+
train.add_argument("--eval-every", type=int, default=50)
|
| 44 |
+
train.add_argument("--validation-fraction", type=float, default=.2)
|
| 45 |
+
train.add_argument("--allow-synthetic", action="store_true")
|
| 46 |
+
generate = sub.add_parser("generate", help="Generate standard 3DGS PLY with a trained student")
|
| 47 |
+
generate.add_argument("--checkpoint", required=True)
|
| 48 |
+
generate.add_argument("--image", required=True, help="Centered cutout or teacher-prepared image, black background")
|
| 49 |
+
generate.add_argument("--out", required=True)
|
| 50 |
+
generate.add_argument("--device", choices=["auto","cuda","mps","cpu"], default="auto")
|
| 51 |
+
generate.add_argument("--threshold", type=float, default=.5)
|
| 52 |
+
generate.add_argument("--max-splats", type=int, default=32768)
|
| 53 |
+
render = sub.add_parser("preview", help="CPU diagnostic turntable contact sheet")
|
| 54 |
+
render.add_argument("--ply", required=True)
|
| 55 |
+
render.add_argument("--out", required=True)
|
| 56 |
+
teacher = sub.add_parser("teacher-idol", help="EXPERIMENTAL staged Mac runner; weights not bundled")
|
| 57 |
+
teacher.add_argument("--image", required=True)
|
| 58 |
+
teacher.add_argument("--out", required=True)
|
| 59 |
+
teacher.add_argument("--cache", default=".cache/huggingface")
|
| 60 |
+
teacher.add_argument("--weights", default="speridlabs/idol")
|
| 61 |
+
teacher.add_argument("--device", choices=["mps","cpu"], default="mps")
|
| 62 |
+
teacher.add_argument("--mode", choices=["real","synthetic"], default="synthetic")
|
| 63 |
+
teacher.add_argument("--steps", type=int, default=20)
|
| 64 |
+
teacher.add_argument("--gaussians", type=int, default=32768)
|
| 65 |
+
teacher.add_argument("--query-chunk", type=int, default=128)
|
| 66 |
+
teacher.add_argument("--seed", type=int, default=42)
|
| 67 |
+
teacher.add_argument("--allow-download", action="store_true", help="Explicitly allow downloading model weights and gated encoders")
|
| 68 |
+
return p
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def main():
|
| 72 |
+
p = parser()
|
| 73 |
+
args = p.parse_args()
|
| 74 |
+
try:
|
| 75 |
+
if args.command == "doctor":
|
| 76 |
+
report = {"python": platform.python_version(), "platform": platform.platform(), "torch": str(torch.__version__),
|
| 77 |
+
"mps_available": torch.backends.mps.is_available(), "mps_built": torch.backends.mps.is_built(),
|
| 78 |
+
"free_disk_gib": round(shutil.disk_usage(Path.cwd()).free / 1024**3, 2),
|
| 79 |
+
"idol_installed": importlib.util.find_spec("idol") is not None,
|
| 80 |
+
"teacher_status": {"Idol": "experimental adapter, real weights unverified",
|
| 81 |
+
"InstantMesh": "cached mesh import only; no Mac execution adapter",
|
| 82 |
+
"Hi3D": "cached mesh import only; no Mac execution adapter"},
|
| 83 |
+
"weights_bundled": False, "quantization": "FP32 student; no binary kernels or 1-bit claims"}
|
| 84 |
+
print(json.dumps(report, indent=2))
|
| 85 |
+
elif args.command == "demo-data":
|
| 86 |
+
from .demo import create_demo
|
| 87 |
+
print(create_demo(args.out, args.count, args.grid))
|
| 88 |
+
elif args.command == "cache":
|
| 89 |
+
from .data import cache_sample
|
| 90 |
+
from .splats import read_ply
|
| 91 |
+
print(json.dumps(cache_sample(args.out, args.id, args.image, read_ply(args.idol_ply), args.grid,
|
| 92 |
+
coarse=args.coarse_mesh, coarse_transform=args.coarse_transform,
|
| 93 |
+
fine=args.fine_mesh, fine_transform=args.fine_transform,
|
| 94 |
+
extra={"object_id": args.object_id or args.id, "idol_ply": str(Path(args.idol_ply).resolve())}), indent=2))
|
| 95 |
+
elif args.command == "train":
|
| 96 |
+
from .engine import train
|
| 97 |
+
train(args)
|
| 98 |
+
elif args.command == "generate":
|
| 99 |
+
from .engine import generate
|
| 100 |
+
print(json.dumps(generate(args.checkpoint, args.image, args.out, args.device, args.threshold, args.max_splats), indent=2))
|
| 101 |
+
elif args.command == "preview":
|
| 102 |
+
from PIL import Image, ImageDraw
|
| 103 |
+
import numpy as np
|
| 104 |
+
from .splats import preview, read_ply
|
| 105 |
+
splats = read_ply(args.ply)
|
| 106 |
+
sheet = Image.new("RGB", (1024, 280), "#111111")
|
| 107 |
+
draw = ImageDraw.Draw(sheet)
|
| 108 |
+
for i in range(4):
|
| 109 |
+
sheet.paste(preview(splats, yaw=i*np.pi/2), (i*256, 24))
|
| 110 |
+
draw.text((i*256+8, 6), f"{i*90} degrees - diagnostic view", fill="white")
|
| 111 |
+
out = Path(args.out)
|
| 112 |
+
if out.exists():
|
| 113 |
+
raise ValueError("Preview already exists")
|
| 114 |
+
out.parent.mkdir(parents=True, exist_ok=True)
|
| 115 |
+
sheet.save(out)
|
| 116 |
+
elif args.command == "teacher-idol":
|
| 117 |
+
from .teacher_idol import run_idol
|
| 118 |
+
run_idol(args)
|
| 119 |
+
except (ValueError, RuntimeError, FileNotFoundError, ImportError) as exc:
|
| 120 |
+
print(f"Error: {exc}", file=sys.stderr)
|
| 121 |
+
return 2
|
| 122 |
+
return 0
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
if __name__ == "__main__":
|
| 126 |
+
raise SystemExit(main())
|
src/image3d_student/data.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
from PIL import Image, ImageOps
|
| 10 |
+
from torch.utils.data import Dataset
|
| 11 |
+
|
| 12 |
+
from .splats import Splats, read_ply
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def grid_centers(grid):
|
| 16 |
+
axis = (np.arange(grid, dtype=np.float32) + .5) / grid - .5
|
| 17 |
+
return np.stack(np.meshgrid(axis, axis, axis, indexing="ij"), axis=-1).reshape(-1, 3)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def cell_indices(xyz, grid):
|
| 21 |
+
if not np.isfinite(xyz).all() or (np.abs(xyz) > .50001).any():
|
| 22 |
+
raise ValueError("Targets must be aligned inside [-0.5,0.5]^3. No silent normalization is performed.")
|
| 23 |
+
ijk = np.floor((xyz + .5) * grid).astype(np.int64).clip(0, grid-1)
|
| 24 |
+
return (ijk[:, 0] * grid + ijk[:, 1]) * grid + ijk[:, 2]
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def splat_targets(splats, grid):
|
| 28 |
+
"""One highest-opacity teacher Gaussian per occupied cell; explicitly lossy."""
|
| 29 |
+
cells = cell_indices(splats.xyz, grid)
|
| 30 |
+
order = np.argsort(-splats.opacity[:, 0], kind="stable")
|
| 31 |
+
_, first = np.unique(cells[order], return_index=True)
|
| 32 |
+
keep = order[first]
|
| 33 |
+
slots = cells[keep]
|
| 34 |
+
result = {"occ": np.zeros((grid**3, 1), np.float32), "xyz": grid_centers(grid),
|
| 35 |
+
"rgb": np.zeros((grid**3, 3), np.float32),
|
| 36 |
+
"scale": np.full((grid**3, 3), 1 / grid / 3, np.float32),
|
| 37 |
+
"quat": np.tile(np.array([1, 0, 0, 0], np.float32), (grid**3, 1)),
|
| 38 |
+
"opacity": np.zeros((grid**3, 1), np.float32),
|
| 39 |
+
"geometry": np.zeros((grid**3, 2), np.float32),
|
| 40 |
+
"geometry_valid": np.zeros(2, np.float32), "grid": np.array(grid, np.int32)}
|
| 41 |
+
result["occ"][slots] = 1
|
| 42 |
+
for key in ("xyz", "rgb", "scale", "quat", "opacity"):
|
| 43 |
+
result[key][slots] = getattr(splats, key)[keep]
|
| 44 |
+
return result
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def mesh_occupancy(path, transform_path, grid):
|
| 48 |
+
import trimesh
|
| 49 |
+
if not transform_path:
|
| 50 |
+
raise ValueError("Every geometry teacher needs an explicit --*-transform JSON matrix, even for identity.")
|
| 51 |
+
transform = np.asarray(json.loads(Path(transform_path).read_text()), dtype=float)
|
| 52 |
+
if transform.shape != (4, 4) or not np.isfinite(transform).all() or not np.allclose(transform[3], [0,0,0,1]):
|
| 53 |
+
raise ValueError("Transform must be a finite affine 4x4 matrix")
|
| 54 |
+
mesh = trimesh.load(str(path), force="mesh", process=False)
|
| 55 |
+
if not isinstance(mesh, trimesh.Trimesh) or len(mesh.faces) == 0:
|
| 56 |
+
raise ValueError("Geometry teacher must contain a triangle mesh")
|
| 57 |
+
mesh.apply_transform(transform)
|
| 58 |
+
cell_indices(np.asarray(mesh.vertices), grid) # validates the whole object
|
| 59 |
+
points, _ = trimesh.sample.sample_surface(mesh, min(200_000, grid**3 * 16), seed=42)
|
| 60 |
+
mask = np.zeros(grid**3, np.float32)
|
| 61 |
+
mask[np.unique(cell_indices(points, grid))] = 1
|
| 62 |
+
return mask
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def cache_sample(root, sample_id, image_path, splats, grid=16, source="idol",
|
| 66 |
+
coarse=None, coarse_transform=None, fine=None, fine_transform=None, extra=None):
|
| 67 |
+
root = Path(root)
|
| 68 |
+
if not sample_id or any(c not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" for c in sample_id):
|
| 69 |
+
raise ValueError("Sample ID may contain only letters, digits, - and _")
|
| 70 |
+
if grid not in (8, 16, 32):
|
| 71 |
+
raise ValueError("Grid must be 8, 16 or 32")
|
| 72 |
+
target = splat_targets(splats, grid)
|
| 73 |
+
for channel, mesh, transform in [(0, coarse, coarse_transform), (1, fine, fine_transform)]:
|
| 74 |
+
if mesh:
|
| 75 |
+
target["geometry"][:, channel] = mesh_occupancy(mesh, transform, grid)
|
| 76 |
+
target["geometry_valid"][channel] = 1
|
| 77 |
+
row = {"id": sample_id, "image": f"images/{sample_id}.png", "target": f"targets/{sample_id}.npz",
|
| 78 |
+
"source": source, "grid": grid, "teachers": {"appearance": source,
|
| 79 |
+
"coarse": "InstantMesh" if coarse else None, "fine": "Hi3D" if fine else None},
|
| 80 |
+
"teacher_gaussians": len(splats.xyz), "retained_gaussians": int(target["occ"].sum()),
|
| 81 |
+
"geometry_transforms": {"coarse": json.loads(Path(coarse_transform).read_text()) if coarse else None,
|
| 82 |
+
"fine": json.loads(Path(fine_transform).read_text()) if fine else None},
|
| 83 |
+
"metadata": extra or {}}
|
| 84 |
+
manifest = root / "manifest.jsonl"
|
| 85 |
+
rows = load_manifest(manifest) if manifest.exists() else []
|
| 86 |
+
if any(r["id"] == sample_id for r in rows):
|
| 87 |
+
raise ValueError(f"Sample {sample_id} already exists; choose a new ID")
|
| 88 |
+
if rows and any(r["grid"] != grid for r in rows):
|
| 89 |
+
raise ValueError("Do not mix grid resolutions in one dataset")
|
| 90 |
+
image = Image.open(image_path)
|
| 91 |
+
image.load()
|
| 92 |
+
if root.joinpath(row["image"]).exists() or root.joinpath(row["target"]).exists():
|
| 93 |
+
raise ValueError("Sample files already exist; refusing to overwrite")
|
| 94 |
+
root.joinpath("images").mkdir(parents=True, exist_ok=True)
|
| 95 |
+
root.joinpath("targets").mkdir(parents=True, exist_ok=True)
|
| 96 |
+
image.save(root / row["image"])
|
| 97 |
+
row["image_sha256"] = hashlib.sha256((root / row["image"]).read_bytes()).hexdigest()
|
| 98 |
+
np.savez_compressed(root / row["target"], **target)
|
| 99 |
+
row["target_sha256"] = hashlib.sha256((root / row["target"]).read_bytes()).hexdigest()
|
| 100 |
+
# Single-writer dataset. Atomic manifest replacement avoids partial JSON lines.
|
| 101 |
+
tmp = manifest.with_suffix(".tmp")
|
| 102 |
+
tmp.write_text("".join(json.dumps(r) + "\n" for r in [*rows, row]))
|
| 103 |
+
tmp.replace(manifest)
|
| 104 |
+
return row
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def load_manifest(path):
|
| 108 |
+
rows = [json.loads(line) for line in Path(path).read_text().splitlines() if line.strip()]
|
| 109 |
+
if not rows or len({r["id"] for r in rows}) != len(rows):
|
| 110 |
+
raise ValueError("Manifest must contain nonempty, unique sample IDs")
|
| 111 |
+
return rows
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def image_tensor(path, size=128):
|
| 115 |
+
"""Already-framed RGB or RGBA cutout; black background, square letterbox."""
|
| 116 |
+
with Image.open(path) as original:
|
| 117 |
+
rgba = ImageOps.exif_transpose(original).convert("RGBA")
|
| 118 |
+
bg = Image.new("RGBA", rgba.size, (0, 0, 0, 255))
|
| 119 |
+
bg.alpha_composite(rgba)
|
| 120 |
+
image = ImageOps.pad(bg.convert("RGB"), (size, size), method=Image.Resampling.LANCZOS, color="black")
|
| 121 |
+
return torch.from_numpy(np.array(image, dtype=np.float32).transpose(2, 0, 1) / 255)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
class CachedDataset(Dataset):
|
| 125 |
+
def __init__(self, manifest, rows=None):
|
| 126 |
+
self.root = Path(manifest).resolve().parent
|
| 127 |
+
self.rows = load_manifest(manifest) if rows is None else rows
|
| 128 |
+
if not self.rows:
|
| 129 |
+
raise ValueError("Empty dataset")
|
| 130 |
+
self.grid = self.rows[0]["grid"]
|
| 131 |
+
for row in self.rows:
|
| 132 |
+
if row["grid"] != self.grid:
|
| 133 |
+
raise ValueError("Mixed grids")
|
| 134 |
+
for key in ("image", "target"):
|
| 135 |
+
p = (self.root / row[key]).resolve()
|
| 136 |
+
if not p.is_relative_to(self.root) or not p.is_file():
|
| 137 |
+
raise ValueError(f"Invalid dataset {key} path")
|
| 138 |
+
digest = hashlib.sha256(p.read_bytes()).hexdigest()
|
| 139 |
+
if digest != row[f"{key}_sha256"]:
|
| 140 |
+
raise ValueError(f"Dataset {key} hash mismatch for {row['id']}")
|
| 141 |
+
|
| 142 |
+
def __len__(self):
|
| 143 |
+
return len(self.rows)
|
| 144 |
+
|
| 145 |
+
def __getitem__(self, i):
|
| 146 |
+
row = self.rows[i]
|
| 147 |
+
with np.load(self.root / row["target"], allow_pickle=False) as cached:
|
| 148 |
+
target = {k: torch.from_numpy(cached[k].copy()).float() for k in cached.files if k != "grid"}
|
| 149 |
+
return image_tensor(self.root / row["image"]), target
|
src/image3d_student/demo.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
from .data import cache_sample
|
| 8 |
+
from .splats import Splats, preview
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def create_demo(root, count=12, grid=8):
|
| 12 |
+
"""Procedural ellipsoids only. Never presented as outputs of any real teacher."""
|
| 13 |
+
if count < 4:
|
| 14 |
+
raise ValueError("Use at least four fixtures")
|
| 15 |
+
root = Path(root)
|
| 16 |
+
root.mkdir(parents=True, exist_ok=True)
|
| 17 |
+
rng = np.random.default_rng(17)
|
| 18 |
+
phi, theta = np.meshgrid(np.linspace(.06, np.pi-.06, 14), np.linspace(0, 2*np.pi, 28, endpoint=False))
|
| 19 |
+
unit = np.stack([np.sin(phi)*np.cos(theta), np.cos(phi), np.sin(phi)*np.sin(theta)], axis=-1).reshape(-1, 3)
|
| 20 |
+
for i in range(count):
|
| 21 |
+
radii = rng.uniform(.17, .36, 3)
|
| 22 |
+
xyz = (unit * radii).astype(np.float32)
|
| 23 |
+
colour = rng.uniform(.2, .95, 3)
|
| 24 |
+
rgb = np.clip(colour[None] * (.8 + .2 * unit[:, 1:2]), 0, 1)
|
| 25 |
+
splats = Splats(xyz, rgb, np.full_like(xyz, .035), np.tile([1,0,0,0], (len(xyz),1)), np.full((len(xyz),1), .9))
|
| 26 |
+
image_path = root / f"fixture-{i:03}.png"
|
| 27 |
+
preview(splats, size=128).save(image_path)
|
| 28 |
+
cache_sample(root, f"fixture-{i:03}", image_path, splats, grid, source="synthetic",
|
| 29 |
+
extra={"purpose": "code verification only", "object_id": f"fixture-{i:03}"})
|
| 30 |
+
image_path.unlink()
|
| 31 |
+
return root / "manifest.jsonl"
|
src/image3d_student/engine.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
import json
|
| 4 |
+
import hashlib
|
| 5 |
+
import random
|
| 6 |
+
import time
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import torch
|
| 11 |
+
from torch.utils.data import DataLoader
|
| 12 |
+
|
| 13 |
+
from .data import CachedDataset, image_tensor, load_manifest
|
| 14 |
+
from .model import Student, distillation_loss
|
| 15 |
+
from .splats import Splats, write_ply
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def device_for(name="auto"):
|
| 19 |
+
if name == "auto":
|
| 20 |
+
name = "cuda" if torch.cuda.is_available() else ("mps" if torch.backends.mps.is_available() else "cpu")
|
| 21 |
+
if name == "mps" and not torch.backends.mps.is_available():
|
| 22 |
+
raise ValueError("Metal GPU unavailable in this process. Run in a normal macOS Terminal, or use --device cpu.")
|
| 23 |
+
if name == "cuda" and not torch.cuda.is_available():
|
| 24 |
+
raise ValueError("CUDA requested but unavailable")
|
| 25 |
+
if name not in ("mps", "cpu", "cuda"):
|
| 26 |
+
raise ValueError("Supported devices: cpu, mps, cuda")
|
| 27 |
+
torch.set_num_threads(min(4, torch.get_num_threads()))
|
| 28 |
+
return torch.device(name)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def split_rows(rows, fraction, seed):
|
| 32 |
+
if not 0 <= fraction < 1:
|
| 33 |
+
raise ValueError("Validation fraction must lie in [0,1)")
|
| 34 |
+
# Keep repeated views/crops of the same object together. Supply object_id in cache metadata.
|
| 35 |
+
groups = {}
|
| 36 |
+
for row in rows:
|
| 37 |
+
group = row.get("metadata", {}).get("object_id", row["id"])
|
| 38 |
+
groups.setdefault(group, []).append(row)
|
| 39 |
+
keys = sorted(groups)
|
| 40 |
+
random.Random(seed).shuffle(keys)
|
| 41 |
+
n = max(1, round(len(keys) * fraction)) if fraction else 0
|
| 42 |
+
if n >= len(keys):
|
| 43 |
+
raise ValueError("Need at least two different objects for validation, or explicitly use --validation-fraction 0 for an overfit test")
|
| 44 |
+
val_keys = set(keys[:n])
|
| 45 |
+
train = [r for k in keys if k not in val_keys for r in groups[k]]
|
| 46 |
+
val = [r for k in keys if k in val_keys for r in groups[k]]
|
| 47 |
+
if {r["image_sha256"] for r in train} & {r["image_sha256"] for r in val}:
|
| 48 |
+
raise ValueError("Identical images occur across training/validation. Assign the same object-id to related samples.")
|
| 49 |
+
return train, val
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def on_device(target, device):
|
| 53 |
+
return {k: v.to(device) for k, v in target.items()}
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
@torch.no_grad()
|
| 57 |
+
def evaluate(model, loader, device):
|
| 58 |
+
model.eval()
|
| 59 |
+
sums = {}
|
| 60 |
+
for image, target in loader:
|
| 61 |
+
target = on_device(target, device)
|
| 62 |
+
pred = model(image.to(device))
|
| 63 |
+
loss, terms = distillation_loss(pred, target, model.grid)
|
| 64 |
+
occupied = pred["occ"].sigmoid() > .5
|
| 65 |
+
truth = target["occ"] > .5
|
| 66 |
+
terms = {**terms, "loss": loss, "occupancy_iou": (occupied & truth).sum() / (occupied | truth).sum().clamp_min(1)}
|
| 67 |
+
for key, value in terms.items():
|
| 68 |
+
sums[key] = sums.get(key, 0.) + float(value)
|
| 69 |
+
return {k: v / len(loader) for k, v in sums.items()}
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def save_checkpoint(path, model, optimizer, step, metadata):
|
| 73 |
+
payload = {"format": 1, "config": model.config, "model": {k: v.detach().cpu() for k, v in model.state_dict().items()},
|
| 74 |
+
"optimizer": optimizer.state_dict(), "step": step, "metadata": metadata}
|
| 75 |
+
tmp = Path(path).with_suffix(".tmp")
|
| 76 |
+
torch.save(payload, tmp)
|
| 77 |
+
tmp.replace(path)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def train(args):
|
| 81 |
+
if args.steps < 1 or args.batch_size < 1 or args.lr <= 0 or args.eval_every < 1:
|
| 82 |
+
raise ValueError("Steps, batch size, learning rate, and evaluation interval must be positive")
|
| 83 |
+
device = device_for(args.device)
|
| 84 |
+
random.seed(args.seed)
|
| 85 |
+
np.random.seed(args.seed)
|
| 86 |
+
torch.manual_seed(args.seed)
|
| 87 |
+
rows = load_manifest(args.manifest)
|
| 88 |
+
synthetic = any(r["source"] == "synthetic" for r in rows)
|
| 89 |
+
if synthetic and not args.allow_synthetic:
|
| 90 |
+
raise ValueError("Synthetic fixtures are code tests, not teacher data. Add --allow-synthetic for an explicit smoke test.")
|
| 91 |
+
train_rows, val_rows = split_rows(rows, args.validation_fraction, args.seed)
|
| 92 |
+
train_data = CachedDataset(args.manifest, train_rows)
|
| 93 |
+
generator = torch.Generator().manual_seed(args.seed)
|
| 94 |
+
loader = DataLoader(train_data, batch_size=args.batch_size, shuffle=True, num_workers=0, generator=generator)
|
| 95 |
+
val_loader = DataLoader(CachedDataset(args.manifest, val_rows), batch_size=1) if val_rows else None
|
| 96 |
+
model = Student(train_data.grid, args.width).to(device)
|
| 97 |
+
initialization = None
|
| 98 |
+
if getattr(args, "init_from", None):
|
| 99 |
+
initial_path = Path(args.init_from)
|
| 100 |
+
state = torch.load(initial_path, map_location="cpu", weights_only=True)
|
| 101 |
+
if state.get("format") != 1 or state["config"]["width"] != args.width:
|
| 102 |
+
raise ValueError("Initialization requires format=1 and matching --width")
|
| 103 |
+
# Coordinates are deterministic buffers, not learned weights. Rebuild them
|
| 104 |
+
# for the target grid while preserving every learned encoder/decoder weight.
|
| 105 |
+
learned = {k: v for k, v in state["model"].items() if k not in ("centers", "coords")}
|
| 106 |
+
result = model.load_state_dict(learned, strict=False)
|
| 107 |
+
if set(result.missing_keys) != {"centers", "coords"} or result.unexpected_keys:
|
| 108 |
+
raise ValueError("Checkpoint architecture mismatch")
|
| 109 |
+
initialization = {"sha256": hashlib.sha256(initial_path.read_bytes()).hexdigest(),
|
| 110 |
+
"config": state["config"], "step": state["step"],
|
| 111 |
+
"optimizer": "fresh AdamW; weights-only post-training"}
|
| 112 |
+
optimizer = torch.optim.AdamW(model.parameters(), lr=args.lr, weight_decay=1e-4)
|
| 113 |
+
run = Path(args.out)
|
| 114 |
+
if run.exists() and any(run.iterdir()):
|
| 115 |
+
raise ValueError("Run directory must be empty; choose a new --out directory")
|
| 116 |
+
run.mkdir(parents=True, exist_ok=True)
|
| 117 |
+
meta = {"synthetic_only": all(r["source"] == "synthetic" for r in rows),
|
| 118 |
+
"contains_synthetic": synthetic, "device": str(device), "torch": str(torch.__version__),
|
| 119 |
+
"seed": args.seed, "train_ids": [r["id"] for r in train_rows], "val_ids": [r["id"] for r in val_rows],
|
| 120 |
+
"parameters": sum(p.numel() for p in model.parameters()),
|
| 121 |
+
"teachers": sorted({v for r in rows for v in r["teachers"].values() if v}),
|
| 122 |
+
"validation": "held-out objects" if val_rows else "none; training overfit test only",
|
| 123 |
+
"dataset": str(Path(args.manifest).resolve()),
|
| 124 |
+
"manifest_sha256": hashlib.sha256(Path(args.manifest).read_bytes()).hexdigest(),
|
| 125 |
+
"initialization": initialization, "steps_requested": args.steps,
|
| 126 |
+
"learning_rate": args.lr, "batch_size": args.batch_size,
|
| 127 |
+
"validation_fraction": args.validation_fraction}
|
| 128 |
+
if val_loader:
|
| 129 |
+
meta["baseline_validation"] = evaluate(model, val_loader, device)
|
| 130 |
+
(run / "run.json").write_text(json.dumps(meta, indent=2))
|
| 131 |
+
print(json.dumps(meta), flush=True)
|
| 132 |
+
step, best, started = 0, float("inf"), time.monotonic()
|
| 133 |
+
iterator = iter(loader)
|
| 134 |
+
try:
|
| 135 |
+
while step < args.steps:
|
| 136 |
+
try:
|
| 137 |
+
images, target = next(iterator)
|
| 138 |
+
except StopIteration:
|
| 139 |
+
iterator = iter(loader)
|
| 140 |
+
images, target = next(iterator)
|
| 141 |
+
model.train()
|
| 142 |
+
optimizer.zero_grad(set_to_none=True)
|
| 143 |
+
pred = model(images.to(device))
|
| 144 |
+
loss, terms = distillation_loss(pred, on_device(target, device), model.grid)
|
| 145 |
+
if not torch.isfinite(loss):
|
| 146 |
+
raise RuntimeError("Nonfinite training loss; stopping")
|
| 147 |
+
loss.backward()
|
| 148 |
+
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.)
|
| 149 |
+
optimizer.step()
|
| 150 |
+
step += 1
|
| 151 |
+
if step == 1 or step % args.eval_every == 0 or step == args.steps:
|
| 152 |
+
record = {"step": step, "seconds": round(time.monotonic()-started, 3),
|
| 153 |
+
"train_loss": float(loss.detach()), **{k: float(v.detach()) for k, v in terms.items()}}
|
| 154 |
+
if val_loader:
|
| 155 |
+
record["validation"] = evaluate(model, val_loader, device)
|
| 156 |
+
score = record["validation"]["loss"]
|
| 157 |
+
if score < best:
|
| 158 |
+
best = score
|
| 159 |
+
save_checkpoint(run / "best.pt", model, optimizer, step, meta)
|
| 160 |
+
with (run / "metrics.jsonl").open("a") as handle:
|
| 161 |
+
handle.write(json.dumps(record) + "\n")
|
| 162 |
+
print(json.dumps(record), flush=True)
|
| 163 |
+
save_checkpoint(run / "last.pt", model, optimizer, step, meta)
|
| 164 |
+
except KeyboardInterrupt:
|
| 165 |
+
print("Interrupted; saving completed optimizer steps.", flush=True)
|
| 166 |
+
finally:
|
| 167 |
+
if step:
|
| 168 |
+
save_checkpoint(run / "last.pt", model, optimizer, step, meta)
|
| 169 |
+
return meta
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
@torch.no_grad()
|
| 173 |
+
def generate(checkpoint, image, out, device="auto", threshold=.5, max_splats=32768):
|
| 174 |
+
if not 0 < threshold < 1 or max_splats < 1:
|
| 175 |
+
raise ValueError("Threshold must be in (0,1) and max-splats positive")
|
| 176 |
+
device = device_for(device)
|
| 177 |
+
state = torch.load(checkpoint, map_location="cpu", weights_only=True)
|
| 178 |
+
if state.get("format") != 1 or state.get("step", 0) < 1:
|
| 179 |
+
raise ValueError("Expected a trained image3d-student checkpoint")
|
| 180 |
+
model = Student(**state["config"])
|
| 181 |
+
model.load_state_dict(state["model"])
|
| 182 |
+
model.to(device).eval()
|
| 183 |
+
if device.type == "mps":
|
| 184 |
+
torch.mps.synchronize()
|
| 185 |
+
started = time.monotonic()
|
| 186 |
+
pred = model(image_tensor(image)[None].to(device))
|
| 187 |
+
score = pred["occ"][0, :, 0].sigmoid()
|
| 188 |
+
valid = torch.nonzero(score >= threshold).flatten()
|
| 189 |
+
if len(valid) == 0:
|
| 190 |
+
raise ValueError("No occupied cells above threshold. Train longer or inspect validation; no fabricated fallback splats were exported.")
|
| 191 |
+
valid = valid[torch.argsort(score[valid], descending=True)[:max_splats]]
|
| 192 |
+
values = {k: pred[k][0, valid].cpu().numpy() for k in ("xyz", "rgb", "scale", "quat", "opacity")}
|
| 193 |
+
elapsed = time.monotonic() - started
|
| 194 |
+
splats = Splats(**values)
|
| 195 |
+
out = Path(out)
|
| 196 |
+
if out.exists() or out.with_suffix(".json").exists():
|
| 197 |
+
raise ValueError("Output already exists; choose a new filename")
|
| 198 |
+
write_ply(out, splats)
|
| 199 |
+
report = {"gaussians": len(valid), "seconds_preprocess_and_forward": elapsed,
|
| 200 |
+
"device": str(device), "checkpoint_step": state["step"], "training": state["metadata"],
|
| 201 |
+
"note": ("Synthetic-trained output is a pipeline demonstration, not a pretrained image-to-3D result."
|
| 202 |
+
if state["metadata"].get("contains_synthetic") else "Experimental cached-target student; inspect unseen-object quality.")}
|
| 203 |
+
out.with_suffix(".json").write_text(json.dumps(report, indent=2))
|
| 204 |
+
return report
|
src/image3d_student/model.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
import math
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from torch import nn
|
| 7 |
+
from torch.nn import functional as F
|
| 8 |
+
|
| 9 |
+
from .data import grid_centers
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Student(nn.Module):
|
| 13 |
+
"""Small image-conditioned coordinate MLP; fixed cells avoid unordered point regression.
|
| 14 |
+
|
| 15 |
+
15 Gaussian outputs + 2 auxiliary geometry logits. This is a coarse baseline,
|
| 16 |
+
not an architecture expected to reproduce million-splat teacher detail.
|
| 17 |
+
"""
|
| 18 |
+
def __init__(self, grid=16, width=128):
|
| 19 |
+
super().__init__()
|
| 20 |
+
if grid not in (8, 16, 32) or width < 16:
|
| 21 |
+
raise ValueError("Unsupported grid or width")
|
| 22 |
+
self.config = {"grid": grid, "width": width}
|
| 23 |
+
self.grid = grid
|
| 24 |
+
blocks, channels = [], 3
|
| 25 |
+
for out in (24, 48, 96, 128):
|
| 26 |
+
blocks.extend([nn.Conv2d(channels, out, 3, stride=2, padding=1), nn.GroupNorm(8, out), nn.SiLU()])
|
| 27 |
+
channels = out
|
| 28 |
+
self.encoder = nn.Sequential(*blocks, nn.AdaptiveAvgPool2d(4), nn.Flatten(),
|
| 29 |
+
nn.Linear(128*4*4, width), nn.SiLU())
|
| 30 |
+
centers = torch.from_numpy(grid_centers(grid))
|
| 31 |
+
freq = 2**torch.arange(4, dtype=torch.float32) * math.pi * 2
|
| 32 |
+
angles = centers[..., None] * freq
|
| 33 |
+
self.register_buffer("centers", centers)
|
| 34 |
+
self.register_buffer("coords", torch.cat([centers, angles.sin().flatten(1), angles.cos().flatten(1)], dim=1))
|
| 35 |
+
self.query = nn.Sequential(nn.Linear(27, width), nn.SiLU())
|
| 36 |
+
self.decoder = nn.Sequential(nn.Linear(width, width), nn.SiLU(), nn.Linear(width, width), nn.SiLU(), nn.Linear(width, 17))
|
| 37 |
+
with torch.no_grad():
|
| 38 |
+
self.decoder[-1].bias.zero_()
|
| 39 |
+
self.decoder[-1].bias[0] = -2
|
| 40 |
+
|
| 41 |
+
def forward(self, images):
|
| 42 |
+
features = self.encoder(images)
|
| 43 |
+
raw = self.decoder(features[:, None, :] + self.query(self.coords)[None])
|
| 44 |
+
quat = raw[..., 10:14] + raw.new_tensor([1, 0, 0, 0])
|
| 45 |
+
return {"occ": raw[..., :1], "xyz": self.centers[None] + raw[..., 1:4].tanh() / (2*self.grid),
|
| 46 |
+
"rgb": raw[..., 4:7].sigmoid(),
|
| 47 |
+
"scale": (raw[..., 7:10].clamp(-8, 5) - math.log(self.grid*3)).exp(),
|
| 48 |
+
"quat": F.normalize(quat, dim=-1, eps=1e-8), "opacity": raw[..., 14:15].sigmoid(),
|
| 49 |
+
"geometry": raw[..., 15:17]}
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def balanced_bce(logits, target):
|
| 53 |
+
error = F.binary_cross_entropy_with_logits(logits, target, reduction="none")
|
| 54 |
+
positive = (error * target).sum() / target.sum().clamp_min(1)
|
| 55 |
+
negative = (error * (1-target)).sum() / (1-target).sum().clamp_min(1)
|
| 56 |
+
return (positive + negative) / 2
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def distillation_loss(pred, target, grid):
|
| 60 |
+
mask = target["occ"]
|
| 61 |
+
def masked(value):
|
| 62 |
+
return (value * mask).sum() / (mask.sum().clamp_min(1) * value.shape[-1])
|
| 63 |
+
terms = {"occupancy": balanced_bce(pred["occ"], mask),
|
| 64 |
+
"position": masked(((pred["xyz"] - target["xyz"]) * grid).square()),
|
| 65 |
+
"colour": masked((pred["rgb"] - target["rgb"].clamp(0, 1)).square()),
|
| 66 |
+
"scale": masked((pred["scale"].log() - target["scale"].clamp_min(1e-8).log()).square()),
|
| 67 |
+
"rotation": masked(1 - (pred["quat"] * target["quat"]).sum(-1, keepdim=True).square().clamp(0, 1)),
|
| 68 |
+
"opacity": masked((pred["opacity"] - target["opacity"]).square())}
|
| 69 |
+
geometry = pred["geometry"].sum() * 0
|
| 70 |
+
for i in range(2):
|
| 71 |
+
available = target["geometry_valid"][:, i] > 0
|
| 72 |
+
if available.any():
|
| 73 |
+
geometry = geometry + balanced_bce(pred["geometry"][available, :, i:i+1], target["geometry"][available, :, i:i+1])
|
| 74 |
+
terms["geometry"] = geometry
|
| 75 |
+
total = terms["occupancy"] + terms["position"] + 2*terms["colour"] + .1*terms["scale"] + .1*terms["rotation"] + terms["opacity"] + .25*geometry
|
| 76 |
+
return total, terms
|
src/image3d_student/splats.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
"""Activated Gaussian attributes and standard degree-zero 3DGS PLY IO.
|
| 4 |
+
|
| 5 |
+
Coordinates are preserved as supplied. Idol's *exported PLY* uses its y-up frame;
|
| 6 |
+
we do not apply Idol's internal z-up conversion a second time.
|
| 7 |
+
"""
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
import numpy as np
|
| 12 |
+
from plyfile import PlyData, PlyElement
|
| 13 |
+
|
| 14 |
+
C0 = 0.28209479177387814
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@dataclass
|
| 18 |
+
class Splats:
|
| 19 |
+
xyz: np.ndarray
|
| 20 |
+
rgb: np.ndarray
|
| 21 |
+
scale: np.ndarray
|
| 22 |
+
quat: np.ndarray # w,x,y,z
|
| 23 |
+
opacity: np.ndarray
|
| 24 |
+
|
| 25 |
+
def __post_init__(self):
|
| 26 |
+
n = len(self.xyz)
|
| 27 |
+
if n == 0:
|
| 28 |
+
raise ValueError("No Gaussians: refusing to write or train on an empty object.")
|
| 29 |
+
for name, width in [("xyz", 3), ("rgb", 3), ("scale", 3), ("quat", 4), ("opacity", 1)]:
|
| 30 |
+
a = np.asarray(getattr(self, name), dtype=np.float32).reshape(-1, width)
|
| 31 |
+
if a.shape != (n, width) or not np.isfinite(a).all():
|
| 32 |
+
raise ValueError(f"Invalid {name} shape or nonfinite values")
|
| 33 |
+
setattr(self, name, a)
|
| 34 |
+
if (self.scale <= 0).any():
|
| 35 |
+
raise ValueError("Gaussian scales must be positive")
|
| 36 |
+
if (self.opacity < 0).any() or (self.opacity > 1).any():
|
| 37 |
+
raise ValueError("Opacity must lie in [0,1]")
|
| 38 |
+
norm = np.linalg.norm(self.quat, axis=1, keepdims=True)
|
| 39 |
+
if (norm < 1e-8).any():
|
| 40 |
+
raise ValueError("Zero quaternion")
|
| 41 |
+
self.quat = self.quat / norm
|
| 42 |
+
|
| 43 |
+
def take(self, indices):
|
| 44 |
+
return Splats(**{k: getattr(self, k)[indices] for k in self.__dataclass_fields__})
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def read_ply(path):
|
| 48 |
+
v = PlyData.read(str(path))["vertex"].data
|
| 49 |
+
required = ["x", "y", "z", "f_dc_0", "f_dc_1", "f_dc_2", "opacity",
|
| 50 |
+
"scale_0", "scale_1", "scale_2", "rot_0", "rot_1", "rot_2", "rot_3"]
|
| 51 |
+
if not set(required).issubset(v.dtype.names):
|
| 52 |
+
raise ValueError("Expected a Gaussian-splat PLY, not a mesh or ordinary point-cloud PLY")
|
| 53 |
+
col = lambda names: np.column_stack([v[k] for k in names]).astype(np.float32)
|
| 54 |
+
return Splats(col(["x", "y", "z"]), 0.5 + C0 * col([f"f_dc_{i}" for i in range(3)]),
|
| 55 |
+
np.exp(np.clip(col([f"scale_{i}" for i in range(3)]), -20, 5)),
|
| 56 |
+
col([f"rot_{i}" for i in range(4)]),
|
| 57 |
+
1 / (1 + np.exp(-np.clip(col(["opacity"]), -30, 30))))
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def write_ply(path, splats):
|
| 61 |
+
path = Path(path)
|
| 62 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 63 |
+
names = ["x", "y", "z", "nx", "ny", "nz", "f_dc_0", "f_dc_1", "f_dc_2", "opacity",
|
| 64 |
+
"scale_0", "scale_1", "scale_2", "rot_0", "rot_1", "rot_2", "rot_3"]
|
| 65 |
+
opacity = np.clip(splats.opacity, 1e-6, 1 - 1e-6)
|
| 66 |
+
values = np.concatenate([splats.xyz, np.zeros_like(splats.xyz), (splats.rgb - 0.5) / C0,
|
| 67 |
+
np.log(opacity / (1 - opacity)), np.log(splats.scale), splats.quat], axis=1)
|
| 68 |
+
table = np.empty(len(values), dtype=[(k, "f4") for k in names])
|
| 69 |
+
for i, name in enumerate(names):
|
| 70 |
+
table[name] = values[:, i]
|
| 71 |
+
PlyData([PlyElement.describe(table, "vertex")], text=False).write(str(path))
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def rotation_matrices(q):
|
| 75 |
+
w, x, y, z = q.T
|
| 76 |
+
return np.stack([1-2*(y*y+z*z), 2*(x*y-z*w), 2*(x*z+y*w),
|
| 77 |
+
2*(x*y+z*w), 1-2*(x*x+z*z), 2*(y*z-x*w),
|
| 78 |
+
2*(x*z-y*w), 2*(y*z+x*w), 1-2*(x*x+y*y)], axis=-1).reshape(-1, 3, 3)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def preview(splats, size=256, yaw=0.0):
|
| 82 |
+
"""CPU orthographic Gaussian preview; debugging only, not an evaluation renderer."""
|
| 83 |
+
from PIL import Image
|
| 84 |
+
c, s = np.cos(yaw), np.sin(yaw)
|
| 85 |
+
view = np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]], dtype=np.float32)
|
| 86 |
+
xyz = splats.xyz @ view.T
|
| 87 |
+
rot = rotation_matrices(splats.quat)
|
| 88 |
+
cov = (rot * (splats.scale ** 2)[:, None, :]) @ rot.transpose(0, 2, 1)
|
| 89 |
+
cov = view[None] @ cov @ view.T[None]
|
| 90 |
+
pixels = size / 1.25
|
| 91 |
+
canvas = np.zeros((size, size, 3), dtype=np.float32)
|
| 92 |
+
# Back to front, camera on +z. Image y points down.
|
| 93 |
+
for i in np.argsort(xyz[:, 2]):
|
| 94 |
+
center = np.array([xyz[i, 0], -xyz[i, 1]]) * pixels + size / 2
|
| 95 |
+
flip = np.diag([1., -1.])
|
| 96 |
+
covariance = flip @ cov[i, :2, :2] @ flip * pixels ** 2 + np.eye(2) * 0.3
|
| 97 |
+
radius = min(size, int(np.ceil(3 * np.sqrt(np.linalg.eigvalsh(covariance).max()))))
|
| 98 |
+
lo = np.maximum(0, np.floor(center - radius).astype(int))
|
| 99 |
+
hi = np.minimum(size, np.ceil(center + radius + 1).astype(int))
|
| 100 |
+
if np.any(hi <= lo):
|
| 101 |
+
continue
|
| 102 |
+
yy, xx = np.mgrid[lo[1]:hi[1], lo[0]:hi[0]]
|
| 103 |
+
delta = np.stack([xx - center[0], yy - center[1]], axis=-1)
|
| 104 |
+
exponent = np.einsum("...i,ij,...j->...", delta, np.linalg.inv(covariance), delta)
|
| 105 |
+
alpha = (splats.opacity[i, 0] * np.exp(-0.5 * exponent)).clip(0, .99)[..., None]
|
| 106 |
+
patch = canvas[lo[1]:hi[1], lo[0]:hi[0]]
|
| 107 |
+
patch[:] = patch * (1-alpha) + np.clip(splats.rgb[i], 0, 1) * alpha
|
| 108 |
+
return Image.fromarray((canvas.clip(0, 1) * 255).astype(np.uint8))
|
src/image3d_student/teacher_idol.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# Copyright 2026 Lucasgitguy-co
|
| 3 |
+
"""Experimental, staged Idol inference for Apple MPS.
|
| 4 |
+
|
| 5 |
+
Upstream revision: 4702a7f00850e34b6e83a2170cc123c3e9570744.
|
| 6 |
+
No weights are bundled. Full-weight inference has NOT been verified by this project.
|
| 7 |
+
"""
|
| 8 |
+
import gc
|
| 9 |
+
import json
|
| 10 |
+
import os
|
| 11 |
+
import shutil
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
|
| 14 |
+
import numpy as np
|
| 15 |
+
import torch
|
| 16 |
+
from PIL import Image
|
| 17 |
+
|
| 18 |
+
from .engine import device_for
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def patch_attention(layers, query_chunk=128):
|
| 22 |
+
"""Bound attention working memory by splitting only the query dimension.
|
| 23 |
+
|
| 24 |
+
Each query still sees ALL keys/values. Does not split or change softmax's key axis.
|
| 25 |
+
Disable CUDA-specific optimized sampling and avoid complex RoPE tensors on MPS.
|
| 26 |
+
"""
|
| 27 |
+
if query_chunk < 1:
|
| 28 |
+
raise ValueError("query_chunk must be positive")
|
| 29 |
+
from torch.nn import functional as F
|
| 30 |
+
|
| 31 |
+
def attention(*args):
|
| 32 |
+
q, k, v = args[0].unbind(dim=2) if len(args) == 1 else args
|
| 33 |
+
q, k, v = (t.permute(0, 2, 1, 3) for t in (q, k, v))
|
| 34 |
+
chunks = [F.scaled_dot_product_attention(q[:, :, start:start+query_chunk], k, v)
|
| 35 |
+
for start in range(0, q.shape[2], query_chunk)]
|
| 36 |
+
return torch.cat(chunks, dim=2).permute(0, 2, 1, 3)
|
| 37 |
+
|
| 38 |
+
layers.scaled_dot_product_attention = attention
|
| 39 |
+
layers.RePo3DRotaryEmbedding.real_valued = True
|
| 40 |
+
return attention
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def release(device):
|
| 44 |
+
gc.collect()
|
| 45 |
+
if device.type == "mps":
|
| 46 |
+
torch.mps.synchronize()
|
| 47 |
+
torch.mps.empty_cache()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
@torch.no_grad()
|
| 51 |
+
def run_idol(args):
|
| 52 |
+
if args.steps < 1 or args.gaussians < 32768 or args.gaussians > 1048576 or args.gaussians % 32:
|
| 53 |
+
raise ValueError("Steps must be positive; Idol requires 32768..1048576 Gaussians, divisible by 32")
|
| 54 |
+
device = device_for(args.device)
|
| 55 |
+
cache = Path(args.cache).resolve()
|
| 56 |
+
cache.mkdir(parents=True, exist_ok=True)
|
| 57 |
+
os.environ["HF_HOME"] = str(cache)
|
| 58 |
+
os.environ["HF_HUB_OFFLINE"] = "0" if args.allow_download else "1"
|
| 59 |
+
os.environ["ATTN_BACKEND"] = "sdpa"
|
| 60 |
+
if args.allow_download and shutil.disk_usage(cache).free < 16 * 1024**3:
|
| 61 |
+
raise ValueError("Less than 16 GiB disk space available. This conservative download guard leaves room for teacher weights, encoders and temporary files. Free space, or use an existing complete cache without --allow-download.")
|
| 62 |
+
try:
|
| 63 |
+
from huggingface_hub import hf_hub_download, snapshot_download
|
| 64 |
+
from safetensors.torch import load_file
|
| 65 |
+
from tensordict import TensorDict
|
| 66 |
+
from idol.conditioning import ImageEncoder, require_encoder_access
|
| 67 |
+
from idol.models import BiRefNet, GaussianFixedlenDecoder, LatentSeqMMFlowModel, OctreeProbabilityFixedlenDecoder, layers
|
| 68 |
+
from idol.preprocess import CROP_MARGIN, preprocess_image, has_real_alpha
|
| 69 |
+
from idol.sampler import sample_flow
|
| 70 |
+
from idol.camera import decode_camera
|
| 71 |
+
except ImportError as exc:
|
| 72 |
+
raise RuntimeError("Install the optional teacher dependencies: python -m pip install '.[teacher]'") from exc
|
| 73 |
+
out = Path(args.out)
|
| 74 |
+
if out.exists():
|
| 75 |
+
raise ValueError("Teacher output directory already exists; choose a new --out")
|
| 76 |
+
# Local-only by default. Hugging Face authentication is never written to project files.
|
| 77 |
+
config_path = hf_hub_download(args.weights, "config.json", local_files_only=not args.allow_download)
|
| 78 |
+
config = json.loads(Path(config_path).read_text())
|
| 79 |
+
encoders = config["image_encoders"]
|
| 80 |
+
require_encoder_access([encoders["dinov3"], encoders["flux2_vae"]])
|
| 81 |
+
weights = Path(snapshot_download(args.weights, allow_patterns=["config.json", "*.safetensors"],
|
| 82 |
+
local_files_only=not args.allow_download))
|
| 83 |
+
patch_attention(layers, args.query_chunk)
|
| 84 |
+
torch.manual_seed(args.seed)
|
| 85 |
+
|
| 86 |
+
def load(model, name):
|
| 87 |
+
state = load_file(str(weights / f"{name}.safetensors"), device="cpu")
|
| 88 |
+
model.load_state_dict(state, strict=True)
|
| 89 |
+
del state
|
| 90 |
+
return model.eval().requires_grad_(False).to(device)
|
| 91 |
+
|
| 92 |
+
print("Experimental Idol MPS adapter: full-weight compatibility and quality remain unverified.", flush=True)
|
| 93 |
+
with Image.open(args.image) as opened:
|
| 94 |
+
image = opened.copy()
|
| 95 |
+
background = None
|
| 96 |
+
if not has_real_alpha(image):
|
| 97 |
+
print("Stage 1: background removal", flush=True)
|
| 98 |
+
background = load(BiRefNet(), "birefnet")
|
| 99 |
+
prepared = preprocess_image(image, background, config["image_size"], CROP_MARGIN[args.mode])
|
| 100 |
+
del background
|
| 101 |
+
release(device)
|
| 102 |
+
pixels = torch.from_numpy(np.array(prepared)).permute(2, 0, 1).float()[None].div(255).to(device)
|
| 103 |
+
print("Stage 2: image conditioning", flush=True)
|
| 104 |
+
encoder = ImageEncoder(encoders["dinov3"], encoders["flux2_vae"], device="cpu")
|
| 105 |
+
# Both encoders initially reside on CPU; only the active encoder is moved to MPS.
|
| 106 |
+
encoder.dinov3.to(device)
|
| 107 |
+
feature1 = encoder._dinov3_tokens(pixels)
|
| 108 |
+
del encoder.dinov3
|
| 109 |
+
release(device)
|
| 110 |
+
encoder.flux2_vae.to(device)
|
| 111 |
+
feature2 = encoder._flux2_tokens(pixels)
|
| 112 |
+
del encoder, pixels
|
| 113 |
+
release(device)
|
| 114 |
+
cond = TensorDict({"feature1": feature1, "feature2": feature2}, batch_size=1, device=device)
|
| 115 |
+
print("Stage 3: latent sampling", flush=True)
|
| 116 |
+
model = load(LatentSeqMMFlowModel(**config["denoiser"]["args"]), "denoiser")
|
| 117 |
+
noise = TensorDict({"latent": torch.randn(1, model.q_token_length, model.in_channels, device=device),
|
| 118 |
+
"camera": torch.randn(1, 1, model.cam_channels, device=device)}, batch_size=1, device=device)
|
| 119 |
+
result = sample_flow(model, noise, cond=cond, neg_cond=cond.apply(torch.zeros_like), steps=args.steps,
|
| 120 |
+
rescale_t=3., cfg_strength=3., verbose=True, batch_cfg=False)
|
| 121 |
+
latent, camera = result["latent"], decode_camera(result["camera"][0, 0])
|
| 122 |
+
del model, noise, cond, feature1, feature2, result
|
| 123 |
+
release(device)
|
| 124 |
+
print("Stage 4: octree decoding", flush=True)
|
| 125 |
+
model = load(OctreeProbabilityFixedlenDecoder(**config["decoder"]["args"]), "decoder")
|
| 126 |
+
per_anchor = config["decoder_gs"]["args"]["representation_config"]["num_gaussians"]
|
| 127 |
+
anchors = OctreeProbabilityFixedlenDecoder.sample(model, latent, num_points=args.gaussians // per_anchor,
|
| 128 |
+
level=config["max_voxel_level"])
|
| 129 |
+
del model
|
| 130 |
+
release(device)
|
| 131 |
+
print("Stage 5: Gaussian decoding", flush=True)
|
| 132 |
+
model = load(GaussianFixedlenDecoder(**config["decoder_gs"]["args"]), "decoder_gs")
|
| 133 |
+
gaussian = model.to_representation(anchors, model(anchors, latent))[0]
|
| 134 |
+
out.mkdir(parents=True)
|
| 135 |
+
gaussian.save_ply(str(out / "teacher.ply"))
|
| 136 |
+
prepared.save(out / "input.png")
|
| 137 |
+
(out / "metadata.json").write_text(json.dumps({"teacher": args.weights, "steps": args.steps,
|
| 138 |
+
"gaussians": args.gaussians, "seed": args.seed, "device": str(device), "camera": camera,
|
| 139 |
+
"sdk_revision": "4702a7f00850e34b6e83a2170cc123c3e9570744", "weights_snapshot": weights.name,
|
| 140 |
+
"coordinate_frame": "Idol exported PLY (y-up)", "adapter": "experimental staged MPS"}, indent=2))
|
| 141 |
+
print(f"Teacher output saved to {out}", flush=True)
|
stage_v2.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Evaluate versus pinned forma3d-tiny, stage honest v2 metadata, never upload."""
|
| 2 |
+
import argparse
|
| 3 |
+
import hashlib
|
| 4 |
+
import importlib.util
|
| 5 |
+
import json
|
| 6 |
+
import shutil
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import torch
|
| 9 |
+
from audit_data import audit
|
| 10 |
+
|
| 11 |
+
ROOT = Path(__file__).resolve().parent
|
| 12 |
+
REV = 'e80b8d895cc2e405894473abe4ce40b3472eaca7'
|
| 13 |
+
BASE = 'Lucasgitguy-co/forma3d-tiny'
|
| 14 |
+
|
| 15 |
+
def sha(p):
|
| 16 |
+
return hashlib.sha256(Path(p).read_bytes()).hexdigest()
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
p = argparse.ArgumentParser()
|
| 21 |
+
for name in ('run','data','base','release'):
|
| 22 |
+
p.add_argument('--'+name, required=True)
|
| 23 |
+
p.add_argument('--device', default='mps', choices=['mps','cpu'])
|
| 24 |
+
args = p.parse_args()
|
| 25 |
+
destination = Path(args.release)
|
| 26 |
+
if destination.exists():
|
| 27 |
+
raise ValueError('Release directory exists; choose a fresh --release path')
|
| 28 |
+
audit(args.data, ROOT / 'upstream/ATTRIBUTIONS.json')
|
| 29 |
+
# Ensure the asserted parent is the pinned public checkpoint, not another run.
|
| 30 |
+
if sha(args.base) != '83c065d2a50cf086928542cf8d40dc4416b379e38b9aa61d842dc9106d4b3306':
|
| 31 |
+
raise ValueError('Expected the pinned published forma3d-tiny model.pt')
|
| 32 |
+
spec = importlib.util.spec_from_file_location('pinned_release', ROOT / 'upstream/scripts/release.py')
|
| 33 |
+
module = importlib.util.module_from_spec(spec)
|
| 34 |
+
spec.loader.exec_module(module)
|
| 35 |
+
checkpoint, meta, report = module.evaluate_release(args)
|
| 36 |
+
# Upstream gate checks completed training, validation loss, held-out loss/IoU,
|
| 37 |
+
# source separation, initialization hash and nonempty qualitative predictions.
|
| 38 |
+
source = ROOT / 'upstream'
|
| 39 |
+
destination.mkdir(parents=True)
|
| 40 |
+
for folder in ('src','licenses'):
|
| 41 |
+
shutil.copytree(source / folder, destination / folder,
|
| 42 |
+
ignore=shutil.ignore_patterns('__pycache__','*.egg-info'))
|
| 43 |
+
for name in ('LICENSE','NOTICE','THIRD_PARTY.md','DATASETS.md','requirements-tested-macos.txt'):
|
| 44 |
+
shutil.copyfile(source / name, destination / name)
|
| 45 |
+
project = (source / 'pyproject.toml').read_text()
|
| 46 |
+
project = project.replace('name = "forma3d-tiny"','name = "forma3d-tiny-2"')
|
| 47 |
+
project = project.replace('version = "0.2.0"','version = "0.3.0"')
|
| 48 |
+
(destination / 'pyproject.toml').write_text(project)
|
| 49 |
+
(destination / 'scripts').mkdir()
|
| 50 |
+
for name in ('prepare_hf.py','release.py'):
|
| 51 |
+
shutil.copyfile(source / 'scripts' / name, destination / 'scripts' / name)
|
| 52 |
+
# This file set can reproduce the training recipe from pinned upstream.
|
| 53 |
+
for name in ('posttrain.sh','prepare_v2.py','audit_data.py','stage_v2.py','publish_v2.py','COMMANDS.md'):
|
| 54 |
+
shutil.copyfile(ROOT / name, destination / name)
|
| 55 |
+
public_meta = {k:v for k,v in meta.items() if k != 'dataset'}
|
| 56 |
+
public_meta.update({'base_model':BASE, 'base_revision':REV,
|
| 57 |
+
'training_method':'weights-only continuation; fresh AdamW',
|
| 58 |
+
'known_v1_assets_excluded':True})
|
| 59 |
+
torch.save({'format':1,'config':checkpoint['config'],'model':checkpoint['model'],
|
| 60 |
+
'step':checkpoint['step'],'metadata':public_meta}, destination / 'model.pt')
|
| 61 |
+
(destination / 'config.json').write_text(json.dumps(checkpoint['config'],indent=2))
|
| 62 |
+
(destination / 'training.json').write_text(json.dumps(public_meta,indent=2))
|
| 63 |
+
report.update({'base_model':BASE,'base_revision':REV,
|
| 64 |
+
'published_model_sha256':sha(destination / 'model.pt')})
|
| 65 |
+
(destination / 'evaluation.json').write_text(json.dumps(report,indent=2))
|
| 66 |
+
for src, dst in [('sources.json','ATTRIBUTIONS.json'),('v2-audit.json','data-audit.json'),
|
| 67 |
+
('preparation.json','preparation.json')]:
|
| 68 |
+
# preparation.json includes user-local paths; publish only portable fields.
|
| 69 |
+
if src == 'preparation.json':
|
| 70 |
+
prepared = json.loads((Path(args.data) / src).read_text())
|
| 71 |
+
prepared['settings'] = {k:v for k,v in prepared['settings'].items() if k not in ('out','cache')}
|
| 72 |
+
(destination / dst).write_text(json.dumps(prepared,indent=2))
|
| 73 |
+
else:
|
| 74 |
+
shutil.copyfile(Path(args.data) / src,destination / dst)
|
| 75 |
+
shutil.copyfile(Path(args.run) / 'metrics.jsonl',destination / 'metrics.jsonl')
|
| 76 |
+
if (ROOT / 'runs/environment.txt').exists():
|
| 77 |
+
lines = (ROOT / 'runs/environment.txt').read_text().splitlines()
|
| 78 |
+
portable = [line for line in lines if ' @ file:' not in line and not line.startswith('-e ')]
|
| 79 |
+
portable.append('# Local forma3d-tiny 0.2.0 source: '+BASE+' @ '+REV)
|
| 80 |
+
(destination / 'environment.txt').write_text('\n'.join(portable)+'\n')
|
| 81 |
+
datasets = sorted({r['repo'] for r in json.loads((destination / 'ATTRIBUTIONS.json').read_text())})
|
| 82 |
+
dataset_yaml = '\n'.join('- '+d for d in datasets)
|
| 83 |
+
bt, ft = report['baseline_test'],report['posttrained_test']
|
| 84 |
+
card = f'''---
|
| 85 |
+
license: apache-2.0
|
| 86 |
+
pipeline_tag: image-to-3d
|
| 87 |
+
base_model: {BASE}
|
| 88 |
+
base_model_relation: finetune
|
| 89 |
+
model_name: Forma3D Tiny 2
|
| 90 |
+
datasets:
|
| 91 |
+
{dataset_yaml}
|
| 92 |
+
tags:
|
| 93 |
+
- pytorch
|
| 94 |
+
- gaussian-splatting
|
| 95 |
+
- mesh-proxy-supervision
|
| 96 |
+
- research-prototype
|
| 97 |
+
---
|
| 98 |
+
# Forma3D Tiny 2
|
| 99 |
+
|
| 100 |
+
A {meta['parameters']:,}-parameter image-to-Gaussian research model continued from
|
| 101 |
+
{BASE} at revision `{REV}`. The optimizer starts fresh. It uses a
|
| 102 |
+
{checkpoint['config']['grid']} cubed grid and width {checkpoint['config']['width']}.
|
| 103 |
+
Selected checkpoint step {checkpoint['step']} from {meta['steps_requested']} completed steps.
|
| 104 |
+
|
| 105 |
+
## Training
|
| 106 |
+
|
| 107 |
+
See training.json and metrics.jsonl for actual optimizer settings and validation
|
| 108 |
+
history. Inputs are orthographic renders of centered mesh-derived Gaussian proxy
|
| 109 |
+
clouds on black. Targets are isotropic surface samples compressed to one Gaussian
|
| 110 |
+
per occupied grid cell. No pretrained 3D teacher was run. No natural-photo training
|
| 111 |
+
or differentiable rendering is claimed. Asset IDs, known source groups, and exact
|
| 112 |
+
mesh hashes from all 96 known v1 assets were excluded. Near duplicates can remain.
|
| 113 |
+
The separate 15 percent object-group test split does not enter checkpoint selection;
|
| 114 |
+
20 percent of development groups supply validation. Counts are realized counts in
|
| 115 |
+
preparation.json and data-audit.json, not assumed independent asset counts.
|
| 116 |
+
|
| 117 |
+
## Evaluation
|
| 118 |
+
|
| 119 |
+
Both checkpoints were evaluated on the same new held-out proxy data.
|
| 120 |
+
|
| 121 |
+
| Metric | Starting Forma3D Tiny | Selected Forma3D Tiny 2 |
|
| 122 |
+
|---|---:|---:|
|
| 123 |
+
| Mean test loss | {bt['loss']:.6f} | {ft['loss']:.6f} |
|
| 124 |
+
| Mean view occupancy IoU at threshold 0.5 | {bt['occupancy_iou']:.6f} | {ft['occupancy_iou']:.6f} |
|
| 125 |
+
|
| 126 |
+
Test groups: {report['test_objects']}; views: {report['test_samples']}.
|
| 127 |
+
See evaluation.json for hashes and release gates. Metrics are view-averaged over
|
| 128 |
+
proxy renders. They do not establish photo realism, novel-view image quality,
|
| 129 |
+
mesh quality, texture fidelity, gameplay, or general-purpose 3D reconstruction.
|
| 130 |
+
|
| 131 |
+
## Use
|
| 132 |
+
|
| 133 |
+
Install this source with Python 3.11 and `uv pip install ".[data]"` in a virtual
|
| 134 |
+
environment, then run:
|
| 135 |
+
|
| 136 |
+
```bash
|
| 137 |
+
image3d generate --checkpoint model.pt --image your-centered-cutout.png \\
|
| 138 |
+
--out results/object.ply --device mps
|
| 139 |
+
image3d preview --ply results/object.ply --out results/preview.png
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
Output is Gaussian PLY. Inputs should match centered objects on black. See
|
| 143 |
+
COMMANDS.md for the complete continuation recipe. This model retains the same
|
| 144 |
+
architecture; no text-prompt conditioning or new mesh decoder was added.
|
| 145 |
+
|
| 146 |
+
## License
|
| 147 |
+
|
| 148 |
+
Publisher source and weights are offered under Apache 2.0. Retain NOTICE,
|
| 149 |
+
THIRD_PARTY.md and ATTRIBUTIONS.json. CC BY Scenario assets and CC0 Objaverse
|
| 150 |
+
assets have their own attribution and license records in DATASETS.md.
|
| 151 |
+
'''
|
| 152 |
+
(destination / 'README.md').write_text(card)
|
| 153 |
+
sums = [sha(f)+' '+str(f.relative_to(destination)) for f in sorted(destination.rglob('*')) if f.is_file()]
|
| 154 |
+
(destination / 'SHA256SUMS').write_text('\n'.join(sums)+'\n')
|
| 155 |
+
print('Staged evaluated release at',destination)
|
| 156 |
+
print('Inspect PNG files under',Path(args.run)/'inspection','before optional publication.')
|
| 157 |
+
|
| 158 |
+
if __name__ == '__main__':
|
| 159 |
+
main()
|
training.json
ADDED
|
@@ -0,0 +1,914 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"synthetic_only": false,
|
| 3 |
+
"contains_synthetic": false,
|
| 4 |
+
"device": "mps",
|
| 5 |
+
"torch": "2.15.0",
|
| 6 |
+
"seed": 43,
|
| 7 |
+
"train_ids": [
|
| 8 |
+
"35d930c154dd6808d06a-v0",
|
| 9 |
+
"35d930c154dd6808d06a-v1",
|
| 10 |
+
"35d930c154dd6808d06a-v2",
|
| 11 |
+
"35d930c154dd6808d06a-v3",
|
| 12 |
+
"58da43454d31f401a82a-v0",
|
| 13 |
+
"58da43454d31f401a82a-v1",
|
| 14 |
+
"58da43454d31f401a82a-v2",
|
| 15 |
+
"58da43454d31f401a82a-v3",
|
| 16 |
+
"d4453f35dd55b9fdbe77-v0",
|
| 17 |
+
"d4453f35dd55b9fdbe77-v1",
|
| 18 |
+
"d4453f35dd55b9fdbe77-v2",
|
| 19 |
+
"d4453f35dd55b9fdbe77-v3",
|
| 20 |
+
"53abbbe27d8cae248360-v0",
|
| 21 |
+
"53abbbe27d8cae248360-v1",
|
| 22 |
+
"53abbbe27d8cae248360-v2",
|
| 23 |
+
"53abbbe27d8cae248360-v3",
|
| 24 |
+
"083ef10612b14e4fd91f-v0",
|
| 25 |
+
"083ef10612b14e4fd91f-v1",
|
| 26 |
+
"083ef10612b14e4fd91f-v2",
|
| 27 |
+
"083ef10612b14e4fd91f-v3",
|
| 28 |
+
"dcd7b3ffd85049cfe762-v0",
|
| 29 |
+
"dcd7b3ffd85049cfe762-v1",
|
| 30 |
+
"dcd7b3ffd85049cfe762-v2",
|
| 31 |
+
"dcd7b3ffd85049cfe762-v3",
|
| 32 |
+
"89f2af1771b298088496-v0",
|
| 33 |
+
"89f2af1771b298088496-v1",
|
| 34 |
+
"89f2af1771b298088496-v2",
|
| 35 |
+
"89f2af1771b298088496-v3",
|
| 36 |
+
"aa87bf05321d963c3de9-v0",
|
| 37 |
+
"aa87bf05321d963c3de9-v1",
|
| 38 |
+
"aa87bf05321d963c3de9-v2",
|
| 39 |
+
"aa87bf05321d963c3de9-v3",
|
| 40 |
+
"8b5e78df6cc3bc96ca2a-v0",
|
| 41 |
+
"8b5e78df6cc3bc96ca2a-v1",
|
| 42 |
+
"8b5e78df6cc3bc96ca2a-v2",
|
| 43 |
+
"8b5e78df6cc3bc96ca2a-v3",
|
| 44 |
+
"d186a120c94371ef7a9d-v0",
|
| 45 |
+
"d186a120c94371ef7a9d-v1",
|
| 46 |
+
"d186a120c94371ef7a9d-v2",
|
| 47 |
+
"d186a120c94371ef7a9d-v3",
|
| 48 |
+
"13fc4338908a8073ab71-v0",
|
| 49 |
+
"13fc4338908a8073ab71-v1",
|
| 50 |
+
"13fc4338908a8073ab71-v2",
|
| 51 |
+
"13fc4338908a8073ab71-v3",
|
| 52 |
+
"95b87385ad6d55cb43a9-v0",
|
| 53 |
+
"95b87385ad6d55cb43a9-v1",
|
| 54 |
+
"95b87385ad6d55cb43a9-v2",
|
| 55 |
+
"95b87385ad6d55cb43a9-v3",
|
| 56 |
+
"a9324f8dc45d1616a80a-v0",
|
| 57 |
+
"a9324f8dc45d1616a80a-v1",
|
| 58 |
+
"a9324f8dc45d1616a80a-v2",
|
| 59 |
+
"a9324f8dc45d1616a80a-v3",
|
| 60 |
+
"3b24fd8b39813b859983-v0",
|
| 61 |
+
"3b24fd8b39813b859983-v1",
|
| 62 |
+
"3b24fd8b39813b859983-v2",
|
| 63 |
+
"3b24fd8b39813b859983-v3",
|
| 64 |
+
"4f6ac99f7fd499a7d43c-v0",
|
| 65 |
+
"4f6ac99f7fd499a7d43c-v1",
|
| 66 |
+
"4f6ac99f7fd499a7d43c-v2",
|
| 67 |
+
"4f6ac99f7fd499a7d43c-v3",
|
| 68 |
+
"42d2395a94668a58c4ad-v0",
|
| 69 |
+
"42d2395a94668a58c4ad-v1",
|
| 70 |
+
"42d2395a94668a58c4ad-v2",
|
| 71 |
+
"42d2395a94668a58c4ad-v3",
|
| 72 |
+
"b1deb9d963a2cd12e16c-v0",
|
| 73 |
+
"b1deb9d963a2cd12e16c-v1",
|
| 74 |
+
"b1deb9d963a2cd12e16c-v2",
|
| 75 |
+
"b1deb9d963a2cd12e16c-v3",
|
| 76 |
+
"b4afe853eaef7b154d1f-v0",
|
| 77 |
+
"b4afe853eaef7b154d1f-v1",
|
| 78 |
+
"b4afe853eaef7b154d1f-v2",
|
| 79 |
+
"b4afe853eaef7b154d1f-v3",
|
| 80 |
+
"12e6fa6b9b3fde9b0daf-v0",
|
| 81 |
+
"12e6fa6b9b3fde9b0daf-v1",
|
| 82 |
+
"12e6fa6b9b3fde9b0daf-v2",
|
| 83 |
+
"12e6fa6b9b3fde9b0daf-v3",
|
| 84 |
+
"8b2af62279572f1c348d-v0",
|
| 85 |
+
"8b2af62279572f1c348d-v1",
|
| 86 |
+
"8b2af62279572f1c348d-v2",
|
| 87 |
+
"8b2af62279572f1c348d-v3",
|
| 88 |
+
"9b2928d8e9b6e432b208-v0",
|
| 89 |
+
"9b2928d8e9b6e432b208-v1",
|
| 90 |
+
"9b2928d8e9b6e432b208-v2",
|
| 91 |
+
"9b2928d8e9b6e432b208-v3",
|
| 92 |
+
"0b603cd8889a5a16f625-v0",
|
| 93 |
+
"0b603cd8889a5a16f625-v1",
|
| 94 |
+
"0b603cd8889a5a16f625-v2",
|
| 95 |
+
"0b603cd8889a5a16f625-v3",
|
| 96 |
+
"aa7a5a2f97543fcc8c24-v0",
|
| 97 |
+
"aa7a5a2f97543fcc8c24-v1",
|
| 98 |
+
"aa7a5a2f97543fcc8c24-v2",
|
| 99 |
+
"aa7a5a2f97543fcc8c24-v3",
|
| 100 |
+
"d4547a44652027eec362-v0",
|
| 101 |
+
"d4547a44652027eec362-v1",
|
| 102 |
+
"d4547a44652027eec362-v2",
|
| 103 |
+
"d4547a44652027eec362-v3",
|
| 104 |
+
"09eda16a9ffee9043ad8-v0",
|
| 105 |
+
"09eda16a9ffee9043ad8-v1",
|
| 106 |
+
"09eda16a9ffee9043ad8-v2",
|
| 107 |
+
"09eda16a9ffee9043ad8-v3",
|
| 108 |
+
"149bc550a730196bfb3e-v0",
|
| 109 |
+
"149bc550a730196bfb3e-v1",
|
| 110 |
+
"149bc550a730196bfb3e-v2",
|
| 111 |
+
"149bc550a730196bfb3e-v3",
|
| 112 |
+
"ba456d938777d3d97c7a-v0",
|
| 113 |
+
"ba456d938777d3d97c7a-v1",
|
| 114 |
+
"ba456d938777d3d97c7a-v2",
|
| 115 |
+
"ba456d938777d3d97c7a-v3",
|
| 116 |
+
"b1d9ca1078489bcc8b59-v0",
|
| 117 |
+
"b1d9ca1078489bcc8b59-v1",
|
| 118 |
+
"b1d9ca1078489bcc8b59-v2",
|
| 119 |
+
"b1d9ca1078489bcc8b59-v3",
|
| 120 |
+
"e2774da2d6fdbf320005-v0",
|
| 121 |
+
"e2774da2d6fdbf320005-v1",
|
| 122 |
+
"e2774da2d6fdbf320005-v2",
|
| 123 |
+
"e2774da2d6fdbf320005-v3",
|
| 124 |
+
"9accb35147475a98151b-v0",
|
| 125 |
+
"9accb35147475a98151b-v1",
|
| 126 |
+
"9accb35147475a98151b-v2",
|
| 127 |
+
"9accb35147475a98151b-v3",
|
| 128 |
+
"9154f51711aa5cad741c-v0",
|
| 129 |
+
"9154f51711aa5cad741c-v1",
|
| 130 |
+
"9154f51711aa5cad741c-v2",
|
| 131 |
+
"9154f51711aa5cad741c-v3",
|
| 132 |
+
"714a6ef2a9b86176887e-v0",
|
| 133 |
+
"714a6ef2a9b86176887e-v1",
|
| 134 |
+
"714a6ef2a9b86176887e-v2",
|
| 135 |
+
"714a6ef2a9b86176887e-v3",
|
| 136 |
+
"f475d0b020f840a08da6-v0",
|
| 137 |
+
"f475d0b020f840a08da6-v1",
|
| 138 |
+
"f475d0b020f840a08da6-v2",
|
| 139 |
+
"f475d0b020f840a08da6-v3",
|
| 140 |
+
"b4becf168c3c3e72d1a1-v0",
|
| 141 |
+
"b4becf168c3c3e72d1a1-v1",
|
| 142 |
+
"b4becf168c3c3e72d1a1-v2",
|
| 143 |
+
"b4becf168c3c3e72d1a1-v3",
|
| 144 |
+
"a93c298b61fac88b7815-v0",
|
| 145 |
+
"a93c298b61fac88b7815-v1",
|
| 146 |
+
"a93c298b61fac88b7815-v2",
|
| 147 |
+
"a93c298b61fac88b7815-v3",
|
| 148 |
+
"f098047bad1f2c42e522-v0",
|
| 149 |
+
"f098047bad1f2c42e522-v1",
|
| 150 |
+
"f098047bad1f2c42e522-v2",
|
| 151 |
+
"f098047bad1f2c42e522-v3",
|
| 152 |
+
"6f5650ce949ed44855db-v0",
|
| 153 |
+
"6f5650ce949ed44855db-v1",
|
| 154 |
+
"6f5650ce949ed44855db-v2",
|
| 155 |
+
"6f5650ce949ed44855db-v3",
|
| 156 |
+
"d835f0e933e6a5401e5f-v0",
|
| 157 |
+
"d835f0e933e6a5401e5f-v1",
|
| 158 |
+
"d835f0e933e6a5401e5f-v2",
|
| 159 |
+
"d835f0e933e6a5401e5f-v3",
|
| 160 |
+
"e141279821ff0cd00133-v0",
|
| 161 |
+
"e141279821ff0cd00133-v1",
|
| 162 |
+
"e141279821ff0cd00133-v2",
|
| 163 |
+
"e141279821ff0cd00133-v3",
|
| 164 |
+
"8a833c632913fb94bcfc-v0",
|
| 165 |
+
"8a833c632913fb94bcfc-v1",
|
| 166 |
+
"8a833c632913fb94bcfc-v2",
|
| 167 |
+
"8a833c632913fb94bcfc-v3",
|
| 168 |
+
"d588a44a75ac91946615-v0",
|
| 169 |
+
"d588a44a75ac91946615-v1",
|
| 170 |
+
"d588a44a75ac91946615-v2",
|
| 171 |
+
"d588a44a75ac91946615-v3",
|
| 172 |
+
"161a50c2c6a490dba3b4-v0",
|
| 173 |
+
"161a50c2c6a490dba3b4-v1",
|
| 174 |
+
"161a50c2c6a490dba3b4-v2",
|
| 175 |
+
"161a50c2c6a490dba3b4-v3",
|
| 176 |
+
"f7ad3a2b9911e7f6ea52-v0",
|
| 177 |
+
"f7ad3a2b9911e7f6ea52-v1",
|
| 178 |
+
"f7ad3a2b9911e7f6ea52-v2",
|
| 179 |
+
"f7ad3a2b9911e7f6ea52-v3",
|
| 180 |
+
"e44219edeb2584e028c7-v0",
|
| 181 |
+
"e44219edeb2584e028c7-v1",
|
| 182 |
+
"e44219edeb2584e028c7-v2",
|
| 183 |
+
"e44219edeb2584e028c7-v3",
|
| 184 |
+
"a47cb57e5c8f24e11587-v0",
|
| 185 |
+
"a47cb57e5c8f24e11587-v1",
|
| 186 |
+
"a47cb57e5c8f24e11587-v2",
|
| 187 |
+
"a47cb57e5c8f24e11587-v3",
|
| 188 |
+
"0975f7262f09261c93cf-v0",
|
| 189 |
+
"0975f7262f09261c93cf-v1",
|
| 190 |
+
"0975f7262f09261c93cf-v2",
|
| 191 |
+
"0975f7262f09261c93cf-v3",
|
| 192 |
+
"b48ccce5d32e4c140de6-v0",
|
| 193 |
+
"b48ccce5d32e4c140de6-v1",
|
| 194 |
+
"b48ccce5d32e4c140de6-v2",
|
| 195 |
+
"b48ccce5d32e4c140de6-v3",
|
| 196 |
+
"d5b8361860cb4c246db4-v0",
|
| 197 |
+
"d5b8361860cb4c246db4-v1",
|
| 198 |
+
"d5b8361860cb4c246db4-v2",
|
| 199 |
+
"d5b8361860cb4c246db4-v3",
|
| 200 |
+
"0ab373eff89b242a8264-v0",
|
| 201 |
+
"0ab373eff89b242a8264-v1",
|
| 202 |
+
"0ab373eff89b242a8264-v2",
|
| 203 |
+
"0ab373eff89b242a8264-v3",
|
| 204 |
+
"5008f631374851f24ee1-v0",
|
| 205 |
+
"5008f631374851f24ee1-v1",
|
| 206 |
+
"5008f631374851f24ee1-v2",
|
| 207 |
+
"5008f631374851f24ee1-v3",
|
| 208 |
+
"20cc4d37f55fbf3ed7ee-v0",
|
| 209 |
+
"20cc4d37f55fbf3ed7ee-v1",
|
| 210 |
+
"20cc4d37f55fbf3ed7ee-v2",
|
| 211 |
+
"20cc4d37f55fbf3ed7ee-v3",
|
| 212 |
+
"01cdf02ead8b5e865381-v0",
|
| 213 |
+
"01cdf02ead8b5e865381-v1",
|
| 214 |
+
"01cdf02ead8b5e865381-v2",
|
| 215 |
+
"01cdf02ead8b5e865381-v3",
|
| 216 |
+
"2375d060b093b622ced0-v0",
|
| 217 |
+
"2375d060b093b622ced0-v1",
|
| 218 |
+
"2375d060b093b622ced0-v2",
|
| 219 |
+
"2375d060b093b622ced0-v3",
|
| 220 |
+
"b01c4cf0c7f7066d1630-v0",
|
| 221 |
+
"b01c4cf0c7f7066d1630-v1",
|
| 222 |
+
"b01c4cf0c7f7066d1630-v2",
|
| 223 |
+
"b01c4cf0c7f7066d1630-v3",
|
| 224 |
+
"6ff55fc61e7e5ee7e5cf-v0",
|
| 225 |
+
"6ff55fc61e7e5ee7e5cf-v1",
|
| 226 |
+
"6ff55fc61e7e5ee7e5cf-v2",
|
| 227 |
+
"6ff55fc61e7e5ee7e5cf-v3",
|
| 228 |
+
"01f73f72cedfa86e7b47-v0",
|
| 229 |
+
"01f73f72cedfa86e7b47-v1",
|
| 230 |
+
"01f73f72cedfa86e7b47-v2",
|
| 231 |
+
"01f73f72cedfa86e7b47-v3",
|
| 232 |
+
"b3d18f648ac9bb9154ec-v0",
|
| 233 |
+
"b3d18f648ac9bb9154ec-v1",
|
| 234 |
+
"b3d18f648ac9bb9154ec-v2",
|
| 235 |
+
"b3d18f648ac9bb9154ec-v3",
|
| 236 |
+
"a0ffe8c7cab33d01d46d-v0",
|
| 237 |
+
"a0ffe8c7cab33d01d46d-v1",
|
| 238 |
+
"a0ffe8c7cab33d01d46d-v2",
|
| 239 |
+
"a0ffe8c7cab33d01d46d-v3",
|
| 240 |
+
"abd072738608ed0853e0-v0",
|
| 241 |
+
"abd072738608ed0853e0-v1",
|
| 242 |
+
"abd072738608ed0853e0-v2",
|
| 243 |
+
"abd072738608ed0853e0-v3",
|
| 244 |
+
"854fdbac422e240a3bc0-v0",
|
| 245 |
+
"854fdbac422e240a3bc0-v1",
|
| 246 |
+
"854fdbac422e240a3bc0-v2",
|
| 247 |
+
"854fdbac422e240a3bc0-v3",
|
| 248 |
+
"c2827f8915f273e8b2bf-v0",
|
| 249 |
+
"c2827f8915f273e8b2bf-v1",
|
| 250 |
+
"c2827f8915f273e8b2bf-v2",
|
| 251 |
+
"c2827f8915f273e8b2bf-v3",
|
| 252 |
+
"1e4bee54e046daa3432e-v0",
|
| 253 |
+
"1e4bee54e046daa3432e-v1",
|
| 254 |
+
"1e4bee54e046daa3432e-v2",
|
| 255 |
+
"1e4bee54e046daa3432e-v3",
|
| 256 |
+
"df771458eda4d068489b-v0",
|
| 257 |
+
"df771458eda4d068489b-v1",
|
| 258 |
+
"df771458eda4d068489b-v2",
|
| 259 |
+
"df771458eda4d068489b-v3",
|
| 260 |
+
"9fd36b6629dbe456d618-v0",
|
| 261 |
+
"9fd36b6629dbe456d618-v1",
|
| 262 |
+
"9fd36b6629dbe456d618-v2",
|
| 263 |
+
"9fd36b6629dbe456d618-v3",
|
| 264 |
+
"54f1a71cfdb946aa1e22-v0",
|
| 265 |
+
"54f1a71cfdb946aa1e22-v1",
|
| 266 |
+
"54f1a71cfdb946aa1e22-v2",
|
| 267 |
+
"54f1a71cfdb946aa1e22-v3",
|
| 268 |
+
"8a49ade0408a3d33df2e-v0",
|
| 269 |
+
"8a49ade0408a3d33df2e-v1",
|
| 270 |
+
"8a49ade0408a3d33df2e-v2",
|
| 271 |
+
"8a49ade0408a3d33df2e-v3",
|
| 272 |
+
"aedd94f4737e2811b74f-v0",
|
| 273 |
+
"aedd94f4737e2811b74f-v1",
|
| 274 |
+
"aedd94f4737e2811b74f-v2",
|
| 275 |
+
"aedd94f4737e2811b74f-v3",
|
| 276 |
+
"7a6e5de02d94d4572241-v0",
|
| 277 |
+
"7a6e5de02d94d4572241-v1",
|
| 278 |
+
"7a6e5de02d94d4572241-v2",
|
| 279 |
+
"7a6e5de02d94d4572241-v3",
|
| 280 |
+
"fa70e030c60cf1584945-v0",
|
| 281 |
+
"fa70e030c60cf1584945-v1",
|
| 282 |
+
"fa70e030c60cf1584945-v2",
|
| 283 |
+
"fa70e030c60cf1584945-v3",
|
| 284 |
+
"9b364740f8d8b0b03982-v0",
|
| 285 |
+
"9b364740f8d8b0b03982-v1",
|
| 286 |
+
"9b364740f8d8b0b03982-v2",
|
| 287 |
+
"9b364740f8d8b0b03982-v3",
|
| 288 |
+
"d7c93bae8d9c53fd14bf-v0",
|
| 289 |
+
"d7c93bae8d9c53fd14bf-v1",
|
| 290 |
+
"d7c93bae8d9c53fd14bf-v2",
|
| 291 |
+
"d7c93bae8d9c53fd14bf-v3",
|
| 292 |
+
"236674029e7f3e236f31-v0",
|
| 293 |
+
"236674029e7f3e236f31-v1",
|
| 294 |
+
"236674029e7f3e236f31-v2",
|
| 295 |
+
"236674029e7f3e236f31-v3",
|
| 296 |
+
"5d5948fab5ea82417500-v0",
|
| 297 |
+
"5d5948fab5ea82417500-v1",
|
| 298 |
+
"5d5948fab5ea82417500-v2",
|
| 299 |
+
"5d5948fab5ea82417500-v3",
|
| 300 |
+
"20889edc9ef2a3944b36-v0",
|
| 301 |
+
"20889edc9ef2a3944b36-v1",
|
| 302 |
+
"20889edc9ef2a3944b36-v2",
|
| 303 |
+
"20889edc9ef2a3944b36-v3",
|
| 304 |
+
"e5754be7adb3f9a3c2d9-v0",
|
| 305 |
+
"e5754be7adb3f9a3c2d9-v1",
|
| 306 |
+
"e5754be7adb3f9a3c2d9-v2",
|
| 307 |
+
"e5754be7adb3f9a3c2d9-v3",
|
| 308 |
+
"95e77c467bddee4df460-v0",
|
| 309 |
+
"95e77c467bddee4df460-v1",
|
| 310 |
+
"95e77c467bddee4df460-v2",
|
| 311 |
+
"95e77c467bddee4df460-v3",
|
| 312 |
+
"63cd92c97338def9a9d2-v0",
|
| 313 |
+
"63cd92c97338def9a9d2-v1",
|
| 314 |
+
"63cd92c97338def9a9d2-v2",
|
| 315 |
+
"63cd92c97338def9a9d2-v3",
|
| 316 |
+
"aa8df97e47fd90c64457-v0",
|
| 317 |
+
"aa8df97e47fd90c64457-v1",
|
| 318 |
+
"aa8df97e47fd90c64457-v2",
|
| 319 |
+
"aa8df97e47fd90c64457-v3",
|
| 320 |
+
"aa5d1de39fbae4d7ad9e-v0",
|
| 321 |
+
"aa5d1de39fbae4d7ad9e-v1",
|
| 322 |
+
"aa5d1de39fbae4d7ad9e-v2",
|
| 323 |
+
"aa5d1de39fbae4d7ad9e-v3",
|
| 324 |
+
"cf5ce7483ba098f616f3-v0",
|
| 325 |
+
"cf5ce7483ba098f616f3-v1",
|
| 326 |
+
"cf5ce7483ba098f616f3-v2",
|
| 327 |
+
"cf5ce7483ba098f616f3-v3",
|
| 328 |
+
"3e7a80a6c89def99af8d-v0",
|
| 329 |
+
"3e7a80a6c89def99af8d-v1",
|
| 330 |
+
"3e7a80a6c89def99af8d-v2",
|
| 331 |
+
"3e7a80a6c89def99af8d-v3",
|
| 332 |
+
"bea94a0a0d57de031265-v0",
|
| 333 |
+
"bea94a0a0d57de031265-v1",
|
| 334 |
+
"bea94a0a0d57de031265-v2",
|
| 335 |
+
"bea94a0a0d57de031265-v3",
|
| 336 |
+
"e6f919123c1a9798050c-v0",
|
| 337 |
+
"e6f919123c1a9798050c-v1",
|
| 338 |
+
"e6f919123c1a9798050c-v2",
|
| 339 |
+
"e6f919123c1a9798050c-v3",
|
| 340 |
+
"9431e4b8c8fecb8af197-v0",
|
| 341 |
+
"9431e4b8c8fecb8af197-v1",
|
| 342 |
+
"9431e4b8c8fecb8af197-v2",
|
| 343 |
+
"9431e4b8c8fecb8af197-v3",
|
| 344 |
+
"8a5e2d5ee8a33fba9703-v0",
|
| 345 |
+
"8a5e2d5ee8a33fba9703-v1",
|
| 346 |
+
"8a5e2d5ee8a33fba9703-v2",
|
| 347 |
+
"8a5e2d5ee8a33fba9703-v3",
|
| 348 |
+
"6d71535d904c466c1bd4-v0",
|
| 349 |
+
"6d71535d904c466c1bd4-v1",
|
| 350 |
+
"6d71535d904c466c1bd4-v2",
|
| 351 |
+
"6d71535d904c466c1bd4-v3",
|
| 352 |
+
"08c8562106e171463cfa-v0",
|
| 353 |
+
"08c8562106e171463cfa-v1",
|
| 354 |
+
"08c8562106e171463cfa-v2",
|
| 355 |
+
"08c8562106e171463cfa-v3",
|
| 356 |
+
"5c925cef90b86cca85e7-v0",
|
| 357 |
+
"5c925cef90b86cca85e7-v1",
|
| 358 |
+
"5c925cef90b86cca85e7-v2",
|
| 359 |
+
"5c925cef90b86cca85e7-v3",
|
| 360 |
+
"dde147e8e1b1f215ab74-v0",
|
| 361 |
+
"dde147e8e1b1f215ab74-v1",
|
| 362 |
+
"dde147e8e1b1f215ab74-v2",
|
| 363 |
+
"dde147e8e1b1f215ab74-v3",
|
| 364 |
+
"23209c52d6f1e68bc1c4-v0",
|
| 365 |
+
"23209c52d6f1e68bc1c4-v1",
|
| 366 |
+
"23209c52d6f1e68bc1c4-v2",
|
| 367 |
+
"23209c52d6f1e68bc1c4-v3",
|
| 368 |
+
"72a88cda4d3320bc0b87-v0",
|
| 369 |
+
"72a88cda4d3320bc0b87-v1",
|
| 370 |
+
"72a88cda4d3320bc0b87-v2",
|
| 371 |
+
"72a88cda4d3320bc0b87-v3",
|
| 372 |
+
"91d6685f4c348b8aae5f-v0",
|
| 373 |
+
"91d6685f4c348b8aae5f-v1",
|
| 374 |
+
"91d6685f4c348b8aae5f-v2",
|
| 375 |
+
"91d6685f4c348b8aae5f-v3",
|
| 376 |
+
"f3df6b37e1b67fe2c3b0-v0",
|
| 377 |
+
"f3df6b37e1b67fe2c3b0-v1",
|
| 378 |
+
"f3df6b37e1b67fe2c3b0-v2",
|
| 379 |
+
"f3df6b37e1b67fe2c3b0-v3",
|
| 380 |
+
"bb204ad65ec97e0c1441-v0",
|
| 381 |
+
"bb204ad65ec97e0c1441-v1",
|
| 382 |
+
"bb204ad65ec97e0c1441-v2",
|
| 383 |
+
"bb204ad65ec97e0c1441-v3",
|
| 384 |
+
"42ca4d64703cfce58aab-v0",
|
| 385 |
+
"42ca4d64703cfce58aab-v1",
|
| 386 |
+
"42ca4d64703cfce58aab-v2",
|
| 387 |
+
"42ca4d64703cfce58aab-v3",
|
| 388 |
+
"1dbe996e85d24680ea52-v0",
|
| 389 |
+
"1dbe996e85d24680ea52-v1",
|
| 390 |
+
"1dbe996e85d24680ea52-v2",
|
| 391 |
+
"1dbe996e85d24680ea52-v3",
|
| 392 |
+
"7ae1ecae9954505ec431-v0",
|
| 393 |
+
"7ae1ecae9954505ec431-v1",
|
| 394 |
+
"7ae1ecae9954505ec431-v2",
|
| 395 |
+
"7ae1ecae9954505ec431-v3",
|
| 396 |
+
"e2739774f499722fab3c-v0",
|
| 397 |
+
"e2739774f499722fab3c-v1",
|
| 398 |
+
"e2739774f499722fab3c-v2",
|
| 399 |
+
"e2739774f499722fab3c-v3",
|
| 400 |
+
"455a3889e74a63dc4a7f-v0",
|
| 401 |
+
"455a3889e74a63dc4a7f-v1",
|
| 402 |
+
"455a3889e74a63dc4a7f-v2",
|
| 403 |
+
"455a3889e74a63dc4a7f-v3",
|
| 404 |
+
"266a4542856f979dc406-v0",
|
| 405 |
+
"266a4542856f979dc406-v1",
|
| 406 |
+
"266a4542856f979dc406-v2",
|
| 407 |
+
"266a4542856f979dc406-v3",
|
| 408 |
+
"bc5c99462af23c1ded8b-v0",
|
| 409 |
+
"bc5c99462af23c1ded8b-v1",
|
| 410 |
+
"bc5c99462af23c1ded8b-v2",
|
| 411 |
+
"bc5c99462af23c1ded8b-v3",
|
| 412 |
+
"e4747f32b34ac89e93bf-v0",
|
| 413 |
+
"e4747f32b34ac89e93bf-v1",
|
| 414 |
+
"e4747f32b34ac89e93bf-v2",
|
| 415 |
+
"e4747f32b34ac89e93bf-v3",
|
| 416 |
+
"d60a8e624048386e3094-v0",
|
| 417 |
+
"d60a8e624048386e3094-v1",
|
| 418 |
+
"d60a8e624048386e3094-v2",
|
| 419 |
+
"d60a8e624048386e3094-v3",
|
| 420 |
+
"3843fa0250d312b4f97c-v0",
|
| 421 |
+
"3843fa0250d312b4f97c-v1",
|
| 422 |
+
"3843fa0250d312b4f97c-v2",
|
| 423 |
+
"3843fa0250d312b4f97c-v3",
|
| 424 |
+
"422ece43bb9141c25714-v0",
|
| 425 |
+
"422ece43bb9141c25714-v1",
|
| 426 |
+
"422ece43bb9141c25714-v2",
|
| 427 |
+
"422ece43bb9141c25714-v3",
|
| 428 |
+
"d23622dd4eba6939a1f6-v0",
|
| 429 |
+
"d23622dd4eba6939a1f6-v1",
|
| 430 |
+
"d23622dd4eba6939a1f6-v2",
|
| 431 |
+
"d23622dd4eba6939a1f6-v3",
|
| 432 |
+
"b569a59ac13185b2c8ad-v0",
|
| 433 |
+
"b569a59ac13185b2c8ad-v1",
|
| 434 |
+
"b569a59ac13185b2c8ad-v2",
|
| 435 |
+
"b569a59ac13185b2c8ad-v3",
|
| 436 |
+
"2d820b7c3ee95f80d3f4-v0",
|
| 437 |
+
"2d820b7c3ee95f80d3f4-v1",
|
| 438 |
+
"2d820b7c3ee95f80d3f4-v2",
|
| 439 |
+
"2d820b7c3ee95f80d3f4-v3",
|
| 440 |
+
"5126b25a9bd271eb70c8-v0",
|
| 441 |
+
"5126b25a9bd271eb70c8-v1",
|
| 442 |
+
"5126b25a9bd271eb70c8-v2",
|
| 443 |
+
"5126b25a9bd271eb70c8-v3",
|
| 444 |
+
"4423c37b9342c83342e6-v0",
|
| 445 |
+
"4423c37b9342c83342e6-v1",
|
| 446 |
+
"4423c37b9342c83342e6-v2",
|
| 447 |
+
"4423c37b9342c83342e6-v3",
|
| 448 |
+
"a63c2eada3f5f7aa022e-v0",
|
| 449 |
+
"a63c2eada3f5f7aa022e-v1",
|
| 450 |
+
"a63c2eada3f5f7aa022e-v2",
|
| 451 |
+
"a63c2eada3f5f7aa022e-v3",
|
| 452 |
+
"3f562c2ba5dce1c08966-v0",
|
| 453 |
+
"3f562c2ba5dce1c08966-v1",
|
| 454 |
+
"3f562c2ba5dce1c08966-v2",
|
| 455 |
+
"3f562c2ba5dce1c08966-v3",
|
| 456 |
+
"0822237a043a73c1a4bc-v0",
|
| 457 |
+
"0822237a043a73c1a4bc-v1",
|
| 458 |
+
"0822237a043a73c1a4bc-v2",
|
| 459 |
+
"0822237a043a73c1a4bc-v3",
|
| 460 |
+
"3c22586a840d670b5e6c-v0",
|
| 461 |
+
"3c22586a840d670b5e6c-v1",
|
| 462 |
+
"3c22586a840d670b5e6c-v2",
|
| 463 |
+
"3c22586a840d670b5e6c-v3",
|
| 464 |
+
"c92a387d2882db431f65-v0",
|
| 465 |
+
"c92a387d2882db431f65-v1",
|
| 466 |
+
"c92a387d2882db431f65-v2",
|
| 467 |
+
"c92a387d2882db431f65-v3",
|
| 468 |
+
"d0afc9296f83fad5b52d-v0",
|
| 469 |
+
"d0afc9296f83fad5b52d-v1",
|
| 470 |
+
"d0afc9296f83fad5b52d-v2",
|
| 471 |
+
"d0afc9296f83fad5b52d-v3",
|
| 472 |
+
"d7ca171473b12143fdb4-v0",
|
| 473 |
+
"d7ca171473b12143fdb4-v1",
|
| 474 |
+
"d7ca171473b12143fdb4-v2",
|
| 475 |
+
"d7ca171473b12143fdb4-v3",
|
| 476 |
+
"92aa644c30a82c8729f0-v0",
|
| 477 |
+
"92aa644c30a82c8729f0-v1",
|
| 478 |
+
"92aa644c30a82c8729f0-v2",
|
| 479 |
+
"92aa644c30a82c8729f0-v3",
|
| 480 |
+
"917941dc4b82780cad3c-v0",
|
| 481 |
+
"917941dc4b82780cad3c-v1",
|
| 482 |
+
"917941dc4b82780cad3c-v2",
|
| 483 |
+
"917941dc4b82780cad3c-v3",
|
| 484 |
+
"76c5d77420d0adddfa9a-v0",
|
| 485 |
+
"76c5d77420d0adddfa9a-v1",
|
| 486 |
+
"76c5d77420d0adddfa9a-v2",
|
| 487 |
+
"76c5d77420d0adddfa9a-v3",
|
| 488 |
+
"26888c09438ca17387f8-v0",
|
| 489 |
+
"26888c09438ca17387f8-v1",
|
| 490 |
+
"26888c09438ca17387f8-v2",
|
| 491 |
+
"26888c09438ca17387f8-v3",
|
| 492 |
+
"db50fbd5e0fdb5ce3379-v0",
|
| 493 |
+
"db50fbd5e0fdb5ce3379-v1",
|
| 494 |
+
"db50fbd5e0fdb5ce3379-v2",
|
| 495 |
+
"db50fbd5e0fdb5ce3379-v3",
|
| 496 |
+
"6a5aef795e625c1fb2a9-v0",
|
| 497 |
+
"6a5aef795e625c1fb2a9-v1",
|
| 498 |
+
"6a5aef795e625c1fb2a9-v2",
|
| 499 |
+
"6a5aef795e625c1fb2a9-v3",
|
| 500 |
+
"7ff8979556221a260ce0-v0",
|
| 501 |
+
"7ff8979556221a260ce0-v1",
|
| 502 |
+
"7ff8979556221a260ce0-v2",
|
| 503 |
+
"7ff8979556221a260ce0-v3",
|
| 504 |
+
"b4b0d309012ca95a2c4a-v0",
|
| 505 |
+
"b4b0d309012ca95a2c4a-v1",
|
| 506 |
+
"b4b0d309012ca95a2c4a-v2",
|
| 507 |
+
"b4b0d309012ca95a2c4a-v3",
|
| 508 |
+
"8d1c41e250d29d3bd13c-v0",
|
| 509 |
+
"8d1c41e250d29d3bd13c-v1",
|
| 510 |
+
"8d1c41e250d29d3bd13c-v2",
|
| 511 |
+
"8d1c41e250d29d3bd13c-v3",
|
| 512 |
+
"fd8ee32090590934a302-v0",
|
| 513 |
+
"fd8ee32090590934a302-v1",
|
| 514 |
+
"fd8ee32090590934a302-v2",
|
| 515 |
+
"fd8ee32090590934a302-v3",
|
| 516 |
+
"e94bfdac64dd4fd538e8-v0",
|
| 517 |
+
"e94bfdac64dd4fd538e8-v1",
|
| 518 |
+
"e94bfdac64dd4fd538e8-v2",
|
| 519 |
+
"e94bfdac64dd4fd538e8-v3",
|
| 520 |
+
"0c08cc218664cf71d7a4-v0",
|
| 521 |
+
"0c08cc218664cf71d7a4-v1",
|
| 522 |
+
"0c08cc218664cf71d7a4-v2",
|
| 523 |
+
"0c08cc218664cf71d7a4-v3",
|
| 524 |
+
"d0327acf7b451f652464-v0",
|
| 525 |
+
"d0327acf7b451f652464-v1",
|
| 526 |
+
"d0327acf7b451f652464-v2",
|
| 527 |
+
"d0327acf7b451f652464-v3",
|
| 528 |
+
"67ce375b4b7a7236f8c4-v0",
|
| 529 |
+
"67ce375b4b7a7236f8c4-v1",
|
| 530 |
+
"67ce375b4b7a7236f8c4-v2",
|
| 531 |
+
"67ce375b4b7a7236f8c4-v3",
|
| 532 |
+
"cfce2cfcf8317763dd44-v0",
|
| 533 |
+
"cfce2cfcf8317763dd44-v1",
|
| 534 |
+
"cfce2cfcf8317763dd44-v2",
|
| 535 |
+
"cfce2cfcf8317763dd44-v3",
|
| 536 |
+
"7918b36c98ee56019646-v0",
|
| 537 |
+
"7918b36c98ee56019646-v1",
|
| 538 |
+
"7918b36c98ee56019646-v2",
|
| 539 |
+
"7918b36c98ee56019646-v3",
|
| 540 |
+
"5763f942e73f616782ad-v0",
|
| 541 |
+
"5763f942e73f616782ad-v1",
|
| 542 |
+
"5763f942e73f616782ad-v2",
|
| 543 |
+
"5763f942e73f616782ad-v3",
|
| 544 |
+
"4ce0b2318a0b65abd68b-v0",
|
| 545 |
+
"4ce0b2318a0b65abd68b-v1",
|
| 546 |
+
"4ce0b2318a0b65abd68b-v2",
|
| 547 |
+
"4ce0b2318a0b65abd68b-v3",
|
| 548 |
+
"9f0d8b1ae1f1330141d3-v0",
|
| 549 |
+
"9f0d8b1ae1f1330141d3-v1",
|
| 550 |
+
"9f0d8b1ae1f1330141d3-v2",
|
| 551 |
+
"9f0d8b1ae1f1330141d3-v3",
|
| 552 |
+
"bb8116ecf2320e27daa5-v0",
|
| 553 |
+
"bb8116ecf2320e27daa5-v1",
|
| 554 |
+
"bb8116ecf2320e27daa5-v2",
|
| 555 |
+
"bb8116ecf2320e27daa5-v3",
|
| 556 |
+
"03c02d7470d5a1fab0e5-v0",
|
| 557 |
+
"03c02d7470d5a1fab0e5-v1",
|
| 558 |
+
"03c02d7470d5a1fab0e5-v2",
|
| 559 |
+
"03c02d7470d5a1fab0e5-v3",
|
| 560 |
+
"8a77346173b288a90775-v0",
|
| 561 |
+
"8a77346173b288a90775-v1",
|
| 562 |
+
"8a77346173b288a90775-v2",
|
| 563 |
+
"8a77346173b288a90775-v3",
|
| 564 |
+
"d3c48fb9f017b94b8508-v0",
|
| 565 |
+
"d3c48fb9f017b94b8508-v1",
|
| 566 |
+
"d3c48fb9f017b94b8508-v2",
|
| 567 |
+
"d3c48fb9f017b94b8508-v3",
|
| 568 |
+
"32cd7da01c4e542ad376-v0",
|
| 569 |
+
"32cd7da01c4e542ad376-v1",
|
| 570 |
+
"32cd7da01c4e542ad376-v2",
|
| 571 |
+
"32cd7da01c4e542ad376-v3",
|
| 572 |
+
"5116a7c03161b9a8e33e-v0",
|
| 573 |
+
"5116a7c03161b9a8e33e-v1",
|
| 574 |
+
"5116a7c03161b9a8e33e-v2",
|
| 575 |
+
"5116a7c03161b9a8e33e-v3",
|
| 576 |
+
"3707f2f19bd7ee197669-v0",
|
| 577 |
+
"3707f2f19bd7ee197669-v1",
|
| 578 |
+
"3707f2f19bd7ee197669-v2",
|
| 579 |
+
"3707f2f19bd7ee197669-v3",
|
| 580 |
+
"e6c8b873036ef3cff206-v0",
|
| 581 |
+
"e6c8b873036ef3cff206-v1",
|
| 582 |
+
"e6c8b873036ef3cff206-v2",
|
| 583 |
+
"e6c8b873036ef3cff206-v3",
|
| 584 |
+
"55c986292a19ad365d72-v0",
|
| 585 |
+
"55c986292a19ad365d72-v1",
|
| 586 |
+
"55c986292a19ad365d72-v2",
|
| 587 |
+
"55c986292a19ad365d72-v3",
|
| 588 |
+
"032a127f294a9ccc4046-v0",
|
| 589 |
+
"032a127f294a9ccc4046-v1",
|
| 590 |
+
"032a127f294a9ccc4046-v2",
|
| 591 |
+
"032a127f294a9ccc4046-v3",
|
| 592 |
+
"2d03806613e95eda2c5d-v0",
|
| 593 |
+
"2d03806613e95eda2c5d-v1",
|
| 594 |
+
"2d03806613e95eda2c5d-v2",
|
| 595 |
+
"2d03806613e95eda2c5d-v3",
|
| 596 |
+
"1e0b5a590c8169b226d1-v0",
|
| 597 |
+
"1e0b5a590c8169b226d1-v1",
|
| 598 |
+
"1e0b5a590c8169b226d1-v2",
|
| 599 |
+
"1e0b5a590c8169b226d1-v3",
|
| 600 |
+
"899bb8c3073520a2d786-v0",
|
| 601 |
+
"899bb8c3073520a2d786-v1",
|
| 602 |
+
"899bb8c3073520a2d786-v2",
|
| 603 |
+
"899bb8c3073520a2d786-v3",
|
| 604 |
+
"45a047f3468ca0abbfd6-v0",
|
| 605 |
+
"45a047f3468ca0abbfd6-v1",
|
| 606 |
+
"45a047f3468ca0abbfd6-v2",
|
| 607 |
+
"45a047f3468ca0abbfd6-v3",
|
| 608 |
+
"cce70d8f1cf989863f09-v0",
|
| 609 |
+
"cce70d8f1cf989863f09-v1",
|
| 610 |
+
"cce70d8f1cf989863f09-v2",
|
| 611 |
+
"cce70d8f1cf989863f09-v3",
|
| 612 |
+
"02214738c17052c83f0d-v0",
|
| 613 |
+
"02214738c17052c83f0d-v1",
|
| 614 |
+
"02214738c17052c83f0d-v2",
|
| 615 |
+
"02214738c17052c83f0d-v3",
|
| 616 |
+
"093fad17e65696cf48e2-v0",
|
| 617 |
+
"093fad17e65696cf48e2-v1",
|
| 618 |
+
"093fad17e65696cf48e2-v2",
|
| 619 |
+
"093fad17e65696cf48e2-v3",
|
| 620 |
+
"60942606bb00f6896d04-v0",
|
| 621 |
+
"60942606bb00f6896d04-v1",
|
| 622 |
+
"60942606bb00f6896d04-v2",
|
| 623 |
+
"60942606bb00f6896d04-v3",
|
| 624 |
+
"53a4c2377d09fa43c136-v0",
|
| 625 |
+
"53a4c2377d09fa43c136-v1",
|
| 626 |
+
"53a4c2377d09fa43c136-v2",
|
| 627 |
+
"53a4c2377d09fa43c136-v3",
|
| 628 |
+
"38822653da041f7b0bcc-v0",
|
| 629 |
+
"38822653da041f7b0bcc-v1",
|
| 630 |
+
"38822653da041f7b0bcc-v2",
|
| 631 |
+
"38822653da041f7b0bcc-v3",
|
| 632 |
+
"83331db654f927bba7db-v0",
|
| 633 |
+
"83331db654f927bba7db-v1",
|
| 634 |
+
"83331db654f927bba7db-v2",
|
| 635 |
+
"83331db654f927bba7db-v3",
|
| 636 |
+
"0d6b8870f9f8fbecd06a-v0",
|
| 637 |
+
"0d6b8870f9f8fbecd06a-v1",
|
| 638 |
+
"0d6b8870f9f8fbecd06a-v2",
|
| 639 |
+
"0d6b8870f9f8fbecd06a-v3",
|
| 640 |
+
"efd33140ce57a557e922-v0",
|
| 641 |
+
"efd33140ce57a557e922-v1",
|
| 642 |
+
"efd33140ce57a557e922-v2",
|
| 643 |
+
"efd33140ce57a557e922-v3",
|
| 644 |
+
"e6c9829ce1dcf43fd8e6-v0",
|
| 645 |
+
"e6c9829ce1dcf43fd8e6-v1",
|
| 646 |
+
"e6c9829ce1dcf43fd8e6-v2",
|
| 647 |
+
"e6c9829ce1dcf43fd8e6-v3",
|
| 648 |
+
"d6180603ae0451879c14-v0",
|
| 649 |
+
"d6180603ae0451879c14-v1",
|
| 650 |
+
"d6180603ae0451879c14-v2",
|
| 651 |
+
"d6180603ae0451879c14-v3",
|
| 652 |
+
"08742aac51091be0a9c6-v0",
|
| 653 |
+
"08742aac51091be0a9c6-v1",
|
| 654 |
+
"08742aac51091be0a9c6-v2",
|
| 655 |
+
"08742aac51091be0a9c6-v3",
|
| 656 |
+
"170540b624f3109b3fd0-v0",
|
| 657 |
+
"170540b624f3109b3fd0-v1",
|
| 658 |
+
"170540b624f3109b3fd0-v2",
|
| 659 |
+
"170540b624f3109b3fd0-v3",
|
| 660 |
+
"ef66425f8d0214644d8d-v0",
|
| 661 |
+
"ef66425f8d0214644d8d-v1",
|
| 662 |
+
"ef66425f8d0214644d8d-v2",
|
| 663 |
+
"ef66425f8d0214644d8d-v3",
|
| 664 |
+
"a764131510993a8e2ca2-v0",
|
| 665 |
+
"a764131510993a8e2ca2-v1",
|
| 666 |
+
"a764131510993a8e2ca2-v2",
|
| 667 |
+
"a764131510993a8e2ca2-v3",
|
| 668 |
+
"1555d7f97f52214e121d-v0",
|
| 669 |
+
"1555d7f97f52214e121d-v1",
|
| 670 |
+
"1555d7f97f52214e121d-v2",
|
| 671 |
+
"1555d7f97f52214e121d-v3",
|
| 672 |
+
"57833fdb2b7db066c37f-v0",
|
| 673 |
+
"57833fdb2b7db066c37f-v1",
|
| 674 |
+
"57833fdb2b7db066c37f-v2",
|
| 675 |
+
"57833fdb2b7db066c37f-v3",
|
| 676 |
+
"f8834e3399d85f499155-v0",
|
| 677 |
+
"f8834e3399d85f499155-v1",
|
| 678 |
+
"f8834e3399d85f499155-v2",
|
| 679 |
+
"f8834e3399d85f499155-v3",
|
| 680 |
+
"0ab36e04e97c91000199-v0",
|
| 681 |
+
"0ab36e04e97c91000199-v1",
|
| 682 |
+
"0ab36e04e97c91000199-v2",
|
| 683 |
+
"0ab36e04e97c91000199-v3",
|
| 684 |
+
"18424f2430fb7a69602d-v0",
|
| 685 |
+
"18424f2430fb7a69602d-v1",
|
| 686 |
+
"18424f2430fb7a69602d-v2",
|
| 687 |
+
"18424f2430fb7a69602d-v3",
|
| 688 |
+
"178675742d6486f51bc4-v0",
|
| 689 |
+
"178675742d6486f51bc4-v1",
|
| 690 |
+
"178675742d6486f51bc4-v2",
|
| 691 |
+
"178675742d6486f51bc4-v3",
|
| 692 |
+
"01c0b332d3789803ba65-v0",
|
| 693 |
+
"01c0b332d3789803ba65-v1",
|
| 694 |
+
"01c0b332d3789803ba65-v2",
|
| 695 |
+
"01c0b332d3789803ba65-v3"
|
| 696 |
+
],
|
| 697 |
+
"val_ids": [
|
| 698 |
+
"490d93c0b080175b8a66-v0",
|
| 699 |
+
"490d93c0b080175b8a66-v1",
|
| 700 |
+
"490d93c0b080175b8a66-v2",
|
| 701 |
+
"490d93c0b080175b8a66-v3",
|
| 702 |
+
"eb03c5c9f2711fe9b9ec-v0",
|
| 703 |
+
"eb03c5c9f2711fe9b9ec-v1",
|
| 704 |
+
"eb03c5c9f2711fe9b9ec-v2",
|
| 705 |
+
"eb03c5c9f2711fe9b9ec-v3",
|
| 706 |
+
"c42cdd597515d930619f-v0",
|
| 707 |
+
"c42cdd597515d930619f-v1",
|
| 708 |
+
"c42cdd597515d930619f-v2",
|
| 709 |
+
"c42cdd597515d930619f-v3",
|
| 710 |
+
"9834d7b46708c5f3141a-v0",
|
| 711 |
+
"9834d7b46708c5f3141a-v1",
|
| 712 |
+
"9834d7b46708c5f3141a-v2",
|
| 713 |
+
"9834d7b46708c5f3141a-v3",
|
| 714 |
+
"4a1770b349d1696431dd-v0",
|
| 715 |
+
"4a1770b349d1696431dd-v1",
|
| 716 |
+
"4a1770b349d1696431dd-v2",
|
| 717 |
+
"4a1770b349d1696431dd-v3",
|
| 718 |
+
"a79a0ab110691491fed2-v0",
|
| 719 |
+
"a79a0ab110691491fed2-v1",
|
| 720 |
+
"a79a0ab110691491fed2-v2",
|
| 721 |
+
"a79a0ab110691491fed2-v3",
|
| 722 |
+
"e29f72f0f7fa3b880741-v0",
|
| 723 |
+
"e29f72f0f7fa3b880741-v1",
|
| 724 |
+
"e29f72f0f7fa3b880741-v2",
|
| 725 |
+
"e29f72f0f7fa3b880741-v3",
|
| 726 |
+
"a19bf682317a9d72d332-v0",
|
| 727 |
+
"a19bf682317a9d72d332-v1",
|
| 728 |
+
"a19bf682317a9d72d332-v2",
|
| 729 |
+
"a19bf682317a9d72d332-v3",
|
| 730 |
+
"8cde9d38a1e42442f821-v0",
|
| 731 |
+
"8cde9d38a1e42442f821-v1",
|
| 732 |
+
"8cde9d38a1e42442f821-v2",
|
| 733 |
+
"8cde9d38a1e42442f821-v3",
|
| 734 |
+
"fe9c7b60406ed4fd1983-v0",
|
| 735 |
+
"fe9c7b60406ed4fd1983-v1",
|
| 736 |
+
"fe9c7b60406ed4fd1983-v2",
|
| 737 |
+
"fe9c7b60406ed4fd1983-v3",
|
| 738 |
+
"e4dbd8299958bb3b0b9e-v0",
|
| 739 |
+
"e4dbd8299958bb3b0b9e-v1",
|
| 740 |
+
"e4dbd8299958bb3b0b9e-v2",
|
| 741 |
+
"e4dbd8299958bb3b0b9e-v3",
|
| 742 |
+
"48bbd01188ea983a5b2f-v0",
|
| 743 |
+
"48bbd01188ea983a5b2f-v1",
|
| 744 |
+
"48bbd01188ea983a5b2f-v2",
|
| 745 |
+
"48bbd01188ea983a5b2f-v3",
|
| 746 |
+
"3228102524c244aa3d59-v0",
|
| 747 |
+
"3228102524c244aa3d59-v1",
|
| 748 |
+
"3228102524c244aa3d59-v2",
|
| 749 |
+
"3228102524c244aa3d59-v3",
|
| 750 |
+
"5896b3c215ed8167d0cc-v0",
|
| 751 |
+
"5896b3c215ed8167d0cc-v1",
|
| 752 |
+
"5896b3c215ed8167d0cc-v2",
|
| 753 |
+
"5896b3c215ed8167d0cc-v3",
|
| 754 |
+
"8d36fc4f31a77b9eab9f-v0",
|
| 755 |
+
"8d36fc4f31a77b9eab9f-v1",
|
| 756 |
+
"8d36fc4f31a77b9eab9f-v2",
|
| 757 |
+
"8d36fc4f31a77b9eab9f-v3",
|
| 758 |
+
"dcdf4504811d49eed613-v0",
|
| 759 |
+
"dcdf4504811d49eed613-v1",
|
| 760 |
+
"dcdf4504811d49eed613-v2",
|
| 761 |
+
"dcdf4504811d49eed613-v3",
|
| 762 |
+
"f4bebfef2755063c3327-v0",
|
| 763 |
+
"f4bebfef2755063c3327-v1",
|
| 764 |
+
"f4bebfef2755063c3327-v2",
|
| 765 |
+
"f4bebfef2755063c3327-v3",
|
| 766 |
+
"421edd1b4af56929a114-v0",
|
| 767 |
+
"421edd1b4af56929a114-v1",
|
| 768 |
+
"421edd1b4af56929a114-v2",
|
| 769 |
+
"421edd1b4af56929a114-v3",
|
| 770 |
+
"0c351b2db97c91e18d91-v0",
|
| 771 |
+
"0c351b2db97c91e18d91-v1",
|
| 772 |
+
"0c351b2db97c91e18d91-v2",
|
| 773 |
+
"0c351b2db97c91e18d91-v3",
|
| 774 |
+
"178d10126082a9678b0f-v0",
|
| 775 |
+
"178d10126082a9678b0f-v1",
|
| 776 |
+
"178d10126082a9678b0f-v2",
|
| 777 |
+
"178d10126082a9678b0f-v3",
|
| 778 |
+
"28fd97ba3f683c2a99c5-v0",
|
| 779 |
+
"28fd97ba3f683c2a99c5-v1",
|
| 780 |
+
"28fd97ba3f683c2a99c5-v2",
|
| 781 |
+
"28fd97ba3f683c2a99c5-v3",
|
| 782 |
+
"226b1903a8c86d5f49a8-v0",
|
| 783 |
+
"226b1903a8c86d5f49a8-v1",
|
| 784 |
+
"226b1903a8c86d5f49a8-v2",
|
| 785 |
+
"226b1903a8c86d5f49a8-v3",
|
| 786 |
+
"8d343b01a4d96fce0c93-v0",
|
| 787 |
+
"8d343b01a4d96fce0c93-v1",
|
| 788 |
+
"8d343b01a4d96fce0c93-v2",
|
| 789 |
+
"8d343b01a4d96fce0c93-v3",
|
| 790 |
+
"81b217a8060cb30b40ae-v0",
|
| 791 |
+
"81b217a8060cb30b40ae-v1",
|
| 792 |
+
"81b217a8060cb30b40ae-v2",
|
| 793 |
+
"81b217a8060cb30b40ae-v3",
|
| 794 |
+
"2fac0c5475e8e927f3f8-v0",
|
| 795 |
+
"2fac0c5475e8e927f3f8-v1",
|
| 796 |
+
"2fac0c5475e8e927f3f8-v2",
|
| 797 |
+
"2fac0c5475e8e927f3f8-v3",
|
| 798 |
+
"81ad71ac6fd6f8101cec-v0",
|
| 799 |
+
"81ad71ac6fd6f8101cec-v1",
|
| 800 |
+
"81ad71ac6fd6f8101cec-v2",
|
| 801 |
+
"81ad71ac6fd6f8101cec-v3",
|
| 802 |
+
"0ba4d03f17809d59a303-v0",
|
| 803 |
+
"0ba4d03f17809d59a303-v1",
|
| 804 |
+
"0ba4d03f17809d59a303-v2",
|
| 805 |
+
"0ba4d03f17809d59a303-v3",
|
| 806 |
+
"33391b3c51b93088937f-v0",
|
| 807 |
+
"33391b3c51b93088937f-v1",
|
| 808 |
+
"33391b3c51b93088937f-v2",
|
| 809 |
+
"33391b3c51b93088937f-v3",
|
| 810 |
+
"02b0826239e05c2c97d4-v0",
|
| 811 |
+
"02b0826239e05c2c97d4-v1",
|
| 812 |
+
"02b0826239e05c2c97d4-v2",
|
| 813 |
+
"02b0826239e05c2c97d4-v3",
|
| 814 |
+
"59b5ec716c803703ea5d-v0",
|
| 815 |
+
"59b5ec716c803703ea5d-v1",
|
| 816 |
+
"59b5ec716c803703ea5d-v2",
|
| 817 |
+
"59b5ec716c803703ea5d-v3",
|
| 818 |
+
"f6b970010bc3833bd256-v0",
|
| 819 |
+
"f6b970010bc3833bd256-v1",
|
| 820 |
+
"f6b970010bc3833bd256-v2",
|
| 821 |
+
"f6b970010bc3833bd256-v3",
|
| 822 |
+
"3577c750b03d138cb1c3-v0",
|
| 823 |
+
"3577c750b03d138cb1c3-v1",
|
| 824 |
+
"3577c750b03d138cb1c3-v2",
|
| 825 |
+
"3577c750b03d138cb1c3-v3",
|
| 826 |
+
"1ad605ceb804ef551c30-v0",
|
| 827 |
+
"1ad605ceb804ef551c30-v1",
|
| 828 |
+
"1ad605ceb804ef551c30-v2",
|
| 829 |
+
"1ad605ceb804ef551c30-v3",
|
| 830 |
+
"90df3e622026c10b2314-v0",
|
| 831 |
+
"90df3e622026c10b2314-v1",
|
| 832 |
+
"90df3e622026c10b2314-v2",
|
| 833 |
+
"90df3e622026c10b2314-v3",
|
| 834 |
+
"80b7421276b7b7aa42d2-v0",
|
| 835 |
+
"80b7421276b7b7aa42d2-v1",
|
| 836 |
+
"80b7421276b7b7aa42d2-v2",
|
| 837 |
+
"80b7421276b7b7aa42d2-v3",
|
| 838 |
+
"e892cd1da4e97cbb3156-v0",
|
| 839 |
+
"e892cd1da4e97cbb3156-v1",
|
| 840 |
+
"e892cd1da4e97cbb3156-v2",
|
| 841 |
+
"e892cd1da4e97cbb3156-v3",
|
| 842 |
+
"08316a144698ca3c94fd-v0",
|
| 843 |
+
"08316a144698ca3c94fd-v1",
|
| 844 |
+
"08316a144698ca3c94fd-v2",
|
| 845 |
+
"08316a144698ca3c94fd-v3",
|
| 846 |
+
"ca8462cb5c9b32b9a8d5-v0",
|
| 847 |
+
"ca8462cb5c9b32b9a8d5-v1",
|
| 848 |
+
"ca8462cb5c9b32b9a8d5-v2",
|
| 849 |
+
"ca8462cb5c9b32b9a8d5-v3",
|
| 850 |
+
"89e5abe091880bdda1eb-v0",
|
| 851 |
+
"89e5abe091880bdda1eb-v1",
|
| 852 |
+
"89e5abe091880bdda1eb-v2",
|
| 853 |
+
"89e5abe091880bdda1eb-v3",
|
| 854 |
+
"de83ba5f5f6e234b2cf5-v0",
|
| 855 |
+
"de83ba5f5f6e234b2cf5-v1",
|
| 856 |
+
"de83ba5f5f6e234b2cf5-v2",
|
| 857 |
+
"de83ba5f5f6e234b2cf5-v3",
|
| 858 |
+
"8e5b9b5a46d3351bf52c-v0",
|
| 859 |
+
"8e5b9b5a46d3351bf52c-v1",
|
| 860 |
+
"8e5b9b5a46d3351bf52c-v2",
|
| 861 |
+
"8e5b9b5a46d3351bf52c-v3",
|
| 862 |
+
"60222ffa34fbeaf3487f-v0",
|
| 863 |
+
"60222ffa34fbeaf3487f-v1",
|
| 864 |
+
"60222ffa34fbeaf3487f-v2",
|
| 865 |
+
"60222ffa34fbeaf3487f-v3",
|
| 866 |
+
"460e87ec477c804638b3-v0",
|
| 867 |
+
"460e87ec477c804638b3-v1",
|
| 868 |
+
"460e87ec477c804638b3-v2",
|
| 869 |
+
"460e87ec477c804638b3-v3",
|
| 870 |
+
"07275900f72e95c1f69c-v0",
|
| 871 |
+
"07275900f72e95c1f69c-v1",
|
| 872 |
+
"07275900f72e95c1f69c-v2",
|
| 873 |
+
"07275900f72e95c1f69c-v3",
|
| 874 |
+
"bb5377db3c418476576d-v0",
|
| 875 |
+
"bb5377db3c418476576d-v1",
|
| 876 |
+
"bb5377db3c418476576d-v2",
|
| 877 |
+
"bb5377db3c418476576d-v3"
|
| 878 |
+
],
|
| 879 |
+
"parameters": 306321,
|
| 880 |
+
"teachers": [
|
| 881 |
+
"mesh-proxy:allenai/objaverse",
|
| 882 |
+
"mesh-proxy:scenario-labs/3d-collection"
|
| 883 |
+
],
|
| 884 |
+
"validation": "held-out objects",
|
| 885 |
+
"manifest_sha256": "47c6cb23929321cfc5b21a16807f6a460d18d0231f11c6afd7136fb4b6bea945",
|
| 886 |
+
"initialization": {
|
| 887 |
+
"sha256": "83c065d2a50cf086928542cf8d40dc4416b379e38b9aa61d842dc9106d4b3306",
|
| 888 |
+
"config": {
|
| 889 |
+
"grid": 16,
|
| 890 |
+
"width": 64
|
| 891 |
+
},
|
| 892 |
+
"step": 4800,
|
| 893 |
+
"optimizer": "fresh AdamW; weights-only post-training"
|
| 894 |
+
},
|
| 895 |
+
"steps_requested": 8000,
|
| 896 |
+
"learning_rate": 5e-05,
|
| 897 |
+
"batch_size": 2,
|
| 898 |
+
"validation_fraction": 0.2,
|
| 899 |
+
"baseline_validation": {
|
| 900 |
+
"occupancy": 0.45328883288635147,
|
| 901 |
+
"position": 0.0943834915343258,
|
| 902 |
+
"colour": 0.03328401494266776,
|
| 903 |
+
"scale": 0.002456185630742564,
|
| 904 |
+
"rotation": 0.00010184630525853007,
|
| 905 |
+
"opacity": 0.00022721136727644383,
|
| 906 |
+
"geometry": 0.0,
|
| 907 |
+
"loss": 0.6147233684857686,
|
| 908 |
+
"occupancy_iou": 0.2426499036554661
|
| 909 |
+
},
|
| 910 |
+
"base_model": "Lucasgitguy-co/forma3d-tiny",
|
| 911 |
+
"base_revision": "e80b8d895cc2e405894473abe4ce40b3472eaca7",
|
| 912 |
+
"training_method": "weights-only continuation; fresh AdamW",
|
| 913 |
+
"known_v1_assets_excluded": true
|
| 914 |
+
}
|