Spaces:
Running on Zero
Running on Zero
File size: 8,176 Bytes
5c331a4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | """CPU orchestration of independent ZeroGPU Spaces with per-call artifact isolation."""
from __future__ import annotations
import json
import os
from pathlib import Path
import shutil
from tempfile import TemporaryDirectory
import zipfile
import numpy as np
from PIL import Image
STAGES = ("reference", "geometry", "segment", "texture", "motion")
DEFAULT_WORKERS = {stage: f"mantrakp/component-studio-{stage}" for stage in STAGES}
def workers_from_environment():
configured = json.loads(os.environ.get("STUDIO_WORKERS", "{}"))
if not isinstance(configured, dict) or set(configured) - set(STAGES):
raise ValueError("STUDIO_WORKERS must map known stage names to Space IDs.")
workers = DEFAULT_WORKERS | configured
if any(not isinstance(value, str) or not value.strip() for value in workers.values()):
raise ValueError("Each worker must have a nonempty Space ID or URL.")
return workers
def artifact_path(value):
if isinstance(value, dict):
value = value.get("path")
if not isinstance(value, (str, Path)):
raise RuntimeError("Worker did not return a downloaded artifact.")
path = Path(value)
if not path.is_file() or path.stat().st_size == 0:
raise RuntimeError("Worker returned a missing or empty artifact.")
return path
def load_image(value):
with Image.open(artifact_path(value)) as image:
image.load()
return image.copy()
class RemoteModels:
def __init__(self, workers=None, client_factory=None, token=None):
self.workers = workers or workers_from_environment()
self.client_factory = client_factory
self.token = token
def _call(self, stage, directory, *args):
from gradio_client import Client
from huggingface_hub import get_token
# Construct a fresh client for each stage: GPU proxy authorization must not
# survive the CPU review/rigging interval between generation and motion.
factory = self.client_factory or Client
client = factory(self.workers[stage], hf_token=self.token or os.environ.get("HF_TOKEN") or get_token(),
download_files=str(directory), verbose=False)
return client.predict(*args, api_name=f"/{stage}")
def reference(self, image, prompt, seed):
from gradio_client import handle_file
with TemporaryDirectory(prefix="studio-reference-") as directory:
root = Path(directory)
source = None
if image is not None:
image.save(root / "input.png")
source = handle_file(str(root / "input.png"))
return load_image(self._call("reference", root, source, prompt, seed))
def geometry(self, image, seed, output):
from gradio_client import handle_file
from .mesh import load_mesh
with TemporaryDirectory(prefix="studio-geometry-") as directory:
root = Path(directory)
image.save(root / "input.png")
result = artifact_path(self._call("geometry", root, handle_file(str(root / "input.png")), seed))
load_mesh(result)
output = Path(output)
output.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(result, output)
return output
def segment(self, mesh, count, seed):
from gradio_client import handle_file
with TemporaryDirectory(prefix="studio-segment-") as directory:
root = Path(directory)
np.savez(root / "mesh.npz", vertices=mesh.vertices, faces=mesh.faces)
result = self._call("segment", root, handle_file(str(root / "mesh.npz")), count, seed)
labels = np.load(artifact_path(result), allow_pickle=False)
if (labels.shape != (len(mesh.faces),) or labels.dtype.kind not in "iu"
or np.any(labels < 0) or np.any(labels >= count)):
raise RuntimeError("Segmentation worker returned invalid per-face labels.")
return labels.copy()
def upscale(self, parts, log):
from gradio_client import handle_file
if not parts:
raise ValueError("Texture upscaling requires at least one component.")
with TemporaryDirectory(prefix="studio-texture-") as directory:
root = Path(directory)
manifest = []
with zipfile.ZipFile(root / "input.zip", "w", zipfile.ZIP_DEFLATED) as bundle:
for index, (key, part) in enumerate(parts):
name = f"{index}.png"
part.visual.material.baseColorTexture.save(root / name)
bundle.write(root / name, name)
manifest.append({"key": key, "file": name})
bundle.writestr("manifest.json", json.dumps(manifest))
result, stats, events = self._call("texture", root, handle_file(str(root / "input.zip")))
images = []
with zipfile.ZipFile(artifact_path(result)) as bundle:
returned = json.loads(bundle.read("manifest.json"))
if returned != manifest:
raise RuntimeError("Texture worker changed the component manifest.")
for record in manifest:
with bundle.open(record["file"]) as stream, Image.open(stream) as image:
image.load()
images.append(image.copy())
# Validate everything before mutating the meshes. Geometry, UVs and all
# other material channels remain owned by the caller.
if not isinstance(stats, dict) or not isinstance(events, list):
raise RuntimeError("Texture worker returned invalid metadata.")
for (_, part), image in zip(parts, images, strict=True):
original = part.visual.material.baseColorTexture
if image.size != (original.width * 4, original.height * 4):
raise RuntimeError("Texture worker did not return a four-times upscaled image.")
for (_, part), image in zip(parts, images, strict=True):
part.visual.material.baseColorTexture = image
for event in events:
log(str(event))
return {**stats, "worker_space": self.workers["texture"]}
def motion(self, prompt, duration, seed, output):
with TemporaryDirectory(prefix="studio-motion-") as directory:
root = Path(directory)
npz, bvh, provenance = self._call("motion", root, prompt, duration, seed)
npz, bvh = artifact_path(npz), artifact_path(bvh)
with np.load(npz, allow_pickle=False) as data:
frames = round(duration * 30)
shapes = {"posed_joints": (frames, 77, 3), "global_rot_mats": (frames, 77, 3, 3),
"local_rot_mats": (frames, 77, 3, 3), "root_positions": (frames, 3)}
for key, shape in shapes.items():
if key not in data or data[key].shape != shape or not np.isfinite(data[key]).all():
raise RuntimeError(f"Motion worker returned invalid {key}.")
if ("foot_contacts" not in data or data["foot_contacts"].shape[0] != frames
or not np.isfinite(data["foot_contacts"]).all()):
raise RuntimeError("Motion worker returned invalid foot contacts.")
if not bvh.read_text().lstrip().startswith("HIERARCHY"):
raise RuntimeError("Motion worker returned an invalid BVH file.")
if not isinstance(provenance, dict) or provenance.get("frames") != frames:
raise RuntimeError("Motion worker returned invalid provenance.")
output = Path(output)
output.mkdir(parents=True, exist_ok=True)
shutil.copyfile(npz, output / "motion.npz")
shutil.copyfile(bvh, output / "motion.bvh")
provenance = {**provenance, "worker_space": self.workers["motion"]}
(output / "provenance.json").write_text(json.dumps(provenance, indent=2))
return {"npz": output / "motion.npz", "bvh": output / "motion.bvh", "provenance": provenance}
|