manav0506 commited on
Commit
0796c7e
·
1 Parent(s): 8be256e

Sync deps: Dockerfile system deps + ffmpeg, single pip flow

Browse files
Files changed (4) hide show
  1. Dockerfile +2 -0
  2. README.md +14 -0
  3. app.py +44 -16
  4. scripts/mesh_generator.py +14 -1
Dockerfile CHANGED
@@ -19,6 +19,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
19
 
20
  COPY requirements.txt .
21
  RUN pip install --no-cache-dir -r requirements.txt
 
 
22
  # torchmcubes must be built after torch is installed (CMake needs TorchConfig.cmake)
23
  RUN pip install --no-cache-dir --no-build-isolation "git+https://github.com/tatsy/torchmcubes.git"
24
 
 
19
 
20
  COPY requirements.txt .
21
  RUN pip install --no-cache-dir -r requirements.txt
22
+ # torchmcubes: build deps for --no-build-isolation (scikit-build-core, pybind11, ninja)
23
+ RUN pip install --no-cache-dir scikit-build-core pybind11 ninja
24
  # torchmcubes must be built after torch is installed (CMake needs TorchConfig.cmake)
25
  RUN pip install --no-cache-dir --no-build-isolation "git+https://github.com/tatsy/torchmcubes.git"
26
 
README.md CHANGED
@@ -34,6 +34,20 @@ Add a token so the Space can download the model:
34
 
35
  After that, run Skybox or Mesh again; the model will download on first use.
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  ---
38
 
39
  Model: [evoneural/evoneuralIn3D](https://huggingface.co/evoneural/evoneuralIn3D)
 
34
 
35
  After that, run Skybox or Mesh again; the model will download on first use.
36
 
37
+ ## Ready to use
38
+
39
+ 1. Push this repo to a **Docker** Space (SDK: docker, port 7860).
40
+ 2. Set the **HF_TOKEN** secret (Settings → Variables and secrets).
41
+ 3. Restart the Space. First build may take **15–25 minutes** (installing PyTorch, TripoSR, torchmcubes).
42
+ 4. Open the app and generate a skybox or mesh. First run will download the Stable Diffusion model (~4 GB).
43
+
44
+ ## Troubleshooting
45
+
46
+ - **"Could not load Stable Diffusion"** → Add `HF_TOKEN` in Settings → Variables and secrets, then restart the Space.
47
+ - **"TripoSR not found"** → Rebuild the Space (the Dockerfile clones TripoSR). If you forked the Space, ensure the Dockerfile and `scripts/` are present.
48
+ - **Mesh generation times out** → Use **Mesh resolution 256** and disable **Bake texture atlas** for a faster run; GPU Spaces are much faster than CPU.
49
+ - **Build fails on torchmcubes** → The Dockerfile installs torch first, then builds torchmcubes with `--no-build-isolation` so CMake finds Torch. Do not change the order of `pip install` steps.
50
+
51
  ---
52
 
53
  Model: [evoneural/evoneuralIn3D](https://huggingface.co/evoneural/evoneuralIn3D)
app.py CHANGED
@@ -18,6 +18,9 @@ import streamlit as st
18
  OUTPUTS = ROOT / "outputs"
19
  OUTPUTS.mkdir(exist_ok=True)
20
 
 
 
 
21
 
22
  def main() -> None:
23
  st.set_page_config(
@@ -25,29 +28,41 @@ def main() -> None:
25
  page_icon="🎮",
26
  layout="wide",
27
  )
28
- st.title("Evoneural MVP – Local Mesh & Skybox")
29
- st.caption("Text 3D mesh (TripoSR) and Text → 360° skybox (Stable Diffusion). Runs on localhost.")
 
 
 
 
30
 
31
  # Sidebar: model setup (token + download)
32
  with st.sidebar:
33
  st.subheader("Stable Diffusion model")
 
 
 
 
 
 
 
34
  from scripts.skybox_generator import _default_local_weights_dir
35
  local_model = _default_local_weights_dir()
36
  if local_model:
37
- st.success(f"Local model: found")
38
  st.caption(os.path.basename(local_model))
39
- else:
40
  st.warning("No local model. Download below or need internet on first generate.")
41
- hf_token = st.text_input(
42
- "Hugging Face token (optional, if behind firewall)",
43
- type="password",
44
- key="hf_token",
45
- placeholder="hf_...",
46
- help="Get a token at huggingface.co/settings/tokens",
47
- )
48
- if hf_token:
49
- os.environ["HF_TOKEN"] = hf_token
50
- if st.button("Download model (~4GB to ./weights/sd-v1-5)", key="btn_download"):
 
51
  with st.spinner("Downloading model... (may take several minutes)"):
52
  try:
53
  from scripts.download_sd_model import download_sd_model
@@ -57,6 +72,13 @@ def main() -> None:
57
  except Exception as e:
58
  st.error(str(e))
59
  st.caption("Set a Hugging Face token above if your network blocks Hugging Face.")
 
 
 
 
 
 
 
60
 
61
  tab_mesh, tab_skybox = st.tabs(["🟦 Text → 3D Mesh", "🌅 Text → Skybox"])
62
 
@@ -130,8 +152,8 @@ def main() -> None:
130
  triposr_root = find_triposr_root(str(ROOT))
131
  if not triposr_root:
132
  st.error(
133
- "TripoSR not found. Clone it: "
134
- "`git clone https://github.com/VAST-AI-Research/TripoSR.git TripoSR`"
135
  )
136
  elif image_path_to_use:
137
  path = image_path_to_use
@@ -140,6 +162,8 @@ def main() -> None:
140
  with open(path, "wb") as f:
141
  f.write(uploaded.getvalue())
142
  if path != "upload" and os.path.isfile(path):
 
 
143
  mesh_path, elapsed, msg = generate_mesh_from_image(
144
  path,
145
  output_dir=str(OUTPUTS / "mesh_run"),
@@ -147,6 +171,7 @@ def main() -> None:
147
  mc_resolution=mc_resolution,
148
  bake_texture=bake_texture,
149
  smooth_mesh=smooth_mesh,
 
150
  )
151
  if mesh_path:
152
  st.success(f"Done in {elapsed:.1f}s. {msg}")
@@ -157,6 +182,8 @@ def main() -> None:
157
  elif path == "upload":
158
  st.warning("Upload an image first.")
159
  else:
 
 
160
  mesh_path, elapsed, msg = generate_mesh_from_text(
161
  prompt_mesh,
162
  output_dir=str(OUTPUTS),
@@ -165,6 +192,7 @@ def main() -> None:
165
  mc_resolution=mc_resolution,
166
  bake_texture=bake_texture,
167
  smooth_mesh=smooth_mesh,
 
168
  )
169
  if mesh_path:
170
  st.success(f"Done in {elapsed:.1f}s. {msg}")
 
18
  OUTPUTS = ROOT / "outputs"
19
  OUTPUTS.mkdir(exist_ok=True)
20
 
21
+ # Hugging Face Space: HF sets SPACE_ID when running in a Space
22
+ IS_HF_SPACE = bool(os.environ.get("SPACE_ID") or os.environ.get("SPACE_REPO_ID"))
23
+
24
 
25
  def main() -> None:
26
  st.set_page_config(
 
28
  page_icon="🎮",
29
  layout="wide",
30
  )
31
+ if IS_HF_SPACE:
32
+ st.title("EvoneuralIn3D Mesh & Skybox")
33
+ st.caption("Text → 3D mesh (TripoSR) and Text → 360° skybox (Stable Diffusion). Running on Hugging Face Space.")
34
+ else:
35
+ st.title("Evoneural MVP – Local Mesh & Skybox")
36
+ st.caption("Text → 3D mesh (TripoSR) and Text → 360° skybox (Stable Diffusion). Runs on localhost.")
37
 
38
  # Sidebar: model setup (token + download)
39
  with st.sidebar:
40
  st.subheader("Stable Diffusion model")
41
+ hf_token_env = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
42
+ if IS_HF_SPACE:
43
+ if hf_token_env:
44
+ st.success("HF_TOKEN is set (from Space secrets)")
45
+ else:
46
+ st.error("HF_TOKEN not set")
47
+ st.caption("Add it in this Space: **Settings** → **Variables and secrets** → New secret: `HF_TOKEN`. Then restart the Space.")
48
  from scripts.skybox_generator import _default_local_weights_dir
49
  local_model = _default_local_weights_dir()
50
  if local_model:
51
+ st.success("Local model: found")
52
  st.caption(os.path.basename(local_model))
53
+ elif not IS_HF_SPACE:
54
  st.warning("No local model. Download below or need internet on first generate.")
55
+ if not IS_HF_SPACE:
56
+ hf_token = st.text_input(
57
+ "Hugging Face token (optional, if behind firewall)",
58
+ type="password",
59
+ key="hf_token",
60
+ placeholder="hf_...",
61
+ help="Get a token at huggingface.co/settings/tokens",
62
+ )
63
+ if hf_token:
64
+ os.environ["HF_TOKEN"] = hf_token
65
+ if not IS_HF_SPACE and st.button("Download model (~4GB to ./weights/sd-v1-5)", key="btn_download"):
66
  with st.spinner("Downloading model... (may take several minutes)"):
67
  try:
68
  from scripts.download_sd_model import download_sd_model
 
72
  except Exception as e:
73
  st.error(str(e))
74
  st.caption("Set a Hugging Face token above if your network blocks Hugging Face.")
75
+ # Environment check: TripoSR (useful in Space)
76
+ from scripts.mesh_generator import find_triposr_root as _find_triposr
77
+ triposr_ok = _find_triposr(str(ROOT)) is not None
78
+ if triposr_ok:
79
+ st.caption("TripoSR: ready")
80
+ else:
81
+ st.caption("TripoSR: not found (mesh tab will show instructions)")
82
 
83
  tab_mesh, tab_skybox = st.tabs(["🟦 Text → 3D Mesh", "🌅 Text → Skybox"])
84
 
 
152
  triposr_root = find_triposr_root(str(ROOT))
153
  if not triposr_root:
154
  st.error(
155
+ "TripoSR not found. In this Space the Docker image should include it. "
156
+ "If you see this, rebuild the Space or check the Dockerfile."
157
  )
158
  elif image_path_to_use:
159
  path = image_path_to_use
 
162
  with open(path, "wb") as f:
163
  f.write(uploaded.getvalue())
164
  if path != "upload" and os.path.isfile(path):
165
+ import torch as _torch
166
+ _dev = "cuda:0" if _torch.cuda.is_available() else "cpu"
167
  mesh_path, elapsed, msg = generate_mesh_from_image(
168
  path,
169
  output_dir=str(OUTPUTS / "mesh_run"),
 
171
  mc_resolution=mc_resolution,
172
  bake_texture=bake_texture,
173
  smooth_mesh=smooth_mesh,
174
+ device=_dev,
175
  )
176
  if mesh_path:
177
  st.success(f"Done in {elapsed:.1f}s. {msg}")
 
182
  elif path == "upload":
183
  st.warning("Upload an image first.")
184
  else:
185
+ import torch as _torch
186
+ _dev = "cuda:0" if _torch.cuda.is_available() else "cpu"
187
  mesh_path, elapsed, msg = generate_mesh_from_text(
188
  prompt_mesh,
189
  output_dir=str(OUTPUTS),
 
192
  mc_resolution=mc_resolution,
193
  bake_texture=bake_texture,
194
  smooth_mesh=smooth_mesh,
195
+ device=_dev,
196
  )
197
  if mesh_path:
198
  st.success(f"Done in {elapsed:.1f}s. {msg}")
scripts/mesh_generator.py CHANGED
@@ -82,7 +82,7 @@ def generate_mesh_from_image(
82
  output_dir: str = "outputs",
83
  mesh_format: str = "glb",
84
  triposr_root: str | None = None,
85
- device: str = "cuda:0",
86
  use_hunyuan3d2: bool | None = None,
87
  mc_resolution: int = 512,
88
  bake_texture: bool = True,
@@ -118,6 +118,7 @@ def generate_mesh_from_image(
118
  "TripoSR not found. Clone it: git clone https://github.com/VAST-AI-Research/TripoSR.git",
119
  )
120
 
 
121
  Path(output_dir).mkdir(parents=True, exist_ok=True)
122
  # When baking texture, xatlas.export() always writes OBJ format (ignores .glb extension).
123
  # Ask for OBJ when bake_texture + glb, then we convert OBJ+texture to real GLB.
@@ -204,6 +205,15 @@ def find_hunyuan3d2_root(project_root: str | None = None) -> str | None:
204
  return None
205
 
206
 
 
 
 
 
 
 
 
 
 
207
  def generate_mesh_from_text(
208
  prompt: str,
209
  output_dir: str = "outputs",
@@ -214,6 +224,7 @@ def generate_mesh_from_text(
214
  bake_texture: bool = True,
215
  texture_resolution: int = 2048,
216
  smooth_mesh: bool = True,
 
217
  ) -> tuple[str | None, float, str]:
218
  """
219
  Text → 3D mesh. Uses Hunyuan3D-2 full pipeline when available (use_hunyuan3d2=True or repo found),
@@ -248,6 +259,7 @@ def generate_mesh_from_text(
248
  image_path, _ = text_to_image(prompt, output_dir=output_dir, seed=seed)
249
  except Exception as e:
250
  return (None, 0.0, f"Text-to-image failed: {e}")
 
251
  mesh_path, mesh_time, msg = generate_mesh_from_image(
252
  image_path,
253
  output_dir=os.path.join(output_dir, "mesh_run"),
@@ -256,6 +268,7 @@ def generate_mesh_from_text(
256
  bake_texture=bake_texture,
257
  texture_resolution=texture_resolution,
258
  smooth_mesh=smooth_mesh,
 
259
  )
260
  total_time = time.perf_counter() - t0
261
  if mesh_path:
 
82
  output_dir: str = "outputs",
83
  mesh_format: str = "glb",
84
  triposr_root: str | None = None,
85
+ device: str | None = None,
86
  use_hunyuan3d2: bool | None = None,
87
  mc_resolution: int = 512,
88
  bake_texture: bool = True,
 
118
  "TripoSR not found. Clone it: git clone https://github.com/VAST-AI-Research/TripoSR.git",
119
  )
120
 
121
+ device = device or _infer_device()
122
  Path(output_dir).mkdir(parents=True, exist_ok=True)
123
  # When baking texture, xatlas.export() always writes OBJ format (ignores .glb extension).
124
  # Ask for OBJ when bake_texture + glb, then we convert OBJ+texture to real GLB.
 
205
  return None
206
 
207
 
208
+ def _infer_device() -> str:
209
+ """Use GPU if available, else CPU (for CPU-only Spaces)."""
210
+ try:
211
+ import torch
212
+ return "cuda:0" if torch.cuda.is_available() else "cpu"
213
+ except Exception:
214
+ return "cpu"
215
+
216
+
217
  def generate_mesh_from_text(
218
  prompt: str,
219
  output_dir: str = "outputs",
 
224
  bake_texture: bool = True,
225
  texture_resolution: int = 2048,
226
  smooth_mesh: bool = True,
227
+ device: str | None = None,
228
  ) -> tuple[str | None, float, str]:
229
  """
230
  Text → 3D mesh. Uses Hunyuan3D-2 full pipeline when available (use_hunyuan3d2=True or repo found),
 
259
  image_path, _ = text_to_image(prompt, output_dir=output_dir, seed=seed)
260
  except Exception as e:
261
  return (None, 0.0, f"Text-to-image failed: {e}")
262
+ device = device or _infer_device()
263
  mesh_path, mesh_time, msg = generate_mesh_from_image(
264
  image_path,
265
  output_dir=os.path.join(output_dir, "mesh_run"),
 
268
  bake_texture=bake_texture,
269
  texture_resolution=texture_resolution,
270
  smooth_mesh=smooth_mesh,
271
+ device=device,
272
  )
273
  total_time = time.perf_counter() - t0
274
  if mesh_path: