"""Evaluate versus pinned forma3d-tiny, stage honest v2 metadata, never upload.""" import argparse import hashlib import importlib.util import json import shutil from pathlib import Path import torch from audit_data import audit ROOT = Path(__file__).resolve().parent REV = 'e80b8d895cc2e405894473abe4ce40b3472eaca7' BASE = 'Lucasgitguy-co/forma3d-tiny' def sha(p): return hashlib.sha256(Path(p).read_bytes()).hexdigest() def main(): p = argparse.ArgumentParser() for name in ('run','data','base','release'): p.add_argument('--'+name, required=True) p.add_argument('--device', default='mps', choices=['mps','cpu']) args = p.parse_args() destination = Path(args.release) if destination.exists(): raise ValueError('Release directory exists; choose a fresh --release path') audit(args.data, ROOT / 'upstream/ATTRIBUTIONS.json') # Ensure the asserted parent is the pinned public checkpoint, not another run. if sha(args.base) != '83c065d2a50cf086928542cf8d40dc4416b379e38b9aa61d842dc9106d4b3306': raise ValueError('Expected the pinned published forma3d-tiny model.pt') spec = importlib.util.spec_from_file_location('pinned_release', ROOT / 'upstream/scripts/release.py') module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) checkpoint, meta, report = module.evaluate_release(args) # Upstream gate checks completed training, validation loss, held-out loss/IoU, # source separation, initialization hash and nonempty qualitative predictions. source = ROOT / 'upstream' destination.mkdir(parents=True) for folder in ('src','licenses'): shutil.copytree(source / folder, destination / folder, ignore=shutil.ignore_patterns('__pycache__','*.egg-info')) for name in ('LICENSE','NOTICE','THIRD_PARTY.md','DATASETS.md','requirements-tested-macos.txt'): shutil.copyfile(source / name, destination / name) project = (source / 'pyproject.toml').read_text() project = project.replace('name = "forma3d-tiny"','name = "forma3d-tiny-2"') project = project.replace('version = "0.2.0"','version = "0.3.0"') (destination / 'pyproject.toml').write_text(project) (destination / 'scripts').mkdir() for name in ('prepare_hf.py','release.py'): shutil.copyfile(source / 'scripts' / name, destination / 'scripts' / name) # This file set can reproduce the training recipe from pinned upstream. for name in ('posttrain.sh','prepare_v2.py','audit_data.py','stage_v2.py','publish_v2.py','COMMANDS.md'): shutil.copyfile(ROOT / name, destination / name) public_meta = {k:v for k,v in meta.items() if k != 'dataset'} public_meta.update({'base_model':BASE, 'base_revision':REV, 'training_method':'weights-only continuation; fresh AdamW', 'known_v1_assets_excluded':True}) torch.save({'format':1,'config':checkpoint['config'],'model':checkpoint['model'], 'step':checkpoint['step'],'metadata':public_meta}, destination / 'model.pt') (destination / 'config.json').write_text(json.dumps(checkpoint['config'],indent=2)) (destination / 'training.json').write_text(json.dumps(public_meta,indent=2)) report.update({'base_model':BASE,'base_revision':REV, 'published_model_sha256':sha(destination / 'model.pt')}) (destination / 'evaluation.json').write_text(json.dumps(report,indent=2)) for src, dst in [('sources.json','ATTRIBUTIONS.json'),('v2-audit.json','data-audit.json'), ('preparation.json','preparation.json')]: # preparation.json includes user-local paths; publish only portable fields. if src == 'preparation.json': prepared = json.loads((Path(args.data) / src).read_text()) prepared['settings'] = {k:v for k,v in prepared['settings'].items() if k not in ('out','cache')} (destination / dst).write_text(json.dumps(prepared,indent=2)) else: shutil.copyfile(Path(args.data) / src,destination / dst) shutil.copyfile(Path(args.run) / 'metrics.jsonl',destination / 'metrics.jsonl') if (ROOT / 'runs/environment.txt').exists(): lines = (ROOT / 'runs/environment.txt').read_text().splitlines() portable = [line for line in lines if ' @ file:' not in line and not line.startswith('-e ')] portable.append('# Local forma3d-tiny 0.2.0 source: '+BASE+' @ '+REV) (destination / 'environment.txt').write_text('\n'.join(portable)+'\n') datasets = sorted({r['repo'] for r in json.loads((destination / 'ATTRIBUTIONS.json').read_text())}) dataset_yaml = '\n'.join('- '+d for d in datasets) bt, ft = report['baseline_test'],report['posttrained_test'] card = f'''--- license: apache-2.0 pipeline_tag: image-to-3d base_model: {BASE} base_model_relation: finetune model_name: Forma3D Tiny 2 datasets: {dataset_yaml} tags: - pytorch - gaussian-splatting - mesh-proxy-supervision - research-prototype --- # Forma3D Tiny 2 A {meta['parameters']:,}-parameter image-to-Gaussian research model continued from {BASE} at revision `{REV}`. The optimizer starts fresh. It uses a {checkpoint['config']['grid']} cubed grid and width {checkpoint['config']['width']}. Selected checkpoint step {checkpoint['step']} from {meta['steps_requested']} completed steps. ## Training See training.json and metrics.jsonl for actual optimizer settings and validation history. Inputs are orthographic renders of centered mesh-derived Gaussian proxy clouds on black. Targets are isotropic surface samples compressed to one Gaussian per occupied grid cell. No pretrained 3D teacher was run. No natural-photo training or differentiable rendering is claimed. Asset IDs, known source groups, and exact mesh hashes from all 96 known v1 assets were excluded. Near duplicates can remain. The separate 15 percent object-group test split does not enter checkpoint selection; 20 percent of development groups supply validation. Counts are realized counts in preparation.json and data-audit.json, not assumed independent asset counts. ## Evaluation Both checkpoints were evaluated on the same new held-out proxy data. | Metric | Starting Forma3D Tiny | Selected Forma3D Tiny 2 | |---|---:|---:| | Mean test loss | {bt['loss']:.6f} | {ft['loss']:.6f} | | Mean view occupancy IoU at threshold 0.5 | {bt['occupancy_iou']:.6f} | {ft['occupancy_iou']:.6f} | Test groups: {report['test_objects']}; views: {report['test_samples']}. See evaluation.json for hashes and release gates. Metrics are view-averaged over proxy renders. They do not establish photo realism, novel-view image quality, mesh quality, texture fidelity, gameplay, or general-purpose 3D reconstruction. ## Use Install this source with Python 3.11 and `uv pip install ".[data]"` in a virtual environment, then run: ```bash image3d generate --checkpoint model.pt --image your-centered-cutout.png \\ --out results/object.ply --device mps image3d preview --ply results/object.ply --out results/preview.png ``` Output is Gaussian PLY. Inputs should match centered objects on black. See COMMANDS.md for the complete continuation recipe. This model retains the same architecture; no text-prompt conditioning or new mesh decoder was added. ## License Publisher source and weights are offered under Apache 2.0. Retain NOTICE, THIRD_PARTY.md and ATTRIBUTIONS.json. CC BY Scenario assets and CC0 Objaverse assets have their own attribution and license records in DATASETS.md. ''' (destination / 'README.md').write_text(card) sums = [sha(f)+' '+str(f.relative_to(destination)) for f in sorted(destination.rglob('*')) if f.is_file()] (destination / 'SHA256SUMS').write_text('\n'.join(sums)+'\n') print('Staged evaluated release at',destination) print('Inspect PNG files under',Path(args.run)/'inspection','before optional publication.') if __name__ == '__main__': main()