jboth commited on
Commit
a9397fc
·
verified ·
1 Parent(s): ffd6a1b

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +43 -73
app.py CHANGED
@@ -1,4 +1,4 @@
1
- """SAM 3D Objects – kaolin stubbed for ZeroGPU (PyTorch 2.10+cu128)."""
2
  import os, sys, subprocess
3
  os.environ.setdefault("CUDA_HOME", "/usr/local/cuda")
4
  os.environ.setdefault("CONDA_PREFIX", "/usr/local")
@@ -16,29 +16,33 @@ from pathlib import Path
16
  if os.environ.get("HF_TOKEN"):
17
  login(token=os.environ["HF_TOKEN"])
18
 
19
- # --- Kaolin stub (must be before sam3d imports) ---
20
- STUB = Path("/home/user/app/kaolin_stub")
21
- if STUB.exists():
22
- sys.path.insert(0, str(STUB))
23
- print("Kaolin stub path added")
 
 
24
 
25
  # --- Runtime pip installs ---
26
  def _pip(*a):
27
  r = subprocess.run([sys.executable, "-m", "pip", "install", "--no-cache-dir"] + list(a),
28
  capture_output=True, text=True, timeout=1200)
29
  ok = r.returncode == 0
30
- if not ok:
31
- print(f" pip FAIL ({a[-1][:40]}): {r.stderr[-200:]}")
 
32
  else:
33
- print(f" pip OK: {a[-1][:40]}")
 
34
  return ok
35
 
36
  print("=== Runtime installs ===")
37
  _pip("open3d>=0.18.0")
38
- _pip("utils3d")
 
39
  _pip("iopath")
40
  _pip("--no-deps", "sam2>=1.1.0")
41
- _pip("--no-deps", "pytorch3d")
42
  _pip("--no-deps", "git+https://github.com/microsoft/MoGe.git@a8c37341bc0325ca99b9d57981cc3bb2bd3e255b")
43
 
44
  # gsplat
@@ -47,6 +51,14 @@ for idx in ["https://docs.gsplat.studio/whl/pt210cu128",
47
  if _pip("--no-deps", f"--extra-index-url={idx}", "gsplat"):
48
  break
49
 
 
 
 
 
 
 
 
 
50
  # --- Clone sam-3d-objects ---
51
  SAM3D_PATH = Path("/home/user/app/sam-3d-objects")
52
  if not SAM3D_PATH.exists():
@@ -77,73 +89,35 @@ if hf_ckpt.exists() and not local_ckpt.exists():
77
  local_ckpt.symlink_to(hf_ckpt)
78
  CONFIG_PATH = str(local_ckpt / "pipeline.yaml")
79
  print(f"Config exists: {Path(CONFIG_PATH).exists()}")
80
-
81
  print("=== Startup complete ===")
82
 
83
  # --- Endpoints ---
84
 
85
  @spaces.GPU(duration=60)
86
  def diagnose():
87
- """Safe diagnostic - test each module individually."""
88
  import torch
89
  lines = [f"torch={torch.__version__}", f"cuda={torch.cuda.is_available()}"]
90
  if torch.cuda.is_available():
91
  lines.append(f"gpu={torch.cuda.get_device_name()}")
92
-
93
- # Test each module safely
94
- for mod_name in ["kaolin", "open3d", "utils3d", "iopath"]:
95
  try:
96
- __import__(mod_name)
97
- lines.append(f"{mod_name}: OK")
98
  except Exception as e:
99
- lines.append(f"{mod_name}: FAIL - {e}")
100
-
101
- # Test sam2 (careful - no CUDA init at import)
102
  try:
103
  from sam2.automatic_mask_generator import SAM2AutomaticMaskGenerator
104
  lines.append("sam2: OK")
105
  except Exception as e:
106
  lines.append(f"sam2: FAIL - {e}")
107
-
108
- # Test gsplat carefully
109
- try:
110
- import gsplat
111
- lines.append(f"gsplat: OK ({gsplat.__version__})")
112
- except Exception as e:
113
- lines.append(f"gsplat: FAIL - {e}")
114
-
115
- # Test pytorch3d carefully
116
- try:
117
- import pytorch3d
118
- lines.append("pytorch3d: OK")
119
- except Exception as e:
120
- lines.append(f"pytorch3d: FAIL - {e}")
121
-
122
- # Test MoGe
123
- try:
124
- import moge
125
- lines.append("MoGe: OK")
126
- except Exception as e:
127
- lines.append(f"MoGe: FAIL - {e}")
128
-
129
- # Test SAM3D inference
130
  try:
131
  from inference import Inference
132
- lines.append("SAM3D inference: importable")
133
  except Exception as e:
134
- lines.append(f"SAM3D inference: FAIL - {e}")
135
-
136
- # Config exists?
137
  lines.append(f"config: {Path(CONFIG_PATH).exists()}")
138
-
139
  return "\n".join(lines)
140
 
141
- @spaces.GPU(duration=60)
142
- def diagnose_minimal():
143
- """Absolutely minimal GPU test."""
144
- import torch
145
- return f"torch={torch.__version__}, cuda={torch.cuda.is_available()}, gpu={torch.cuda.get_device_name() if torch.cuda.is_available() else 'none'}"
146
-
147
  @spaces.GPU(duration=300)
148
  def reconstruct_objects(image: np.ndarray):
149
  if image is None:
@@ -153,14 +127,11 @@ def reconstruct_objects(image: np.ndarray):
153
  t0 = time.time()
154
  print(f"GPU: {torch.cuda.get_device_name()}")
155
 
156
- # Load SAM2
157
  from sam2.automatic_mask_generator import SAM2AutomaticMaskGenerator
158
  sam2_gen = SAM2AutomaticMaskGenerator.from_pretrained("facebook/sam2-hiera-large")
159
- print(f" SAM2 ready ({time.time()-t0:.0f}s)")
160
 
161
  image_np = np.array(image) if not isinstance(image, np.ndarray) else image
162
-
163
- # Detect objects
164
  masks = sam2_gen.generate(image_np)
165
  if not masks:
166
  return None, image_np, "No objects detected"
@@ -169,21 +140,17 @@ def reconstruct_objects(image: np.ndarray):
169
 
170
  preview = image_np.copy()
171
  preview[best_mask] = (preview[best_mask] * 0.5 + np.array([0, 255, 0]) * 0.5).astype(np.uint8)
172
- print(f" {len(masks)} masks ({time.time()-t0:.0f}s)")
173
 
174
- # Load SAM3D
175
  from inference import Inference
176
  sam3d = Inference(CONFIG_PATH, compile=False)
177
- print(f" SAM3D ready ({time.time()-t0:.0f}s)")
178
 
179
- # Reconstruct
180
  result = sam3d(image=image_np, mask=best_mask, seed=42)
181
  print(f" Reconstructed ({time.time()-t0:.0f}s)")
182
-
183
  if result is None:
184
  return None, preview, "Reconstruction returned None"
185
 
186
- # Export to GLB
187
  od = tempfile.mkdtemp()
188
  glb = f"{od}/object.glb"
189
 
@@ -191,7 +158,7 @@ def reconstruct_objects(image: np.ndarray):
191
  if hasattr(result, "save_ply"):
192
  gs = result
193
  elif isinstance(result, dict):
194
- for k in ("gs", "gaussian", "gaussians"):
195
  v = result.get(k)
196
  if v is not None:
197
  gs = v[0] if isinstance(v, (list, tuple)) else v
@@ -212,15 +179,22 @@ def reconstruct_objects(image: np.ndarray):
212
  pcd.estimate_normals()
213
  mesh, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=8)
214
  o3d.io.write_triangle_mesh(glb, mesh)
 
 
 
 
 
 
 
215
  else:
216
- return None, preview, f"Cannot extract 3D from {type(result)}"
 
217
 
218
  n = 0
219
  try:
220
  n = len(trimesh.load(glb, force="mesh").faces)
221
  except Exception:
222
  pass
223
-
224
  elapsed = int(time.time() - t0)
225
  return glb, preview, f"OK: {len(masks)} objects, {n:,} faces ({elapsed}s)"
226
  except Exception as e:
@@ -245,12 +219,8 @@ with gr.Blocks(title="SAM 3D Objects") as demo:
245
  btn.click(reconstruct_objects, inputs=[inp], outputs=[m3d, prev, stat])
246
  m3d.change(lambda x: x, inputs=[m3d], outputs=[dl])
247
  with gr.Tab("Diagnose"):
248
- dbtn = gr.Button("Full Diagnose")
249
  dout = gr.Textbox(lines=15)
250
  dbtn.click(diagnose, outputs=[dout])
251
- gr.Markdown("---")
252
- mbtn = gr.Button("Minimal GPU Test")
253
- mout = gr.Textbox(lines=3)
254
- mbtn.click(diagnose_minimal, outputs=[mout])
255
 
256
  demo.launch(mcp_server=True)
 
1
+ """SAM 3D Objects – kaolin+pytorch3d stubbed for ZeroGPU (PyTorch 2.10+cu128)."""
2
  import os, sys, subprocess
3
  os.environ.setdefault("CUDA_HOME", "/usr/local/cuda")
4
  os.environ.setdefault("CONDA_PREFIX", "/usr/local")
 
16
  if os.environ.get("HF_TOKEN"):
17
  login(token=os.environ["HF_TOKEN"])
18
 
19
+ # --- Stubs (must be before sam3d imports) ---
20
+ STUB_KAOLIN = Path("/home/user/app/kaolin_stub")
21
+ STUB_PT3D = Path("/home/user/app/pytorch3d_stub")
22
+ for stub in [STUB_KAOLIN, STUB_PT3D]:
23
+ if stub.exists():
24
+ sys.path.insert(0, str(stub))
25
+ print(f"Stub added: {stub.name}")
26
 
27
  # --- Runtime pip installs ---
28
  def _pip(*a):
29
  r = subprocess.run([sys.executable, "-m", "pip", "install", "--no-cache-dir"] + list(a),
30
  capture_output=True, text=True, timeout=1200)
31
  ok = r.returncode == 0
32
+ tag = a[-1][:50] if a else "?"
33
+ if ok:
34
+ print(f" pip OK: {tag}")
35
  else:
36
+ print(f" pip FAIL: {tag}")
37
+ print(f" stderr: {r.stderr[-300:]}")
38
  return ok
39
 
40
  print("=== Runtime installs ===")
41
  _pip("open3d>=0.18.0")
42
+ # utils3d - pure Python wheel, install with verbose
43
+ _pip("-v", "utils3d")
44
  _pip("iopath")
45
  _pip("--no-deps", "sam2>=1.1.0")
 
46
  _pip("--no-deps", "git+https://github.com/microsoft/MoGe.git@a8c37341bc0325ca99b9d57981cc3bb2bd3e255b")
47
 
48
  # gsplat
 
51
  if _pip("--no-deps", f"--extra-index-url={idx}", "gsplat"):
52
  break
53
 
54
+ # Quick verify
55
+ for mod in ["utils3d", "open3d", "gsplat", "kaolin", "pytorch3d"]:
56
+ try:
57
+ __import__(mod)
58
+ print(f" import {mod}: OK")
59
+ except ImportError as e:
60
+ print(f" import {mod}: FAIL - {e}")
61
+
62
  # --- Clone sam-3d-objects ---
63
  SAM3D_PATH = Path("/home/user/app/sam-3d-objects")
64
  if not SAM3D_PATH.exists():
 
89
  local_ckpt.symlink_to(hf_ckpt)
90
  CONFIG_PATH = str(local_ckpt / "pipeline.yaml")
91
  print(f"Config exists: {Path(CONFIG_PATH).exists()}")
 
92
  print("=== Startup complete ===")
93
 
94
  # --- Endpoints ---
95
 
96
  @spaces.GPU(duration=60)
97
  def diagnose():
 
98
  import torch
99
  lines = [f"torch={torch.__version__}", f"cuda={torch.cuda.is_available()}"]
100
  if torch.cuda.is_available():
101
  lines.append(f"gpu={torch.cuda.get_device_name()}")
102
+ for mod in ["kaolin", "open3d", "utils3d", "iopath", "gsplat", "pytorch3d", "moge"]:
 
 
103
  try:
104
+ m = __import__(mod)
105
+ lines.append(f"{mod}: OK ({getattr(m, '__version__', '-')})")
106
  except Exception as e:
107
+ lines.append(f"{mod}: FAIL - {e}")
 
 
108
  try:
109
  from sam2.automatic_mask_generator import SAM2AutomaticMaskGenerator
110
  lines.append("sam2: OK")
111
  except Exception as e:
112
  lines.append(f"sam2: FAIL - {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  try:
114
  from inference import Inference
115
+ lines.append("SAM3D Inference: importable")
116
  except Exception as e:
117
+ lines.append(f"SAM3D Inference: FAIL - {e}")
 
 
118
  lines.append(f"config: {Path(CONFIG_PATH).exists()}")
 
119
  return "\n".join(lines)
120
 
 
 
 
 
 
 
121
  @spaces.GPU(duration=300)
122
  def reconstruct_objects(image: np.ndarray):
123
  if image is None:
 
127
  t0 = time.time()
128
  print(f"GPU: {torch.cuda.get_device_name()}")
129
 
 
130
  from sam2.automatic_mask_generator import SAM2AutomaticMaskGenerator
131
  sam2_gen = SAM2AutomaticMaskGenerator.from_pretrained("facebook/sam2-hiera-large")
132
+ print(f" SAM2 loaded ({time.time()-t0:.0f}s)")
133
 
134
  image_np = np.array(image) if not isinstance(image, np.ndarray) else image
 
 
135
  masks = sam2_gen.generate(image_np)
136
  if not masks:
137
  return None, image_np, "No objects detected"
 
140
 
141
  preview = image_np.copy()
142
  preview[best_mask] = (preview[best_mask] * 0.5 + np.array([0, 255, 0]) * 0.5).astype(np.uint8)
143
+ print(f" {len(masks)} masks, largest area={masks[0]['area']} ({time.time()-t0:.0f}s)")
144
 
 
145
  from inference import Inference
146
  sam3d = Inference(CONFIG_PATH, compile=False)
147
+ print(f" SAM3D loaded ({time.time()-t0:.0f}s)")
148
 
 
149
  result = sam3d(image=image_np, mask=best_mask, seed=42)
150
  print(f" Reconstructed ({time.time()-t0:.0f}s)")
 
151
  if result is None:
152
  return None, preview, "Reconstruction returned None"
153
 
 
154
  od = tempfile.mkdtemp()
155
  glb = f"{od}/object.glb"
156
 
 
158
  if hasattr(result, "save_ply"):
159
  gs = result
160
  elif isinstance(result, dict):
161
+ for k in ("gs", "gaussian", "gaussians", "scene"):
162
  v = result.get(k)
163
  if v is not None:
164
  gs = v[0] if isinstance(v, (list, tuple)) else v
 
179
  pcd.estimate_normals()
180
  mesh, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=8)
181
  o3d.io.write_triangle_mesh(glb, mesh)
182
+ elif isinstance(result, dict) and "mesh" in result:
183
+ m = result["mesh"]
184
+ if hasattr(m, "export"):
185
+ m.export(glb)
186
+ else:
187
+ import open3d as o3d
188
+ o3d.io.write_triangle_mesh(glb, m)
189
  else:
190
+ keys = list(result.keys()) if isinstance(result, dict) else dir(result)
191
+ return None, preview, f"Cannot extract 3D. Keys: {keys}"
192
 
193
  n = 0
194
  try:
195
  n = len(trimesh.load(glb, force="mesh").faces)
196
  except Exception:
197
  pass
 
198
  elapsed = int(time.time() - t0)
199
  return glb, preview, f"OK: {len(masks)} objects, {n:,} faces ({elapsed}s)"
200
  except Exception as e:
 
219
  btn.click(reconstruct_objects, inputs=[inp], outputs=[m3d, prev, stat])
220
  m3d.change(lambda x: x, inputs=[m3d], outputs=[dl])
221
  with gr.Tab("Diagnose"):
222
+ dbtn = gr.Button("Diagnose GPU & Modules")
223
  dout = gr.Textbox(lines=15)
224
  dbtn.click(diagnose, outputs=[dout])
 
 
 
 
225
 
226
  demo.launch(mcp_server=True)