ritianyu commited on
Commit
721427d
·
1 Parent(s): 3a76675

fix bug for output

Browse files
InfiniDepth/utils/hf_demo_utils.py CHANGED
@@ -7,6 +7,7 @@ import os
7
  import re
8
  import shutil
9
  import tempfile
 
10
  import uuid
11
 
12
  import cv2
@@ -20,6 +21,7 @@ from .vis_utils import colorize_depth_maps
20
 
21
  _DEPTH_EXTENSIONS = {".png", ".npz", ".npy", ".h5", ".hdf5", ".exr"}
22
  _IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp"}
 
23
 
24
 
25
  @dataclass(frozen=True)
@@ -98,9 +100,45 @@ def scan_example_cases(example_dir: str | Path) -> list[ExampleCase]:
98
  return cases
99
 
100
 
101
- def ensure_session_output_dir(app_name: str, session_hash: Optional[str]) -> Path:
102
- safe_hash = session_hash or f"session-{uuid.uuid4().hex[:10]}"
103
- output_dir = Path(tempfile.gettempdir()) / app_name / safe_hash
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  shutil.rmtree(output_dir, ignore_errors=True)
105
  output_dir.mkdir(parents=True, exist_ok=True)
106
  return output_dir
 
7
  import re
8
  import shutil
9
  import tempfile
10
+ import time
11
  import uuid
12
 
13
  import cv2
 
21
 
22
  _DEPTH_EXTENSIONS = {".png", ".npz", ".npy", ".h5", ".hdf5", ".exr"}
23
  _IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp"}
24
+ _SAFE_PATH_PART = re.compile(r"[^A-Za-z0-9._-]+")
25
 
26
 
27
  @dataclass(frozen=True)
 
100
  return cases
101
 
102
 
103
+ def _sanitize_path_part(value: str, fallback_prefix: str) -> str:
104
+ sanitized = _SAFE_PATH_PART.sub("-", value).strip(".-")
105
+ if sanitized:
106
+ return sanitized
107
+ return f"{fallback_prefix}-{uuid.uuid4().hex[:10]}"
108
+
109
+
110
+ def _prune_stale_run_dirs(session_dir: Path, max_age_seconds: int) -> None:
111
+ if max_age_seconds <= 0 or not session_dir.exists():
112
+ return
113
+
114
+ now = time.time()
115
+ for child in session_dir.iterdir():
116
+ if not child.is_dir():
117
+ continue
118
+ try:
119
+ age_seconds = now - child.stat().st_mtime
120
+ except FileNotFoundError:
121
+ continue
122
+ if age_seconds > max_age_seconds:
123
+ shutil.rmtree(child, ignore_errors=True)
124
+
125
+
126
+ def ensure_session_output_dir(
127
+ app_name: str,
128
+ session_hash: Optional[str],
129
+ run_id: Optional[str] = None,
130
+ max_age_seconds: int = 60 * 60,
131
+ ) -> Path:
132
+ safe_app_name = _sanitize_path_part(app_name, "app")
133
+ raw_hash = session_hash or f"session-{uuid.uuid4().hex[:10]}"
134
+ safe_hash = _sanitize_path_part(raw_hash, "session")
135
+ session_dir = Path(tempfile.gettempdir()) / safe_app_name / safe_hash
136
+ session_dir.mkdir(parents=True, exist_ok=True)
137
+ _prune_stale_run_dirs(session_dir, max_age_seconds=max_age_seconds)
138
+
139
+ raw_run_id = run_id or f"run-{time.strftime('%Y%m%d-%H%M%S')}-{uuid.uuid4().hex[:8]}"
140
+ safe_run_id = _sanitize_path_part(raw_run_id, "run")
141
+ output_dir = session_dir / safe_run_id
142
  shutil.rmtree(output_dir, ignore_errors=True)
143
  output_dir.mkdir(parents=True, exist_ok=True)
144
  return output_dir
InfiniDepth/utils/hf_gs_viewer.py CHANGED
@@ -132,17 +132,25 @@ def ensure_supersplat_viewer_assets() -> Path:
132
  return _copy_viewer_dist(dist_dir)
133
 
134
 
135
- def _gradio_file_url(path: str | Path) -> str:
136
- return "/gradio_api/file=" + quote(str(Path(path).resolve()), safe="/")
137
-
138
-
139
- def build_embedded_viewer_html(ply_path: str | Path, height_px: int = 700) -> str:
 
 
 
 
 
 
 
140
  viewer_dir = ensure_supersplat_viewer_assets()
141
- viewer_url = _gradio_file_url(viewer_dir / "index.html")
142
- splat_url = _gradio_file_url(ply_path)
143
  filename = Path(ply_path).name
 
144
  iframe_src = (
145
- f"{viewer_url}?load={quote(splat_url, safe='')}&filename={quote(filename, safe='')}"
146
  )
147
  escaped_src = html.escape(iframe_src, quote=True)
148
  return (
@@ -155,9 +163,9 @@ def build_embedded_viewer_html(ply_path: str | Path, height_px: int = 700) -> st
155
  )
156
 
157
 
158
- def build_viewer_error_html(message: str, ply_path: str | Path) -> str:
159
  escaped_message = html.escape(message)
160
- ply_url = html.escape(_gradio_file_url(ply_path), quote=True)
161
  return (
162
  '<div style="padding:1rem; border:1px solid #d0d7de; border-radius:12px; background:#fff;">'
163
  '<strong>Embedded SuperSplat viewer is unavailable.</strong>'
 
132
  return _copy_viewer_dist(dist_dir)
133
 
134
 
135
+ def _gradio_file_url(path: str | Path, cache_key: str | None = None) -> str:
136
+ url = "/gradio_api/file=" + quote(str(Path(path).resolve()), safe="/")
137
+ if cache_key:
138
+ url += f"?v={quote(cache_key, safe='')}"
139
+ return url
140
+
141
+
142
+ def build_embedded_viewer_html(
143
+ ply_path: str | Path,
144
+ height_px: int = 700,
145
+ cache_key: str | None = None,
146
+ ) -> str:
147
  viewer_dir = ensure_supersplat_viewer_assets()
148
+ viewer_url = _gradio_file_url(viewer_dir / "index.html", cache_key=cache_key)
149
+ splat_url = _gradio_file_url(ply_path, cache_key=cache_key)
150
  filename = Path(ply_path).name
151
+ query_join = "&" if "?" in viewer_url else "?"
152
  iframe_src = (
153
+ f"{viewer_url}{query_join}load={quote(splat_url, safe='')}&filename={quote(filename, safe='')}"
154
  )
155
  escaped_src = html.escape(iframe_src, quote=True)
156
  return (
 
163
  )
164
 
165
 
166
+ def build_viewer_error_html(message: str, ply_path: str | Path, cache_key: str | None = None) -> str:
167
  escaped_message = html.escape(message)
168
+ ply_url = html.escape(_gradio_file_url(ply_path, cache_key=cache_key), quote=True)
169
  return (
170
  '<div style="padding:1rem; border:1px solid #d0d7de; border-radius:12px; background:#fff;">'
171
  '<strong>Embedded SuperSplat viewer is unavailable.</strong>'
app.py CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
3
  from functools import lru_cache
4
  import os
5
  from pathlib import Path
 
6
  from typing import Optional
7
 
8
 
@@ -63,6 +64,7 @@ from InfiniDepth.utils.hf_gs_viewer import (
63
  APP_TEMP_ROOT as GS_VIEWER_ROOT,
64
  build_embedded_viewer_html,
65
  build_viewer_error_html,
 
66
  )
67
  from InfiniDepth.utils.inference_utils import (
68
  apply_sky_mask_to_depth,
@@ -217,7 +219,12 @@ def _resolve_moge2_source() -> str:
217
  def _preload_repo_assets() -> tuple[str, ...]:
218
  depth_paths = tuple(_resolve_depth_checkpoint(model_type) for model_type in MODEL_CHOICES)
219
  gs_paths = tuple(_resolve_gs_checkpoint(model_type) for model_type in MODEL_CHOICES)
220
- return depth_paths + gs_paths + (_resolve_moge2_source(), _resolve_skyseg_path())
 
 
 
 
 
221
 
222
 
223
  @lru_cache(maxsize=2)
@@ -291,9 +298,10 @@ def _format_gs_status(
291
  intrinsics_source: str,
292
  depth_file: Optional[str],
293
  gaussian_count: int,
 
294
  ) -> str:
295
  depth_label = Path(depth_file).name if depth_file else "None"
296
- return (
297
  f"Task: `GS`\n\n"
298
  f"Model: `{model_type}`\n\n"
299
  f"Input depth: `{depth_label}`\n\n"
@@ -301,6 +309,10 @@ def _format_gs_status(
301
  f"Camera intrinsics source: `{intrinsics_source}`\n\n"
302
  f"Exported gaussians: `{gaussian_count}`"
303
  )
 
 
 
 
304
 
305
 
306
  def _load_example_image(example_name: str) -> tuple[np.ndarray, Optional[str], Optional[np.ndarray], str, str]:
@@ -375,6 +387,10 @@ def _normalize_filtered_gaussians(filtered_result):
375
  return filtered_result
376
 
377
 
 
 
 
 
378
  @torch.no_grad()
379
  def _run_depth_inference(
380
  image: np.ndarray,
@@ -535,6 +551,7 @@ def _run_gs_inference(
535
  request: gr.Request,
536
  ):
537
  _ensure_cuda()
 
538
  if image is None:
539
  raise gr.Error("Upload an image or load an example before running inference.")
540
  if model_type == "InfiniDepth_DepthSensor" and not depth_file:
@@ -591,6 +608,7 @@ def _run_gs_inference(
591
  dilate_px=0,
592
  )
593
 
 
594
  depthmap, dino_tokens, query_3d_uniform_coord, pred_depth_3d = model.inference_for_gs(
595
  image=image_tensor,
596
  intrinsics=intrinsics,
@@ -627,13 +645,16 @@ def _run_gs_inference(
627
  gaussian_count = int(pixel_gaussians.means.shape[1])
628
  if gaussian_count == 0:
629
  raise gr.Error("No valid gaussians remained after filtering.")
 
630
 
631
  means, harmonics, opacities, scales, rotations = unpack_gaussians_for_export(pixel_gaussians)
632
 
633
  session_hash = getattr(request, "session_hash", None)
634
  output_dir = ensure_session_output_dir(APP_NAME, session_hash)
635
- ply_path = output_dir / "gaussians.ply"
 
636
 
 
637
  export_ply(
638
  means=means,
639
  harmonics=harmonics,
@@ -646,12 +667,15 @@ def _run_gs_inference(
646
  image_shape=(org_h, org_w),
647
  extrinsic_matrix=extrinsics[0],
648
  )
 
649
 
 
650
  try:
651
- gs_viewer_html = build_embedded_viewer_html(ply_path)
652
  except Exception as exc:
653
  print(f"[Warning] Failed to build embedded GS viewer: {exc}")
654
- gs_viewer_html = build_viewer_error_html(str(exc), ply_path)
 
655
 
656
  metric_depth_source = "user depth" if use_gt_depth and depth_file else "MoGe-2"
657
  status = _format_gs_status(
@@ -660,6 +684,7 @@ def _run_gs_inference(
660
  intrinsics_source=intrinsics_source,
661
  depth_file=depth_file,
662
  gaussian_count=gaussian_count,
 
663
  )
664
  download_files = [str(ply_path)]
665
 
@@ -837,6 +862,18 @@ with gr.Blocks(css=CSS, theme=gr.themes.Soft()) as demo:
837
  depth_download_files = gr.File(label="Depth Files", type="filepath")
838
  gs_download_files = gr.File(label="GS Files", type="filepath")
839
 
 
 
 
 
 
 
 
 
 
 
 
 
840
  task_type.change(
841
  fn=_settings_visibility,
842
  inputs=[task_type, output_resolution_mode],
@@ -857,6 +894,9 @@ with gr.Blocks(css=CSS, theme=gr.themes.Soft()) as demo:
857
  example_gallery.select(
858
  fn=_select_example,
859
  outputs=[selected_example_name, example_selection],
 
 
 
860
  )
861
 
862
  input_depth_file.change(
@@ -866,9 +906,12 @@ with gr.Blocks(css=CSS, theme=gr.themes.Soft()) as demo:
866
  )
867
 
868
  load_example_btn.click(
 
 
 
869
  fn=_load_example_image,
870
  inputs=[selected_example_name],
871
- outputs=[input_image, input_depth_file, input_depth_preview, model_type, depth_info],
872
  )
873
 
874
  submit_btn.click(
@@ -877,16 +920,7 @@ with gr.Blocks(css=CSS, theme=gr.themes.Soft()) as demo:
877
  outputs=[primary_view_tabs],
878
  ).then(
879
  fn=_clear_outputs,
880
- outputs=[
881
- status_output,
882
- depth_comparison,
883
- color_depth,
884
- gray_depth,
885
- depth_model_3d,
886
- depth_download_files,
887
- gs_viewer_html,
888
- gs_download_files,
889
- ],
890
  ).then(
891
  fn=_run_inference,
892
  inputs=[
@@ -905,16 +939,7 @@ with gr.Blocks(css=CSS, theme=gr.themes.Soft()) as demo:
905
  cx_org,
906
  cy_org,
907
  ],
908
- outputs=[
909
- status_output,
910
- depth_comparison,
911
- color_depth,
912
- gray_depth,
913
- depth_model_3d,
914
- depth_download_files,
915
- gs_viewer_html,
916
- gs_download_files,
917
- ],
918
  )
919
 
920
 
 
3
  from functools import lru_cache
4
  import os
5
  from pathlib import Path
6
+ from time import perf_counter
7
  from typing import Optional
8
 
9
 
 
64
  APP_TEMP_ROOT as GS_VIEWER_ROOT,
65
  build_embedded_viewer_html,
66
  build_viewer_error_html,
67
+ ensure_supersplat_viewer_assets,
68
  )
69
  from InfiniDepth.utils.inference_utils import (
70
  apply_sky_mask_to_depth,
 
219
  def _preload_repo_assets() -> tuple[str, ...]:
220
  depth_paths = tuple(_resolve_depth_checkpoint(model_type) for model_type in MODEL_CHOICES)
221
  gs_paths = tuple(_resolve_gs_checkpoint(model_type) for model_type in MODEL_CHOICES)
222
+ viewer_paths: tuple[str, ...] = ()
223
+ try:
224
+ viewer_paths = (str(ensure_supersplat_viewer_assets()),)
225
+ except Exception as exc:
226
+ print(f"[Warning] Failed to preload SuperSplat viewer assets: {exc}")
227
+ return depth_paths + gs_paths + (_resolve_moge2_source(), _resolve_skyseg_path()) + viewer_paths
228
 
229
 
230
  @lru_cache(maxsize=2)
 
298
  intrinsics_source: str,
299
  depth_file: Optional[str],
300
  gaussian_count: int,
301
+ stage_timings: Optional[list[tuple[str, float]]] = None,
302
  ) -> str:
303
  depth_label = Path(depth_file).name if depth_file else "None"
304
+ status = (
305
  f"Task: `GS`\n\n"
306
  f"Model: `{model_type}`\n\n"
307
  f"Input depth: `{depth_label}`\n\n"
 
309
  f"Camera intrinsics source: `{intrinsics_source}`\n\n"
310
  f"Exported gaussians: `{gaussian_count}`"
311
  )
312
+ if stage_timings:
313
+ timing_lines = "\n".join(f"- `{name}`: `{seconds:.2f}s`" for name, seconds in stage_timings)
314
+ status += f"\n\nStage timings:\n{timing_lines}"
315
+ return status
316
 
317
 
318
  def _load_example_image(example_name: str) -> tuple[np.ndarray, Optional[str], Optional[np.ndarray], str, str]:
 
387
  return filtered_result
388
 
389
 
390
+ def _format_seconds(value: float) -> float:
391
+ return round(float(value), 2)
392
+
393
+
394
  @torch.no_grad()
395
  def _run_depth_inference(
396
  image: np.ndarray,
 
551
  request: gr.Request,
552
  ):
553
  _ensure_cuda()
554
+ stage_timings: list[tuple[str, float]] = []
555
  if image is None:
556
  raise gr.Error("Upload an image or load an example before running inference.")
557
  if model_type == "InfiniDepth_DepthSensor" and not depth_file:
 
608
  dilate_px=0,
609
  )
610
 
611
+ model_stage_start = perf_counter()
612
  depthmap, dino_tokens, query_3d_uniform_coord, pred_depth_3d = model.inference_for_gs(
613
  image=image_tensor,
614
  intrinsics=intrinsics,
 
645
  gaussian_count = int(pixel_gaussians.means.shape[1])
646
  if gaussian_count == 0:
647
  raise gr.Error("No valid gaussians remained after filtering.")
648
+ stage_timings.append(("GS model inference", _format_seconds(perf_counter() - model_stage_start)))
649
 
650
  means, harmonics, opacities, scales, rotations = unpack_gaussians_for_export(pixel_gaussians)
651
 
652
  session_hash = getattr(request, "session_hash", None)
653
  output_dir = ensure_session_output_dir(APP_NAME, session_hash)
654
+ run_id = output_dir.name
655
+ ply_path = output_dir / f"{run_id}_gaussians.ply"
656
 
657
+ export_stage_start = perf_counter()
658
  export_ply(
659
  means=means,
660
  harmonics=harmonics,
 
667
  image_shape=(org_h, org_w),
668
  extrinsic_matrix=extrinsics[0],
669
  )
670
+ stage_timings.append(("PLY export", _format_seconds(perf_counter() - export_stage_start)))
671
 
672
+ viewer_stage_start = perf_counter()
673
  try:
674
+ gs_viewer_html = build_embedded_viewer_html(ply_path, cache_key=run_id)
675
  except Exception as exc:
676
  print(f"[Warning] Failed to build embedded GS viewer: {exc}")
677
+ gs_viewer_html = build_viewer_error_html(str(exc), ply_path, cache_key=run_id)
678
+ stage_timings.append(("Viewer HTML prep", _format_seconds(perf_counter() - viewer_stage_start)))
679
 
680
  metric_depth_source = "user depth" if use_gt_depth and depth_file else "MoGe-2"
681
  status = _format_gs_status(
 
684
  intrinsics_source=intrinsics_source,
685
  depth_file=depth_file,
686
  gaussian_count=gaussian_count,
687
+ stage_timings=stage_timings,
688
  )
689
  download_files = [str(ply_path)]
690
 
 
862
  depth_download_files = gr.File(label="Depth Files", type="filepath")
863
  gs_download_files = gr.File(label="GS Files", type="filepath")
864
 
865
+ result_outputs = [
866
+ status_output,
867
+ depth_comparison,
868
+ color_depth,
869
+ gray_depth,
870
+ depth_model_3d,
871
+ depth_download_files,
872
+ gs_viewer_html,
873
+ gs_download_files,
874
+ ]
875
+ example_load_outputs = [input_image, input_depth_file, input_depth_preview, model_type, depth_info]
876
+
877
  task_type.change(
878
  fn=_settings_visibility,
879
  inputs=[task_type, output_resolution_mode],
 
894
  example_gallery.select(
895
  fn=_select_example,
896
  outputs=[selected_example_name, example_selection],
897
+ ).then(
898
+ fn=_clear_outputs,
899
+ outputs=result_outputs,
900
  )
901
 
902
  input_depth_file.change(
 
906
  )
907
 
908
  load_example_btn.click(
909
+ fn=_clear_outputs,
910
+ outputs=result_outputs,
911
+ ).then(
912
  fn=_load_example_image,
913
  inputs=[selected_example_name],
914
+ outputs=example_load_outputs,
915
  )
916
 
917
  submit_btn.click(
 
920
  outputs=[primary_view_tabs],
921
  ).then(
922
  fn=_clear_outputs,
923
+ outputs=result_outputs,
 
 
 
 
 
 
 
 
 
924
  ).then(
925
  fn=_run_inference,
926
  inputs=[
 
939
  cx_org,
940
  cy_org,
941
  ],
942
+ outputs=result_outputs,
 
 
 
 
 
 
 
 
 
943
  )
944
 
945