Spaces:
Paused
Paused
update
Browse files- InfiniDepth/utils/hf_demo_utils.py +17 -1
- 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
|