Spaces:
Running on Zero
Running on Zero
Download app.py from mantrakp/component-studio-reference: direct link, hf CLI and curl.
- Browser
- Download file 7.87 kB
-
https://huggingface.co/spaces/mantrakp/component-studio-reference/resolve/main/app.py
- Command line
-
hf download hf://spaces/mantrakp/component-studio-reference/app.py
-
curl -L -o app.py https://huggingface.co/spaces/mantrakp/component-studio-reference/resolve/main/app.py
7.87 kB
| """Gradio entry point for local development and Hugging Face ZeroGPU.""" | |
| import os | |
| from pathlib import Path | |
| import gradio as gr | |
| from studio.contracts import Request | |
| from studio.pipeline import STAGES, execute | |
| ROOT = Path(__file__).resolve().parent | |
| def load_models(): | |
| role = os.environ.get("STUDIO_WORKER_ROLE") | |
| if role or os.environ.get("STUDIO_NATIVE") == "1": | |
| from scripts.prepare_runtime import prepare | |
| from studio.native import NativeModels | |
| prepare(role=role) | |
| return NativeModels(role=role) | |
| if os.environ.get("SPACE_ID") or os.environ.get("STUDIO_WORKERS"): | |
| from scripts.prepare_runtime import prepare_tools | |
| from studio.remote import RemoteModels | |
| prepare_tools() | |
| return RemoteModels() | |
| return None | |
| def build_app(models=None): | |
| def generate(image, prompt, parts, seed, animation_prompt="", animation_seconds=6, | |
| progress=gr.Progress(), source_mesh=None): | |
| try: | |
| request = Request(image=Path(image) if image else None, prompt=prompt, | |
| parts=int(parts), seed=int(seed), animation_prompt=animation_prompt, | |
| animation_seconds=animation_seconds) | |
| def notify(stage, detail): | |
| progress((STAGES.index(stage), len(STAGES)), desc=detail) | |
| run = execute(request, models, notify, source_mesh=source_mesh) | |
| selected = "after" if run.data["refinement"]["verdict"]["accept"] else "before" | |
| previews = sorted((run.path / selected).glob("*.png")) | |
| previews = [str(path) for path in previews if path.name != "contact.png"] | |
| report = run.data["refinement"] | |
| status = f"{run.data['metrics']['components']} components ready. {report['summary']}" | |
| if report["unresolved"]: | |
| status += "\n\nRemaining issues: " + "; ".join(report["unresolved"]) | |
| animation = run.data.get("animation") | |
| asset_path = run.path / "final.glb" | |
| video_path = None | |
| if not run.data.get("model_quality_passed", True): | |
| status += "\n\nModel needs correction before animation: " + report["verdict"]["reason"] | |
| if animation: | |
| asset_path = run.path / animation["artifacts"]["animated_glb"] | |
| video_path = str(run.path / animation["artifacts"]["video"]) | |
| status += "\n\n" + ("Animation accepted." if animation["accepted"] | |
| else "Animation needs correction: " + animation["visual_review"]["reason"]) | |
| progress(1, desc="Ready" if run.data["status"] == "complete" else "Needs review") | |
| return (str(asset_path), str(run.path / "reference.png"), previews, | |
| str(run.path / "asset.zip"), status, run.data, video_path) | |
| except Exception as error: | |
| raise gr.Error(str(error)) from None | |
| with gr.Blocks(title="Component Studio") as demo: | |
| gr.Markdown("# Component Studio\nImage or idea → refined 3D → animated character.") | |
| if models is None: | |
| gr.Markdown("**Local interface preview.** GPU inference requires the ZeroGPU runtime described in README.md.") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| image = gr.Image(type="filepath", label="Reference image", image_mode="RGBA") | |
| prompt = gr.Textbox(label="What do you want to make or change?", lines=3, | |
| placeholder="A worn brass desk lamp with a green glass shade") | |
| gr.Markdown("Use an image alone to reconstruct it. Use a prompt alone to create a reference. " | |
| "Use both to edit the reference before reconstruction.") | |
| animation_prompt = gr.Textbox(label="Animation", lines=2, | |
| placeholder="Runs forward at a steady pace with natural arm swing") | |
| gr.Markdown("Describe the motion for a humanoid character. Leave blank for a static asset.") | |
| with gr.Accordion("Generation settings", open=False): | |
| animation_seconds = gr.Slider(2, 12, value=6, step=1, label="Animation duration (seconds)") | |
| parts = gr.Slider(2, 32, value=8, step=1, label="Target components") | |
| seed = gr.Number(value=0, precision=0, label="Seed") | |
| gr.Markdown("1024px initial textures · 4× base-color upscale · 10% memory reserve") | |
| button = gr.Button("Create asset", variant="primary") | |
| with gr.Accordion("Continue an existing textured mesh", open=False): | |
| source_mesh = gr.File(label="Source GLB", file_types=[".glb"], type="filepath") | |
| continue_button = gr.Button("Continue from geometry") | |
| gr.Markdown("Supply the original reference above. Continue segmentation, upscaling and review without regenerating geometry.") | |
| gr.Examples(examples=[ | |
| [str(ROOT / "assets/stool.png"), ""], | |
| [None, "A worn brass desk lamp with a green glass shade, separate base, stem and shade"], | |
| [str(ROOT / "assets/stool.png"), "Keep this stool's shape; make the seat dark walnut and the legs black metal"], | |
| ], inputs=[image, prompt], label="Image only · prompt only · image + prompt", cache_examples=False) | |
| with gr.Column(scale=3): | |
| model = gr.Model3D(label="Final asset", height=480) | |
| video = gr.Video(label="Animation video", interactive=False) | |
| status = gr.Markdown("Your assembled asset and individual components will appear here.") | |
| download = gr.File(label="GLB, component GLBs, textures and review evidence") | |
| with gr.Tab("Review views"): | |
| gallery = gr.Gallery(label="Final review", columns=3) | |
| with gr.Tab("Reference"): | |
| reference = gr.Image(label="Reference used for geometry") | |
| with gr.Tab("Run details"): | |
| details = gr.JSON(label="Stage history and provenance") | |
| button.click(generate, inputs=[image, prompt, parts, seed, animation_prompt, animation_seconds], | |
| outputs=[model, reference, gallery, download, status, details, video], | |
| concurrency_limit=1, concurrency_id="asset-pipeline", api_name="generate") | |
| def continue_asset(image, source_mesh, prompt, parts, seed, animation_prompt="", animation_seconds=6, | |
| progress=gr.Progress()): | |
| if not image or not source_mesh: | |
| raise gr.Error("Supply both the reference image and source GLB.") | |
| return generate(image, prompt, parts, seed, animation_prompt, animation_seconds, | |
| progress=progress, source_mesh=source_mesh) | |
| continue_button.click(continue_asset, inputs=[image, source_mesh, prompt, parts, seed, animation_prompt, animation_seconds], | |
| outputs=[model, reference, gallery, download, status, details, video], | |
| concurrency_limit=1, concurrency_id="asset-pipeline", api_name="continue_asset") | |
| return demo.queue(max_size=8, default_concurrency_limit=1) | |
| if __name__ == "__main__": | |
| models = load_models() | |
| if role := os.environ.get("STUDIO_WORKER_ROLE"): | |
| from studio.worker import build_worker | |
| demo = build_worker(role, models) | |
| else: | |
| demo = build_app(models) | |
| demo.launch(server_name="0.0.0.0" if os.environ.get("SPACE_ID") else "127.0.0.1", | |
| server_port=int(os.environ.get("PORT", "7860")), | |
| allowed_paths=[str(ROOT / "outputs"), str(ROOT / "assets")], | |
| share=False) | |