chore: auto-commit (5 archivos)

- CMakeLists.txt
- app.md
- appicon.ico
- backend/
- main.cpp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 17:28:48 +02:00
commit d3c83053f2
14 changed files with 1020 additions and 0 deletions
View File
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
"""Interfaz comun para backends image-to-3D.
Cada backend (triposr, hunyuan3d_2, trellis) expone:
load() -> Handle
donde Handle tiene metodo infer(image: PIL.Image, cfg: dict) -> bytes (GLB).
`cfg` recibe: seed, mc_resolution, foreground_ratio, texture.
"""
from __future__ import annotations
from typing import Protocol, Any, Dict
from PIL import Image
class BackendHandle(Protocol):
def infer(self, image: Image.Image, cfg: Dict[str, Any]) -> bytes: ...
def close(self) -> None: ...
+23
View File
@@ -0,0 +1,23 @@
"""Backend Hunyuan3D-2 (Tencent, Community License).
STUB. Para implementar: clonar github.com/Tencent/Hunyuan3D-2 a sources/,
instalar deps, cargar pipeline shape + texture.
"""
from __future__ import annotations
from typing import Any, Dict
from PIL import Image
class Handle:
def infer(self, image: Image.Image, cfg: Dict[str, Any]) -> bytes:
raise NotImplementedError(
"hunyuan3d-2 backend pendiente. Ver notebook 03_smoke_hunyuan3d.ipynb"
)
def close(self) -> None: # pragma: no cover
return
def load() -> Handle:
return Handle()
+23
View File
@@ -0,0 +1,23 @@
"""Backend Trellis (Microsoft, MIT code).
STUB. Para implementar: clonar github.com/microsoft/TRELLIS a sources/,
instalar deps (kaolin + custom CUDA), cargar pipeline structured latents.
"""
from __future__ import annotations
from typing import Any, Dict
from PIL import Image
class Handle:
def infer(self, image: Image.Image, cfg: Dict[str, Any]) -> bytes:
raise NotImplementedError(
"trellis backend pendiente. Ver notebook 04_smoke_trellis.ipynb"
)
def close(self) -> None: # pragma: no cover
return
def load() -> Handle:
return Handle()
+93
View File
@@ -0,0 +1,93 @@
"""Backend TripoSR (Stability + Tripo, MIT).
Asume que `sources/TripoSR` esta clonado en el registry. Importa `tsr.system.TSR`.
Descarga checkpoint desde HF en la primera carga (~1.2 GB).
"""
from __future__ import annotations
import io
import os
import pathlib
import sys
from dataclasses import dataclass
from typing import Any, Dict
import numpy as np
import torch
import trimesh
from PIL import Image
def _ensure_sources_on_path() -> pathlib.Path:
root = pathlib.Path(os.environ.get("FN_REGISTRY_ROOT", "/home/lucas/fn_registry"))
src = root / "sources" / "TripoSR"
if not src.exists():
raise RuntimeError(
f"TripoSR no clonado en {src}. "
"git clone --depth=1 https://github.com/VAST-AI-Research/TripoSR.git "
f"{src}"
)
if str(src) not in sys.path:
sys.path.insert(0, str(src))
return src
@dataclass
class Handle:
model: Any
rembg_session: Any
device: str
def infer(self, image: Image.Image, cfg: Dict[str, Any]) -> bytes:
from tsr.utils import remove_background, resize_foreground
fg_ratio = float(cfg.get("foreground_ratio", 0.85))
mc_res = int(cfg.get("mc_resolution", 256))
fg = remove_background(image, self.rembg_session)
fg = resize_foreground(fg, fg_ratio)
# Composite RGBA -> RGB sobre gris 0.5 (preprocesado canonico TripoSR
# run.py). Sin esto el tokenizer DINO recibe 4 canales y peta:
# "The size of tensor a (4) must match tensor b (3) at dim 2".
arr = np.asarray(fg).astype(np.float32) / 255.0
if arr.shape[-1] == 4:
arr = arr[:, :, :3] * arr[:, :, 3:4] + (1.0 - arr[:, :, 3:4]) * 0.5
fg = Image.fromarray((arr * 255.0).astype(np.uint8))
with torch.no_grad():
scene_codes = self.model([fg], device=self.device)
meshes = self.model.extract_mesh(
scene_codes, has_vertex_color=False, resolution=mc_res
)
m = meshes[0]
tm = trimesh.Trimesh(
vertices=np.asarray(m.vertices),
faces=np.asarray(m.faces),
process=True,
)
buf = io.BytesIO()
tm.export(buf, file_type="glb")
return buf.getvalue()
def close(self) -> None:
del self.model
del self.rembg_session
if torch.cuda.is_available():
torch.cuda.empty_cache()
def load() -> Handle:
_ensure_sources_on_path()
from tsr.system import TSR
from rembg import new_session
device = "cuda" if torch.cuda.is_available() else "cpu"
model = TSR.from_pretrained(
"stabilityai/TripoSR",
config_name="config.yaml",
weight_name="model.ckpt",
)
model.renderer.set_chunk_size(8192)
model.to(device)
return Handle(model=model, rembg_session=new_session(), device=device)