multimodalart HF Staff commited on
Commit
a5c56a7
·
verified ·
1 Parent(s): 8f8cd22

Keep pycolmap out of the main process; CUDA diagnostics

Browse files
Files changed (2) hide show
  1. app.py +28 -26
  2. colmap_sfm.py +25 -0
app.py CHANGED
@@ -69,6 +69,18 @@ if CUDA_HOME:
69
  except OSError:
70
  continue
71
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  import torch.utils.cpp_extension as _cppext # noqa: E402
73
 
74
  _cppext._check_cuda_version = lambda *a, **k: None
@@ -80,6 +92,7 @@ from gsplat.cuda._backend import _C as _GSPLAT_C # noqa: E402
80
  if _GSPLAT_C is None:
81
  raise RuntimeError(f"gsplat CUDA backend unavailable (CUDA_HOME={CUDA_HOME}).")
82
  print(f"[setup] gsplat backend ready in {time.time() - _t0:.1f}s", flush=True)
 
83
 
84
 
85
  # ---------------------------------------------------------------------------
@@ -88,11 +101,12 @@ print(f"[setup] gsplat backend ready in {time.time() - _t0:.1f}s", flush=True)
88
  import imageio.v2 as imageio # noqa: E402
89
  import numpy as np # noqa: E402
90
  import gradio as gr # noqa: E402
91
- import pycolmap # noqa: E402
92
  from huggingface_hub import hf_hub_download # noqa: E402
93
  from PIL import Image # noqa: E402
94
 
95
  import codecsplat_pipeline as cp # noqa: E402
 
 
96
  from colmap_sfm import IMAGE_EXT, run_sfm_subprocess # noqa: E402
97
 
98
  MODEL_REPO = "pengpeng-yu/CodecSplat"
@@ -107,6 +121,7 @@ print("[setup] building CodecSplat...", flush=True)
107
  ENCODER = cp.build_encoder(ckpt_path).to("cuda")
108
  DECODER = cp.build_decoder().to("cuda")
109
  print("[setup] model ready", flush=True)
 
110
 
111
 
112
  def _u8(t: torch.Tensor) -> np.ndarray:
@@ -173,31 +188,17 @@ def codecsplat_gpu(image_paths: list[str], c2w: np.ndarray, K: np.ndarray, lmb:
173
  # ---------------------------------------------------------------------------
174
  # 4. Full pipeline (CPU COLMAP -> GPU CodecSplat -> CPU export)
175
  # ---------------------------------------------------------------------------
176
- def _read_colmap_scene(scene_dir: str):
177
- rec = pycolmap.Reconstruction(os.path.join(scene_dir, "sparse"))
178
- items = []
179
- for _, img in rec.images.items():
180
- cam = rec.cameras[img.camera_id]
181
- p = cam.params
182
- if cam.model.name == "PINHOLE":
183
- fx, fy, cx, cy = p[:4]
184
- elif cam.model.name == "SIMPLE_PINHOLE":
185
- fx = fy = p[0]
186
- cx, cy = p[1:3]
187
- else:
188
- continue
189
- K = np.eye(3, dtype=np.float32)
190
- K[0, 0], K[1, 1] = fx / cam.width, fy / cam.height
191
- K[0, 2], K[1, 2] = cx / cam.width, cy / cam.height
192
- w2c = np.eye(4, dtype=np.float64)
193
- w2c[:3, :4] = img.cam_from_world().matrix() if callable(img.cam_from_world) else img.cam_from_world.matrix()
194
- path = os.path.join(scene_dir, "images", img.name)
195
- if os.path.exists(path):
196
- items.append((img.name, path, np.linalg.inv(w2c).astype(np.float32), K))
197
- items.sort(key=lambda x: x[0])
198
- if len(items) < 2:
199
  raise gr.Error("COLMAP registered fewer than 2 usable images.")
200
- return [i[1] for i in items], np.stack([i[2] for i in items]), np.stack([i[3] for i in items])
 
 
 
 
 
 
 
201
 
202
 
203
  def reconstruct(images, lmb: float = 1024.0, matcher: str = "exhaustive",
@@ -235,7 +236,8 @@ def reconstruct(images, lmb: float = 1024.0, matcher: str = "exhaustive",
235
  except (ValueError, RuntimeError) as exc:
236
  raise gr.Error(str(exc)) from exc
237
  t_sfm = time.time() - t_start
238
- img_paths, c2w, K = _read_colmap_scene(sfm["scene_dir"])
 
239
 
240
  progress(0.55, desc="CodecSplat: encode -> bitstream -> decode -> render")
241
  r = codecsplat_gpu(img_paths, c2w, K, float(lmb))
 
69
  except OSError:
70
  continue
71
 
72
+ def _cuda_diag(stage: str) -> None:
73
+ """Log whether the CUDA driver got loaded into the main (GPU-less) process."""
74
+ try:
75
+ maps = open("/proc/self/maps").read()
76
+ libcuda = "libcuda.so" in maps
77
+ except Exception:
78
+ libcuda = "?"
79
+ print(f"[diag] {stage}: libcuda mapped={libcuda} "
80
+ f"torch.cuda.is_initialized={torch.cuda.is_initialized()}", flush=True)
81
+
82
+
83
+ _cuda_diag("after torch import")
84
  import torch.utils.cpp_extension as _cppext # noqa: E402
85
 
86
  _cppext._check_cuda_version = lambda *a, **k: None
 
92
  if _GSPLAT_C is None:
93
  raise RuntimeError(f"gsplat CUDA backend unavailable (CUDA_HOME={CUDA_HOME}).")
94
  print(f"[setup] gsplat backend ready in {time.time() - _t0:.1f}s", flush=True)
95
+ _cuda_diag("after gsplat build")
96
 
97
 
98
  # ---------------------------------------------------------------------------
 
101
  import imageio.v2 as imageio # noqa: E402
102
  import numpy as np # noqa: E402
103
  import gradio as gr # noqa: E402
 
104
  from huggingface_hub import hf_hub_download # noqa: E402
105
  from PIL import Image # noqa: E402
106
 
107
  import codecsplat_pipeline as cp # noqa: E402
108
+
109
+ _cuda_diag("after pipeline import")
110
  from colmap_sfm import IMAGE_EXT, run_sfm_subprocess # noqa: E402
111
 
112
  MODEL_REPO = "pengpeng-yu/CodecSplat"
 
121
  ENCODER = cp.build_encoder(ckpt_path).to("cuda")
122
  DECODER = cp.build_decoder().to("cuda")
123
  print("[setup] model ready", flush=True)
124
+ _cuda_diag("after model build")
125
 
126
 
127
  def _u8(t: torch.Tensor) -> np.ndarray:
 
188
  # ---------------------------------------------------------------------------
189
  # 4. Full pipeline (CPU COLMAP -> GPU CodecSplat -> CPU export)
190
  # ---------------------------------------------------------------------------
191
+ def _scene_arrays(frames: list[dict]):
192
+ if len(frames) < 2:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  raise gr.Error("COLMAP registered fewer than 2 usable images.")
194
+ paths, c2ws, Ks = [], [], []
195
+ for f in frames:
196
+ w2c = np.eye(4, dtype=np.float64)
197
+ w2c[:3, :4] = np.asarray(f["w2c"], dtype=np.float64)
198
+ paths.append(f["path"])
199
+ c2ws.append(np.linalg.inv(w2c).astype(np.float32))
200
+ Ks.append(np.asarray(f["K"], dtype=np.float32))
201
+ return paths, np.stack(c2ws), np.stack(Ks)
202
 
203
 
204
  def reconstruct(images, lmb: float = 1024.0, matcher: str = "exhaustive",
 
236
  except (ValueError, RuntimeError) as exc:
237
  raise gr.Error(str(exc)) from exc
238
  t_sfm = time.time() - t_start
239
+ img_paths, c2w, K = _scene_arrays(sfm["frames"])
240
+ _cuda_diag("before GPU call")
241
 
242
  progress(0.55, desc="CodecSplat: encode -> bitstream -> decode -> render")
243
  r = codecsplat_gpu(img_paths, c2w, K, float(lmb))
colmap_sfm.py CHANGED
@@ -183,7 +183,32 @@ def run_sfm(
183
  except Exception:
184
  pass
185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  return {
 
187
  "scene_dir": str(scene_dir),
188
  "sparse_sub": "sparse",
189
  "images_sub": "images",
 
183
  except Exception:
184
  pass
185
 
186
+ # Read back the undistorted PINHOLE cameras (normalized K, OpenCV c2w), sorted by name.
187
+ und = pycolmap.Reconstruction(str(scene_dir / "sparse"))
188
+ frames = []
189
+ for _, img in und.images.items():
190
+ cam = und.cameras[img.camera_id]
191
+ p = list(cam.params)
192
+ if cam.model.name == "PINHOLE":
193
+ fx, fy, cx, cy = p[:4]
194
+ elif cam.model.name == "SIMPLE_PINHOLE":
195
+ fx = fy = p[0]
196
+ cx, cy = p[1:3]
197
+ else:
198
+ continue
199
+ cfw = img.cam_from_world() if callable(img.cam_from_world) else img.cam_from_world
200
+ w2c = cfw.matrix() # 3x4
201
+ path = scene_dir / "images" / img.name
202
+ if path.exists():
203
+ frames.append({
204
+ "name": img.name, "path": str(path),
205
+ "K": [[fx / cam.width, 0, cx / cam.width], [0, fy / cam.height, cy / cam.height], [0, 0, 1]],
206
+ "w2c": [list(map(float, row)) for row in w2c],
207
+ })
208
+ frames.sort(key=lambda f: f["name"])
209
+
210
  return {
211
+ "frames": frames,
212
  "scene_dir": str(scene_dir),
213
  "sparse_sub": "sparse",
214
  "images_sub": "images",