Spaces:
Running on Zero
Running on Zero
Keep pycolmap out of the main process; CUDA diagnostics
Browse files- app.py +28 -26
- 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
|
| 177 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 =
|
|
|
|
| 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",
|