ritianyu commited on
Commit
4b6a4d3
·
1 Parent(s): 13ac8c9
Files changed (2) hide show
  1. __pycache__/app.cpython-310.pyc +0 -0
  2. app.py +151 -77
__pycache__/app.cpython-310.pyc CHANGED
Binary files a/__pycache__/app.cpython-310.pyc and b/__pycache__/app.cpython-310.pyc differ
 
app.py CHANGED
@@ -1,5 +1,9 @@
1
  import os
 
 
2
  import traceback
 
 
3
  from typing import Optional
4
 
5
  # Must be imported before modules that can import torch/cuda.
@@ -17,12 +21,49 @@ except ImportError:
17
 
18
  import gradio as gr
19
  import numpy as np
20
- import plotly.graph_objects as go
21
 
22
  from InfiniDepth.utils.hf_demo_utils import ModelCache, run_single_image_demo
23
 
 
 
 
 
 
24
 
25
  MODEL_CACHE = ModelCache()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
 
28
  def _none_if_invalid(value: Optional[float]) -> Optional[float]:
@@ -35,28 +76,27 @@ def _none_if_invalid(value: Optional[float]) -> Optional[float]:
35
  return None
36
 
37
 
38
- def _build_point_cloud_figure(xyz: np.ndarray, rgb: np.ndarray) -> go.Figure:
39
- fig = go.Figure(
40
- data=[
41
- go.Scatter3d(
42
- x=xyz[:, 0],
43
- y=xyz[:, 1],
44
- z=xyz[:, 2],
45
- mode="markers",
46
- marker={
47
- "size": 1.5,
48
- "color": (rgb * 255.0).clip(0, 255).astype(np.uint8),
49
- "opacity": 0.9,
50
- },
51
- )
52
- ]
53
- )
54
- fig.update_layout(
55
- scene={"aspectmode": "data"},
56
- margin={"l": 0, "r": 0, "t": 30, "b": 0},
57
- title="Point Cloud Preview",
58
- )
59
- return fig
60
 
61
 
62
  @spaces.GPU(duration=180)
@@ -72,8 +112,12 @@ def run_demo(
72
  fy_org: Optional[float],
73
  cx_org: Optional[float],
74
  cy_org: Optional[float],
 
75
  ):
76
  try:
 
 
 
77
  depth_path = None
78
  if depth_file is not None:
79
  depth_path = depth_file if isinstance(depth_file, str) else depth_file.name
@@ -92,71 +136,81 @@ def run_demo(
92
  cy_org=_none_if_invalid(cy_org),
93
  model_cache=MODEL_CACHE,
94
  )
95
- fig = _build_point_cloud_figure(result.xyz, result.rgb)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  h, w = [int(v) for v in input_size.split("x")]
97
  status = (
98
- f"Done. Output depth resolution: {h * int(upsample_ratio)}x{w * int(upsample_ratio)}. "
99
- f"Generated {result.xyz.shape[0]} preview points."
100
  )
101
- return result.depth_vis, fig, result.ply_path, status
102
  except Exception as exc:
103
  traceback.print_exc()
104
- return None, None, None, f"Error: {exc}"
105
-
106
-
107
- DESCRIPTION = """
108
- InfiniDepth Hugging Face Demo
109
 
110
- - Input: RGB image (required), depth map (optional)
111
- - Model: InfiniDepth / InfiniDepth_DC
112
- - Outputs: depth map visualization + 3D point cloud preview + downloadable PLY
113
 
114
- Note:
115
- - GPU is required.
116
- - In this demo, InfiniDepth_DC requires a depth map input.
 
 
117
  """
118
 
119
 
120
- with gr.Blocks(title="InfiniDepth Demo") as demo:
121
- gr.Markdown(DESCRIPTION)
122
 
123
  with gr.Row():
124
- with gr.Column():
125
  image_input = gr.Image(type="numpy", label="Input RGB Image")
126
  depth_input = gr.File(
127
  label="Optional Depth Map (.png/.npy/.npz/.h5/.hdf5/.exr)",
128
  file_types=[".png", ".npy", ".npz", ".h5", ".hdf5", ".exr"],
129
  )
130
 
131
- model_type = gr.Dropdown(
132
- choices=["InfiniDepth", "InfiniDepth_DC"],
133
- value="InfiniDepth",
134
- label="Model Type",
135
- )
136
- geometry_type = gr.Dropdown(
137
- choices=["disparity", "depth"],
138
- value="disparity",
139
- label="Geometry Type",
140
- )
141
- input_size = gr.Dropdown(
142
- choices=["504x672", "768x1024"],
143
- value="768x1024",
144
- label="Inference Resolution (HxW)",
145
- )
146
- upsample_ratio = gr.Slider(
147
- minimum=1,
148
- maximum=8,
149
- value=1,
150
- step=1,
151
- label="Super-resolution Ratio",
152
- )
153
- max_points_preview = gr.Slider(
154
- minimum=5000,
155
- maximum=120000,
156
- value=60000,
157
- step=5000,
158
- label="Max Preview Points",
159
- )
 
160
 
161
  with gr.Accordion("Camera Intrinsics (Optional)", open=False):
162
  fx_org = gr.Number(label="fx", value=None)
@@ -164,15 +218,35 @@ with gr.Blocks(title="InfiniDepth Demo") as demo:
164
  cx_org = gr.Number(label="cx", value=None)
165
  cy_org = gr.Number(label="cy", value=None)
166
 
167
- run_button = gr.Button("Run", variant="primary")
 
 
 
 
168
 
169
- with gr.Column():
170
- depth_output = gr.Image(type="numpy", label="Predicted Depth (Colorized)")
171
- pcd_plot = gr.Plot(label="3D Point Cloud")
172
- pcd_file = gr.File(label="Download Point Cloud (.ply)")
173
- status = gr.Textbox(label="Status")
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  run_button.click(
 
 
 
176
  fn=run_demo,
177
  inputs=[
178
  image_input,
@@ -187,7 +261,7 @@ with gr.Blocks(title="InfiniDepth Demo") as demo:
187
  cx_org,
188
  cy_org,
189
  ],
190
- outputs=[depth_output, pcd_plot, pcd_file, status],
191
  )
192
 
193
  demo = demo.queue()
 
1
  import os
2
+ import shutil
3
+ import tempfile
4
  import traceback
5
+ import uuid
6
+ from pathlib import Path
7
  from typing import Optional
8
 
9
  # Must be imported before modules that can import torch/cuda.
 
21
 
22
  import gradio as gr
23
  import numpy as np
24
+ from PIL import Image
25
 
26
  from InfiniDepth.utils.hf_demo_utils import ModelCache, run_single_image_demo
27
 
28
+ try:
29
+ import trimesh
30
+ except ImportError:
31
+ trimesh = None
32
+
33
 
34
  MODEL_CACHE = ModelCache()
35
+ OUTPUT_ROOT = Path(tempfile.gettempdir()) / "infinidepth_hf_demo"
36
+
37
+ CUSTOM_CSS = """
38
+ .gradio-container {
39
+ max-width: 1280px !important;
40
+ }
41
+
42
+ #hero {
43
+ border: 1px solid #d7e3ef;
44
+ border-radius: 18px;
45
+ padding: 20px 22px;
46
+ margin-bottom: 18px;
47
+ background: linear-gradient(140deg, #f7fbff 0%, #ecf8f7 100%);
48
+ }
49
+
50
+ #hero h1 {
51
+ margin: 0;
52
+ font-size: 30px;
53
+ font-weight: 700;
54
+ letter-spacing: -0.02em;
55
+ color: #0f172a;
56
+ }
57
+
58
+ #hero p {
59
+ margin: 10px 0 0;
60
+ color: #334155;
61
+ }
62
+
63
+ #run-btn {
64
+ min-height: 48px;
65
+ }
66
+ """
67
 
68
 
69
  def _none_if_invalid(value: Optional[float]) -> Optional[float]:
 
76
  return None
77
 
78
 
79
+ def _prepare_output_dir(request: Optional[gr.Request]) -> Path:
80
+ session_hash = "local"
81
+ if request is not None and getattr(request, "session_hash", None):
82
+ session_hash = str(request.session_hash)
83
+ session_dir = OUTPUT_ROOT / session_hash
84
+ session_dir.mkdir(parents=True, exist_ok=True)
85
+ output_dir = session_dir / uuid.uuid4().hex
86
+ output_dir.mkdir(parents=True, exist_ok=True)
87
+ return output_dir
88
+
89
+
90
+ def _export_glb_from_points(xyz: np.ndarray, rgb: np.ndarray, output_path: Path) -> None:
91
+ if trimesh is None:
92
+ raise RuntimeError("`trimesh` is required to export .glb for the 3D viewer")
93
+ if xyz.size == 0:
94
+ raise ValueError("Point cloud is empty")
95
+
96
+ vertices = xyz.astype(np.float32) * np.array([1.0, -1.0, -1.0], dtype=np.float32)
97
+ colors = (rgb.clip(0.0, 1.0) * 255.0).astype(np.uint8)
98
+ cloud = trimesh.PointCloud(vertices=vertices, colors=colors)
99
+ cloud.export(output_path.as_posix())
 
100
 
101
 
102
  @spaces.GPU(duration=180)
 
112
  fy_org: Optional[float],
113
  cx_org: Optional[float],
114
  cy_org: Optional[float],
115
+ request: gr.Request,
116
  ):
117
  try:
118
+ if image is None:
119
+ raise ValueError("Input RGB image is required")
120
+
121
  depth_path = None
122
  if depth_file is not None:
123
  depth_path = depth_file if isinstance(depth_file, str) else depth_file.name
 
136
  cy_org=_none_if_invalid(cy_org),
137
  model_cache=MODEL_CACHE,
138
  )
139
+ output_dir = _prepare_output_dir(request)
140
+ glb_path = output_dir / "pointcloud.glb"
141
+ ply_path = output_dir / "pointcloud.ply"
142
+ depth_vis_path = output_dir / "depth_colorized.png"
143
+
144
+ _export_glb_from_points(result.xyz, result.rgb, glb_path)
145
+ if os.path.exists(result.ply_path):
146
+ shutil.copy2(result.ply_path, ply_path)
147
+ depth_vis_uint8 = result.depth_vis if result.depth_vis.dtype == np.uint8 else result.depth_vis.astype(np.uint8)
148
+ Image.fromarray(depth_vis_uint8).save(depth_vis_path)
149
+
150
+ download_files = [glb_path.as_posix(), depth_vis_path.as_posix()]
151
+ if ply_path.exists():
152
+ download_files.append(ply_path.as_posix())
153
+
154
  h, w = [int(v) for v in input_size.split("x")]
155
  status = (
156
+ f"Done. Depth resolution: {h * int(upsample_ratio)}x{w * int(upsample_ratio)}. "
157
+ f"Preview points: {result.xyz.shape[0]}."
158
  )
159
+ return result.depth_vis, glb_path.as_posix(), download_files, status
160
  except Exception as exc:
161
  traceback.print_exc()
162
+ return None, None, [], f"Error: {exc}"
 
 
 
 
163
 
 
 
 
164
 
165
+ DESCRIPTION_MD = """
166
+ <div id="hero">
167
+ <h1>InfiniDepth Demo</h1>
168
+ <p>High-quality monocular depth + interactive 3D point cloud preview for Hugging Face Spaces.</p>
169
+ </div>
170
  """
171
 
172
 
173
+ with gr.Blocks(title="InfiniDepth Demo", theme=gr.themes.Soft(), css=CUSTOM_CSS) as demo:
174
+ gr.Markdown(DESCRIPTION_MD)
175
 
176
  with gr.Row():
177
+ with gr.Column(scale=5):
178
  image_input = gr.Image(type="numpy", label="Input RGB Image")
179
  depth_input = gr.File(
180
  label="Optional Depth Map (.png/.npy/.npz/.h5/.hdf5/.exr)",
181
  file_types=[".png", ".npy", ".npz", ".h5", ".hdf5", ".exr"],
182
  )
183
 
184
+ with gr.Accordion("Inference Settings", open=True):
185
+ model_type = gr.Dropdown(
186
+ choices=["InfiniDepth", "InfiniDepth_DC"],
187
+ value="InfiniDepth",
188
+ label="Model Type",
189
+ )
190
+ geometry_type = gr.Dropdown(
191
+ choices=["disparity", "depth"],
192
+ value="disparity",
193
+ label="Geometry Type",
194
+ )
195
+ input_size = gr.Dropdown(
196
+ choices=["504x672", "768x1024"],
197
+ value="768x1024",
198
+ label="Inference Resolution (HxW)",
199
+ )
200
+ upsample_ratio = gr.Slider(
201
+ minimum=1,
202
+ maximum=8,
203
+ value=1,
204
+ step=1,
205
+ label="Super-resolution Ratio",
206
+ )
207
+ max_points_preview = gr.Slider(
208
+ minimum=5000,
209
+ maximum=120000,
210
+ value=60000,
211
+ step=5000,
212
+ label="Max Preview Points",
213
+ )
214
 
215
  with gr.Accordion("Camera Intrinsics (Optional)", open=False):
216
  fx_org = gr.Number(label="fx", value=None)
 
218
  cx_org = gr.Number(label="cx", value=None)
219
  cy_org = gr.Number(label="cy", value=None)
220
 
221
+ run_button = gr.Button("Generate Depth + 3D", variant="primary", elem_id="run-btn")
222
+ gr.Markdown(
223
+ "Tips: `InfiniDepth_DC` requires a depth map input. "
224
+ "Use lower preview points for faster 3D interaction."
225
+ )
226
 
227
+ with gr.Column(scale=7):
228
+ with gr.Tabs():
229
+ with gr.Tab("3D View"):
230
+ pcd_viewer = gr.Model3D(
231
+ label="Point Cloud Viewer",
232
+ display_mode="solid",
233
+ clear_color=[1, 1, 1, 1],
234
+ height=560,
235
+ )
236
+ with gr.Tab("Depth"):
237
+ depth_output = gr.Image(type="numpy", label="Predicted Depth (Colorized)")
238
+ with gr.Tab("Download"):
239
+ files_output = gr.File(
240
+ label="Artifacts",
241
+ type="filepath",
242
+ file_count="multiple",
243
+ )
244
+ status = gr.Textbox(label="Status", interactive=False)
245
 
246
  run_button.click(
247
+ fn=lambda: (None, None, [], "Running..."),
248
+ outputs=[depth_output, pcd_viewer, files_output, status],
249
+ ).then(
250
  fn=run_demo,
251
  inputs=[
252
  image_input,
 
261
  cx_org,
262
  cy_org,
263
  ],
264
+ outputs=[depth_output, pcd_viewer, files_output, status],
265
  )
266
 
267
  demo = demo.queue()