akhaliq HF Staff commited on
Commit
ad07c70
·
verified ·
1 Parent(s): 73167bd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -147
app.py CHANGED
@@ -1,94 +1,91 @@
1
- # Copyright (c) Meta Platforms, Inc. and affiliates.
2
  import os
3
  import subprocess
4
  import sys
 
 
 
 
 
5
 
6
  # ============ Install Dependencies & Setup ============
7
  def install_dependencies():
8
- """Install all required dependencies."""
9
- subprocess.run([sys.executable, "-m", "pip", "install", "--upgrade", "pip"], check=True)
10
-
11
- # Core dependencies
12
- core_deps = [
13
- "spaces",
14
- "gradio>=4.0.0",
15
- "numpy",
16
- "Pillow",
17
- "omegaconf",
18
- "hydra-core",
19
- "einops",
20
- "timm",
21
- "safetensors",
22
- "accelerate",
23
- "transformers",
24
- "diffusers",
25
- "trimesh",
26
- "utils3d",
27
- ]
28
- subprocess.run([sys.executable, "-m", "pip", "install"] + core_deps, check=True)
29
-
30
- # PyTorch with CUDA 12.1
31
- subprocess.run([
32
- sys.executable, "-m", "pip", "install",
33
- "torch>=2.1.0", "torchvision",
34
- "--extra-index-url", "https://download.pytorch.org/whl/cu121"
35
- ], check=True)
36
 
37
- # PyTorch3D
38
- subprocess.run([
39
- sys.executable, "-m", "pip", "install",
40
- "pytorch3d",
41
- "--find-links", "https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py310_cu121_pyt251/download.html"
42
- ], check=True)
 
 
43
 
44
- # xformers (optional but recommended)
45
- subprocess.run([sys.executable, "-m", "pip", "install", "xformers"], check=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  # Run installation
48
- print("Installing dependencies...")
49
  install_dependencies()
50
 
51
- # Clone repo
52
- REPO_DIR = "/home/user/app/sam-3d-objects"
53
- if not os.path.exists(REPO_DIR):
54
- print("Cloning sam-3d-objects repository...")
55
- subprocess.run([
56
- "git", "clone",
57
- "https://github.com/facebookresearch/sam-3d-objects.git",
58
- REPO_DIR
59
- ], check=True)
60
-
61
- # Install sam3d-objects package
62
- subprocess.run([
63
- sys.executable, "-m", "pip", "install", "-e", REPO_DIR
64
- ], check=True)
65
-
66
  # Add repo to Python path
67
  if REPO_DIR not in sys.path:
68
  sys.path.insert(0, REPO_DIR)
69
 
70
- # Set environment variables
71
  os.environ["CUDA_HOME"] = "/usr/local/cuda"
72
  os.environ["LIDRA_SKIP_INIT"] = "true"
73
- os.environ["PYTORCH3D_NO_CUDA_CHECK"] = "1"
 
74
 
 
75
  import spaces
 
76
  from typing import Optional, List, Callable
 
 
77
  import numpy as np
78
  from PIL import Image
79
- from omegaconf import OmegaConf, DictConfig, ListConfig
80
- from hydra.utils import instantiate, get_method
81
  import torch
82
  import math
83
- import shutil
84
- import builtins
85
- from copy import deepcopy
86
- import gradio as gr
87
 
88
- # Lazy imports - loaded after GPU allocation
89
  _sam3d_imported = False
90
  _pipeline = None
91
 
 
92
  WHITELIST_FILTERS = [
93
  lambda target: target.split(".", 1)[0] in {"sam3d_objects", "torch", "torchvision", "moge"},
94
  ]
@@ -124,6 +121,7 @@ def check_hydra_safety(config: DictConfig, whitelist_filters: List[Callable], bl
124
  elif isinstance(node, ListConfig):
125
  to_check.extend(list(node))
126
 
 
127
  def lazy_import_sam3d():
128
  """Import sam3d modules lazily after GPU is available."""
129
  global _sam3d_imported
@@ -131,25 +129,30 @@ def lazy_import_sam3d():
131
  global utils3d, sam3d_objects, InferencePipelinePointMap, render_utils, SceneVisualizer
132
  global quaternion_multiply, quaternion_invert
133
 
134
- import utils3d as _utils3d
135
- utils3d = _utils3d
136
-
137
- import sam3d_objects as _sam3d_objects
138
- sam3d_objects = _sam3d_objects
139
-
140
- from sam3d_objects.pipeline.inference_pipeline_pointmap import InferencePipelinePointMap as _IPP
141
- InferencePipelinePointMap = _IPP
142
-
143
- from sam3d_objects.model.backbone.tdfy_dit.utils import render_utils as _ru
144
- render_utils = _ru
145
-
146
- from sam3d_objects.utils.visualization import SceneVisualizer as _SV
147
- SceneVisualizer = _SV
148
-
149
- from pytorch3d.transforms import quaternion_multiply as _qm, quaternion_invert as _qi
150
- quaternion_multiply, quaternion_invert = _qm, _qi
151
-
152
- _sam3d_imported = True
 
 
 
 
 
153
 
154
  def load_pipeline(config_file: str):
155
  """Load the inference pipeline (call inside GPU context)."""
@@ -173,11 +176,14 @@ def merge_mask_to_rgba(image, mask):
173
  def run_inference(image: np.ndarray, mask: np.ndarray, config_file: str, seed: Optional[int] = None, pointmap=None) -> dict:
174
  """GPU-decorated inference function for ZeroGPU."""
175
  global _pipeline
 
 
176
  _pipeline = load_pipeline(config_file)
177
  if hasattr(_pipeline, 'to'):
178
  _pipeline.to('cuda')
179
 
180
  rgba_image = merge_mask_to_rgba(image, mask)
 
181
  return _pipeline.run(
182
  rgba_image, None, seed,
183
  stage1_only=False,
@@ -189,6 +195,8 @@ def run_inference(image: np.ndarray, mask: np.ndarray, config_file: str, seed: O
189
  pointmap=pointmap,
190
  )
191
 
 
 
192
  def _yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitchs, rs, fovs):
193
  lazy_import_sam3d()
194
  is_list = isinstance(yaws, list)
@@ -225,72 +233,6 @@ def render_video_gpu(sample, resolution=512, bg_color=(0,0,0), num_frames=300, r
225
  extr, intr = _yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitch, r, fov)
226
  return render_utils.render_frames(sample, extr, intr, {"resolution": resolution, "bg_color": bg_color, "backend": "gsplat"}, **kwargs)
227
 
228
- def normalized_gaussian(scene_gs, in_place=False, outlier_percentile=None):
229
- if not in_place:
230
- scene_gs = deepcopy(scene_gs)
231
- orig_xyz, orig_scale = scene_gs.get_xyz, scene_gs.get_scaling
232
- active_mask = (scene_gs.get_opacity > 0.9).squeeze()
233
- inv_scale = (orig_xyz[active_mask].max(dim=0)[0] - orig_xyz[active_mask].min(dim=0)[0]).max()
234
- norm_scale, norm_xyz = orig_scale / inv_scale, orig_xyz / inv_scale
235
- if outlier_percentile is None:
236
- lower = torch.min(norm_xyz[active_mask], dim=0)[0]
237
- upper = torch.max(norm_xyz[active_mask], dim=0)[0]
238
- else:
239
- lower = torch.quantile(norm_xyz[active_mask], outlier_percentile, dim=0)
240
- upper = torch.quantile(norm_xyz[active_mask], 1.0 - outlier_percentile, dim=0)
241
- scene_gs.from_xyz(norm_xyz - (lower + upper) / 2)
242
- scene_gs.mininum_kernel_size /= inv_scale.item()
243
- scene_gs.from_scaling(norm_scale)
244
- return scene_gs
245
-
246
- def _fix_gaussian_alignment(scene_gs, in_place=False):
247
- if not in_place:
248
- scene_gs = deepcopy(scene_gs)
249
- device, dtype = scene_gs._xyz.device, scene_gs._xyz.dtype
250
- scene_gs._xyz = scene_gs._xyz @ torch.tensor([[-1,0,0],[0,0,1],[0,1,0]], device=device, dtype=dtype).T
251
- return scene_gs
252
-
253
- def ready_gaussian_for_video_rendering(scene_gs, in_place=False, fix_alignment=False):
254
- if fix_alignment:
255
- scene_gs = _fix_gaussian_alignment(scene_gs, in_place=in_place)
256
- return normalized_gaussian(scene_gs, in_place=fix_alignment)
257
-
258
- def make_scene(*outputs, in_place=False):
259
- lazy_import_sam3d()
260
- if not in_place:
261
- outputs = [deepcopy(o) for o in outputs]
262
- all_outs, min_kernel = [], float("inf")
263
- for output in outputs:
264
- PC = SceneVisualizer.object_pointcloud(
265
- points_local=output["gaussian"][0].get_xyz.unsqueeze(0),
266
- quat_l2c=output["rotation"], trans_l2c=output["translation"], scale_l2c=output["scale"])
267
- output["gaussian"][0].from_xyz(PC.points_list()[0])
268
- output["gaussian"][0].from_rotation(quaternion_multiply(quaternion_invert(output["rotation"]), output["gaussian"][0].get_rotation))
269
- scale = output["gaussian"][0].get_scaling * output["scale"]
270
- assert output["scale"][0,0].item() == output["scale"][0,1].item() == output["scale"][0,2].item()
271
- output["gaussian"][0].mininum_kernel_size *= output["scale"][0,0].item()
272
- scale = torch.maximum(scale, torch.tensor(output["gaussian"][0].mininum_kernel_size * 1.1, device=scale.device))
273
- output["gaussian"][0].from_scaling(scale)
274
- min_kernel = min(min_kernel, output["gaussian"][0].mininum_kernel_size)
275
- all_outs.append(output)
276
- scene_gs = all_outs[0]["gaussian"][0]
277
- scene_gs.mininum_kernel_size = min_kernel
278
- for out in all_outs[1:]:
279
- gs = out["gaussian"][0]
280
- scene_gs._xyz = torch.cat([scene_gs._xyz, gs._xyz], dim=0)
281
- scene_gs._features_dc = torch.cat([scene_gs._features_dc, gs._features_dc], dim=0)
282
- scene_gs._scaling = torch.cat([scene_gs._scaling, gs._scaling], dim=0)
283
- scene_gs._rotation = torch.cat([scene_gs._rotation, gs._rotation], dim=0)
284
- scene_gs._opacity = torch.cat([scene_gs._opacity, gs._opacity], dim=0)
285
- return scene_gs
286
-
287
- def load_image(path):
288
- return np.array(Image.open(path)).astype(np.uint8)
289
-
290
- def load_mask(path):
291
- mask = load_image(path) > 0
292
- return mask[..., -1] if mask.ndim == 3 else mask
293
-
294
  # ============ Gradio Interface ============
295
  CONFIG_FILE = os.path.join(REPO_DIR, "configs/inference.yaml")
296
 
@@ -312,6 +254,8 @@ def process_image(input_image, input_mask, seed):
312
  return ply_path, "✅ Inference complete!"
313
  return None, "⚠️ No 3D output generated"
314
  except Exception as e:
 
 
315
  return None, f"❌ Error: {str(e)}"
316
 
317
  with gr.Blocks(title="SAM 3D Objects", theme=gr.themes.Soft()) as demo:
 
 
1
  import os
2
  import subprocess
3
  import sys
4
+ import shutil
5
+
6
+ # ============ Configuration ============
7
+ REPO_URL = "https://github.com/facebookresearch/sam-3d-objects.git"
8
+ REPO_DIR = "/home/user/app/sam-3d-objects"
9
 
10
  # ============ Install Dependencies & Setup ============
11
  def install_dependencies():
12
+ """
13
+ Installs dependencies using the official repo method (pip install -e .[extras])
14
+ instead of manual package listing.
15
+ """
16
+ print("Starting installation sequence...")
17
+
18
+ # 1. Clone Repository
19
+ if not os.path.exists(REPO_DIR):
20
+ print(f"Cloning repository to {REPO_DIR}...")
21
+ subprocess.run(["git", "clone", REPO_URL, REPO_DIR], check=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ # Switch working directory to repo for local installs
24
+ os.chdir(REPO_DIR)
25
+
26
+ # 2. Set Environment Variables for PIP
27
+ # As per instructions: export PIP_EXTRA_INDEX_URL and PIP_FIND_LINKS
28
+ env = os.environ.copy()
29
+ env["PIP_EXTRA_INDEX_URL"] = "https://pypi.ngc.nvidia.com https://download.pytorch.org/whl/cu121"
30
+ env["PIP_FIND_LINKS"] = "https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.5.1_cu121.html"
31
 
32
+ # Upgrade pip first
33
+ subprocess.run([sys.executable, "-m", "pip", "install", "--upgrade", "pip"], env=env, check=True)
34
+
35
+ # 3. Install Dependencies via setup.py extras
36
+ # Step A: Install [dev]
37
+ print("Installing [dev] dependencies...")
38
+ subprocess.run([sys.executable, "-m", "pip", "install", "-e", ".[dev]"], env=env, check=True)
39
+
40
+ # Step B: Install [p3d] - The 2-step approach mentioned in instructions
41
+ print("Installing [p3d] dependencies...")
42
+ subprocess.run([sys.executable, "-m", "pip", "install", "-e", ".[p3d]"], env=env, check=True)
43
+
44
+ # Step C: Install [inference]
45
+ print("Installing [inference] dependencies...")
46
+ subprocess.run([sys.executable, "-m", "pip", "install", "-e", ".[inference]"], env=env, check=True)
47
+
48
+ # 4. Apply Patches
49
+ # Run ./patching/hydra
50
+ patch_script = os.path.join(REPO_DIR, "patching", "hydra")
51
+ if os.path.exists(patch_script):
52
+ print("Applying Hydra patch...")
53
+ subprocess.run(["chmod", "+x", patch_script], check=True)
54
+ subprocess.run([patch_script], check=True)
55
+ else:
56
+ print(f"Warning: Patch script not found at {patch_script}")
57
 
58
  # Run installation
 
59
  install_dependencies()
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  # Add repo to Python path
62
  if REPO_DIR not in sys.path:
63
  sys.path.insert(0, REPO_DIR)
64
 
65
+ # Set environment variables required for runtime
66
  os.environ["CUDA_HOME"] = "/usr/local/cuda"
67
  os.environ["LIDRA_SKIP_INIT"] = "true"
68
+ # Often required to prevent Pytorch3D checks in certain container environments
69
+ os.environ["PYTORCH3D_NO_CUDA_CHECK"] = "1"
70
 
71
+ # ============ Imports ============
72
  import spaces
73
+ import builtins
74
  from typing import Optional, List, Callable
75
+ from copy import deepcopy
76
+ import gradio as gr
77
  import numpy as np
78
  from PIL import Image
 
 
79
  import torch
80
  import math
81
+ from omegaconf import OmegaConf, DictConfig, ListConfig
82
+ from hydra.utils import instantiate, get_method
 
 
83
 
84
+ # Lazy imports placehoder
85
  _sam3d_imported = False
86
  _pipeline = None
87
 
88
+ # ============ Security / Config Filters ============
89
  WHITELIST_FILTERS = [
90
  lambda target: target.split(".", 1)[0] in {"sam3d_objects", "torch", "torchvision", "moge"},
91
  ]
 
121
  elif isinstance(node, ListConfig):
122
  to_check.extend(list(node))
123
 
124
+ # ============ Lazy Loading & Model Logic ============
125
  def lazy_import_sam3d():
126
  """Import sam3d modules lazily after GPU is available."""
127
  global _sam3d_imported
 
129
  global utils3d, sam3d_objects, InferencePipelinePointMap, render_utils, SceneVisualizer
130
  global quaternion_multiply, quaternion_invert
131
 
132
+ try:
133
+ import utils3d as _utils3d
134
+ utils3d = _utils3d
135
+
136
+ import sam3d_objects as _sam3d_objects
137
+ sam3d_objects = _sam3d_objects
138
+
139
+ from sam3d_objects.pipeline.inference_pipeline_pointmap import InferencePipelinePointMap as _IPP
140
+ InferencePipelinePointMap = _IPP
141
+
142
+ from sam3d_objects.model.backbone.tdfy_dit.utils import render_utils as _ru
143
+ render_utils = _ru
144
+
145
+ from sam3d_objects.utils.visualization import SceneVisualizer as _SV
146
+ SceneVisualizer = _SV
147
+
148
+ from pytorch3d.transforms import quaternion_multiply as _qm, quaternion_invert as _qi
149
+ quaternion_multiply, quaternion_invert = _qm, _qi
150
+
151
+ _sam3d_imported = True
152
+ except ImportError as e:
153
+ print(f"Failed to import SAM 3D modules: {e}")
154
+ print("Ensure the installation step completed successfully.")
155
+ raise
156
 
157
  def load_pipeline(config_file: str):
158
  """Load the inference pipeline (call inside GPU context)."""
 
176
  def run_inference(image: np.ndarray, mask: np.ndarray, config_file: str, seed: Optional[int] = None, pointmap=None) -> dict:
177
  """GPU-decorated inference function for ZeroGPU."""
178
  global _pipeline
179
+
180
+ # Ensure pipeline is loaded
181
  _pipeline = load_pipeline(config_file)
182
  if hasattr(_pipeline, 'to'):
183
  _pipeline.to('cuda')
184
 
185
  rgba_image = merge_mask_to_rgba(image, mask)
186
+
187
  return _pipeline.run(
188
  rgba_image, None, seed,
189
  stage1_only=False,
 
195
  pointmap=pointmap,
196
  )
197
 
198
+ # ============ Rendering Helpers ============
199
+ # (Retained from original script logic for rendering frames)
200
  def _yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitchs, rs, fovs):
201
  lazy_import_sam3d()
202
  is_list = isinstance(yaws, list)
 
233
  extr, intr = _yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitch, r, fov)
234
  return render_utils.render_frames(sample, extr, intr, {"resolution": resolution, "bg_color": bg_color, "backend": "gsplat"}, **kwargs)
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  # ============ Gradio Interface ============
237
  CONFIG_FILE = os.path.join(REPO_DIR, "configs/inference.yaml")
238
 
 
254
  return ply_path, "✅ Inference complete!"
255
  return None, "⚠️ No 3D output generated"
256
  except Exception as e:
257
+ import traceback
258
+ traceback.print_exc()
259
  return None, f"❌ Error: {str(e)}"
260
 
261
  with gr.Blocks(title="SAM 3D Objects", theme=gr.themes.Soft()) as demo: