ritianyu commited on
Commit
9d7e197
·
1 Parent(s): 2dc583e
Files changed (2) hide show
  1. InfiniDepth/utils/hf_demo_utils.py +17 -1
  2. app.py +32 -0
InfiniDepth/utils/hf_demo_utils.py CHANGED
@@ -2,7 +2,7 @@ import os
2
  import tempfile
3
  from dataclasses import dataclass
4
  from pathlib import Path
5
- from typing import Any, Optional
6
 
7
  import cv2
8
  import numpy as np
@@ -129,6 +129,11 @@ def prepare_runtime_assets() -> None:
129
  resolve_moge2_pretrained()
130
 
131
 
 
 
 
 
 
132
  @dataclass
133
  class DemoResult:
134
  depth_vis: np.ndarray
@@ -310,8 +315,10 @@ def run_single_image_demo(
310
  cx_org: Optional[float] = None,
311
  cy_org: Optional[float] = None,
312
  model_cache: Optional[ModelCache] = None,
 
313
  ) -> DemoResult:
314
  image_shape = tuple(int(dim) for dim in image_np.shape) if image_np is not None else None
 
315
  Log.info(
316
  "run_single_image_demo start: "
317
  f"model_type={model_type}, input_size={input_size_text}, output_resolution_mode={output_resolution_mode}, "
@@ -323,6 +330,7 @@ def run_single_image_demo(
323
  "No CUDA GPU is available. If using Hugging Face ZeroGPU, "
324
  "decorate the Gradio inference function with @spaces.GPU and enable queue()."
325
  )
 
326
 
327
  input_size = _parse_image_size(input_size_text)
328
  if upsample_ratio < 1 or upsample_ratio > 8:
@@ -334,6 +342,7 @@ def run_single_image_demo(
334
 
335
  device = torch.device("cuda")
336
  image, org_h, org_w = _prepare_image_tensor(image_np, input_size, device)
 
337
  h_in, w_in = input_size
338
  h_out, w_out = resolve_output_size_from_mode(
339
  output_resolution_mode=output_resolution_mode,
@@ -354,16 +363,20 @@ def run_single_image_demo(
354
  image=image,
355
  device=device,
356
  )
 
357
  Log.info(f"Depth source resolved: {depth_source_label}")
358
  gt = depth_to_disparity(gt_depth)
359
  prompt = depth_to_disparity(prompt_depth)
360
  prompt_mask = prompt > 0
361
 
362
  ckpt_path = resolve_checkpoint_path(model_type)
 
363
  model_cache = model_cache or ModelCache()
364
  model = model_cache.get(model_type=model_type, model_path=ckpt_path)
 
365
 
366
  query_2d_uniform_coord = make_2d_uniform_coord((h_out, w_out)).unsqueeze(0).to(device)
 
367
  pred_depth, _ = model.inference(
368
  image=image,
369
  query_coord=query_2d_uniform_coord,
@@ -372,10 +385,12 @@ def run_single_image_demo(
372
  prompt_depth=prompt,
373
  prompt_mask=prompt_mask,
374
  )
 
375
  Log.info(f"Model inference completed: output_size={h_out}x{w_out}")
376
 
377
  pred_depthmap = pred_depth.permute(0, 2, 1).reshape(1, 1, h_out, w_out)
378
  depth_vis = _colorize_predicted_depth(pred_depthmap)
 
379
 
380
  fx, fy, cx, cy = resolve_camera_intrinsics(fx_org, fy_org, cx_org, cy_org, org_h, org_w)
381
  fx_out, fy_out, cx_out, cy_out, _ = build_scaled_intrinsics_matrix(
@@ -406,6 +421,7 @@ def run_single_image_demo(
406
  output_path=ply_path,
407
  max_points_preview=int(max_points_preview),
408
  )
 
409
  Log.info(
410
  f"Artifacts saved: depth_npy_path={depth_npy_path}, ply_path={ply_path}, preview_points={xyz.shape[0]}"
411
  )
 
2
  import tempfile
3
  from dataclasses import dataclass
4
  from pathlib import Path
5
+ from typing import Any, Callable, Optional
6
 
7
  import cv2
8
  import numpy as np
 
129
  resolve_moge2_pretrained()
130
 
131
 
132
+ def _report_stage(stage_callback: Optional[Callable[[str], None]], stage: str) -> None:
133
+ if stage_callback is not None:
134
+ stage_callback(stage)
135
+
136
+
137
  @dataclass
138
  class DemoResult:
139
  depth_vis: np.ndarray
 
315
  cx_org: Optional[float] = None,
316
  cy_org: Optional[float] = None,
317
  model_cache: Optional[ModelCache] = None,
318
+ stage_callback: Optional[Callable[[str], None]] = None,
319
  ) -> DemoResult:
320
  image_shape = tuple(int(dim) for dim in image_np.shape) if image_np is not None else None
321
+ _report_stage(stage_callback, "demo:start")
322
  Log.info(
323
  "run_single_image_demo start: "
324
  f"model_type={model_type}, input_size={input_size_text}, output_resolution_mode={output_resolution_mode}, "
 
330
  "No CUDA GPU is available. If using Hugging Face ZeroGPU, "
331
  "decorate the Gradio inference function with @spaces.GPU and enable queue()."
332
  )
333
+ _report_stage(stage_callback, "demo:cuda_ready")
334
 
335
  input_size = _parse_image_size(input_size_text)
336
  if upsample_ratio < 1 or upsample_ratio > 8:
 
342
 
343
  device = torch.device("cuda")
344
  image, org_h, org_w = _prepare_image_tensor(image_np, input_size, device)
345
+ _report_stage(stage_callback, "demo:image_prepared")
346
  h_in, w_in = input_size
347
  h_out, w_out = resolve_output_size_from_mode(
348
  output_resolution_mode=output_resolution_mode,
 
363
  image=image,
364
  device=device,
365
  )
366
+ _report_stage(stage_callback, f"demo:depth_inputs_ready source={depth_source_label}")
367
  Log.info(f"Depth source resolved: {depth_source_label}")
368
  gt = depth_to_disparity(gt_depth)
369
  prompt = depth_to_disparity(prompt_depth)
370
  prompt_mask = prompt > 0
371
 
372
  ckpt_path = resolve_checkpoint_path(model_type)
373
+ _report_stage(stage_callback, f"demo:checkpoint_resolved path={ckpt_path}")
374
  model_cache = model_cache or ModelCache()
375
  model = model_cache.get(model_type=model_type, model_path=ckpt_path)
376
+ _report_stage(stage_callback, "demo:model_loaded")
377
 
378
  query_2d_uniform_coord = make_2d_uniform_coord((h_out, w_out)).unsqueeze(0).to(device)
379
+ _report_stage(stage_callback, "demo:inference_started")
380
  pred_depth, _ = model.inference(
381
  image=image,
382
  query_coord=query_2d_uniform_coord,
 
385
  prompt_depth=prompt,
386
  prompt_mask=prompt_mask,
387
  )
388
+ _report_stage(stage_callback, "demo:inference_finished")
389
  Log.info(f"Model inference completed: output_size={h_out}x{w_out}")
390
 
391
  pred_depthmap = pred_depth.permute(0, 2, 1).reshape(1, 1, h_out, w_out)
392
  depth_vis = _colorize_predicted_depth(pred_depthmap)
393
+ _report_stage(stage_callback, "demo:depth_colorized")
394
 
395
  fx, fy, cx, cy = resolve_camera_intrinsics(fx_org, fy_org, cx_org, cy_org, org_h, org_w)
396
  fx_out, fy_out, cx_out, cy_out, _ = build_scaled_intrinsics_matrix(
 
421
  output_path=ply_path,
422
  max_points_preview=int(max_points_preview),
423
  )
424
+ _report_stage(stage_callback, "demo:pointcloud_saved")
425
  Log.info(
426
  f"Artifacts saved: depth_npy_path={depth_npy_path}, ply_path={ply_path}, preview_points={xyz.shape[0]}"
427
  )
app.py CHANGED
@@ -34,6 +34,7 @@ except ImportError:
34
 
35
  MODEL_CACHE = ModelCache()
36
  OUTPUT_ROOT = Path(tempfile.gettempdir()) / "infinidepth_hf_demo"
 
37
  EXAMPLE_DATA_ROOT = Path(__file__).resolve().parent / "example_data"
38
  EXAMPLE_IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp"}
39
  EXAMPLE_DEPTH_EXTENSIONS = {".png", ".npy", ".npz", ".h5", ".hdf5", ".exr"}
@@ -202,6 +203,22 @@ def _prepare_output_dir() -> Path:
202
  return output_dir
203
 
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  def _export_glb_from_points(xyz: np.ndarray, rgb: np.ndarray, output_path: Path) -> None:
206
  if trimesh is None:
207
  raise RuntimeError("`trimesh` is required to export .glb for the 3D viewer")
@@ -226,7 +243,9 @@ def _run_demo_impl(
226
  fy_org: Optional[float],
227
  cx_org: Optional[float],
228
  cy_org: Optional[float],
 
229
  ):
 
230
  if image is None:
231
  raise ValueError("Input RGB image is required")
232
 
@@ -234,6 +253,7 @@ def _run_demo_impl(
234
  if depth_file is not None:
235
  depth_path = depth_file if isinstance(depth_file, str) else depth_file.name
236
 
 
237
  result = run_single_image_demo(
238
  image_np=image,
239
  depth_path=depth_path,
@@ -247,12 +267,15 @@ def _run_demo_impl(
247
  cx_org=_none_if_invalid(cx_org),
248
  cy_org=_none_if_invalid(cy_org),
249
  model_cache=MODEL_CACHE,
 
250
  )
 
251
  output_dir = _prepare_output_dir()
252
  glb_path = output_dir / "pointcloud.glb"
253
  ply_path = output_dir / "pointcloud.ply"
254
  depth_vis_path = output_dir / "depth_colorized.png"
255
 
 
256
  _export_glb_from_points(result.xyz, result.rgb, glb_path)
257
  if os.path.exists(result.ply_path):
258
  shutil.copy2(result.ply_path, ply_path)
@@ -275,6 +298,7 @@ def _run_demo_impl(
275
  f"Preview points: {result.xyz.shape[0]}. "
276
  f"Camera intrinsics: {'default' if result.used_default_intrinsics else 'custom'}."
277
  )
 
278
  return result.depth_vis, glb_path.as_posix(), download_files, status
279
 
280
 
@@ -291,6 +315,7 @@ def run_demo_gpu(
291
  fy_org: Optional[float],
292
  cx_org: Optional[float],
293
  cy_org: Optional[float],
 
294
  ):
295
  return _run_demo_impl(
296
  image=image,
@@ -304,6 +329,7 @@ def run_demo_gpu(
304
  fy_org=fy_org,
305
  cx_org=cx_org,
306
  cy_org=cy_org,
 
307
  )
308
 
309
 
@@ -321,10 +347,12 @@ def run_demo(
321
  cy_org: Optional[float],
322
  ):
323
  request_id = uuid.uuid4().hex[:8]
 
324
  depth_path = None
325
  if depth_file is not None:
326
  depth_path = depth_file if isinstance(depth_file, str) else depth_file.name
327
  image_shape = tuple(int(dim) for dim in image.shape) if image is not None else None
 
328
  Log.info(
329
  f"[{request_id}] run_demo start: model_type={model_type}, "
330
  f"input_size={input_size}, output_resolution_mode={output_resolution_mode}, "
@@ -344,14 +372,18 @@ def run_demo(
344
  fy_org=fy_org,
345
  cx_org=cx_org,
346
  cy_org=cy_org,
 
347
  )
348
  status_text = result[3] if isinstance(result, tuple) and len(result) == 4 else "Done."
349
  Log.info(f"[{request_id}] run_demo success: {status_text}")
350
  return result
351
  except Exception as exc:
352
  error_trace = traceback.format_exc()
 
353
  Log.exception(f"[{request_id}] run_demo failed")
354
  error_message = f"Error [{request_id}]: {exc}"
 
 
355
  if os.getenv("INFINIDEPTH_SHOW_TRACEBACK", "0") == "1":
356
  error_message = f"{error_message}\n\n{error_trace}"
357
  return None, None, [], error_message
 
34
 
35
  MODEL_CACHE = ModelCache()
36
  OUTPUT_ROOT = Path(tempfile.gettempdir()) / "infinidepth_hf_demo"
37
+ TRACE_ROOT = OUTPUT_ROOT / "trace"
38
  EXAMPLE_DATA_ROOT = Path(__file__).resolve().parent / "example_data"
39
  EXAMPLE_IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp"}
40
  EXAMPLE_DEPTH_EXTENSIONS = {".png", ".npy", ".npz", ".h5", ".hdf5", ".exr"}
 
203
  return output_dir
204
 
205
 
206
+ def _append_trace(trace_path: str, stage: str) -> None:
207
+ trace_file = Path(trace_path)
208
+ trace_file.parent.mkdir(parents=True, exist_ok=True)
209
+ with trace_file.open("a", encoding="utf-8") as handle:
210
+ handle.write(f"{stage}\n")
211
+ handle.flush()
212
+
213
+
214
+ def _read_trace(trace_path: str, max_lines: int = 12) -> str:
215
+ trace_file = Path(trace_path)
216
+ if not trace_file.exists():
217
+ return ""
218
+ lines = trace_file.read_text(encoding="utf-8").splitlines()
219
+ return "\n".join(lines[-max_lines:])
220
+
221
+
222
  def _export_glb_from_points(xyz: np.ndarray, rgb: np.ndarray, output_path: Path) -> None:
223
  if trimesh is None:
224
  raise RuntimeError("`trimesh` is required to export .glb for the 3D viewer")
 
243
  fy_org: Optional[float],
244
  cx_org: Optional[float],
245
  cy_org: Optional[float],
246
+ trace_path: str,
247
  ):
248
+ _append_trace(trace_path, "worker:entered _run_demo_impl")
249
  if image is None:
250
  raise ValueError("Input RGB image is required")
251
 
 
253
  if depth_file is not None:
254
  depth_path = depth_file if isinstance(depth_file, str) else depth_file.name
255
 
256
+ _append_trace(trace_path, f"worker:run_single_image_demo depth_path={depth_path}")
257
  result = run_single_image_demo(
258
  image_np=image,
259
  depth_path=depth_path,
 
267
  cx_org=_none_if_invalid(cx_org),
268
  cy_org=_none_if_invalid(cy_org),
269
  model_cache=MODEL_CACHE,
270
+ stage_callback=lambda stage: _append_trace(trace_path, stage),
271
  )
272
+ _append_trace(trace_path, "worker:prepare_output_dir")
273
  output_dir = _prepare_output_dir()
274
  glb_path = output_dir / "pointcloud.glb"
275
  ply_path = output_dir / "pointcloud.ply"
276
  depth_vis_path = output_dir / "depth_colorized.png"
277
 
278
+ _append_trace(trace_path, "worker:export_glb")
279
  _export_glb_from_points(result.xyz, result.rgb, glb_path)
280
  if os.path.exists(result.ply_path):
281
  shutil.copy2(result.ply_path, ply_path)
 
298
  f"Preview points: {result.xyz.shape[0]}. "
299
  f"Camera intrinsics: {'default' if result.used_default_intrinsics else 'custom'}."
300
  )
301
+ _append_trace(trace_path, "worker:completed")
302
  return result.depth_vis, glb_path.as_posix(), download_files, status
303
 
304
 
 
315
  fy_org: Optional[float],
316
  cx_org: Optional[float],
317
  cy_org: Optional[float],
318
+ trace_path: str,
319
  ):
320
  return _run_demo_impl(
321
  image=image,
 
329
  fy_org=fy_org,
330
  cx_org=cx_org,
331
  cy_org=cy_org,
332
+ trace_path=trace_path,
333
  )
334
 
335
 
 
347
  cy_org: Optional[float],
348
  ):
349
  request_id = uuid.uuid4().hex[:8]
350
+ trace_path = (TRACE_ROOT / f"{request_id}.log").as_posix()
351
  depth_path = None
352
  if depth_file is not None:
353
  depth_path = depth_file if isinstance(depth_file, str) else depth_file.name
354
  image_shape = tuple(int(dim) for dim in image.shape) if image is not None else None
355
+ _append_trace(trace_path, "ui:entered run_demo")
356
  Log.info(
357
  f"[{request_id}] run_demo start: model_type={model_type}, "
358
  f"input_size={input_size}, output_resolution_mode={output_resolution_mode}, "
 
372
  fy_org=fy_org,
373
  cx_org=cx_org,
374
  cy_org=cy_org,
375
+ trace_path=trace_path,
376
  )
377
  status_text = result[3] if isinstance(result, tuple) and len(result) == 4 else "Done."
378
  Log.info(f"[{request_id}] run_demo success: {status_text}")
379
  return result
380
  except Exception as exc:
381
  error_trace = traceback.format_exc()
382
+ trace_summary = _read_trace(trace_path)
383
  Log.exception(f"[{request_id}] run_demo failed")
384
  error_message = f"Error [{request_id}]: {exc}"
385
+ if trace_summary:
386
+ error_message = f"{error_message}\n\nLast worker stages:\n{trace_summary}"
387
  if os.getenv("INFINIDEPTH_SHOW_TRACEBACK", "0") == "1":
388
  error_message = f"{error_message}\n\n{error_trace}"
389
  return None, None, [], error_message