Spaces:
Running on Zero
Running on Zero
Add PXDepth demo
Browse files- app.py +12 -47
- requirements.txt +1 -0
app.py
CHANGED
|
@@ -193,41 +193,21 @@ def filter_flying_points(
|
|
| 193 |
return (points[keep], colors[keep]) if keep.any() else (points, colors)
|
| 194 |
|
| 195 |
|
| 196 |
-
def
|
| 197 |
path: Path,
|
| 198 |
points: np.ndarray,
|
| 199 |
colors: np.ndarray,
|
| 200 |
-
point_size: float,
|
| 201 |
) -> None:
|
| 202 |
-
"""Write
|
|
|
|
|
|
|
| 203 |
display_points = points * np.array([1.0, -1.0, -1.0], dtype=np.float32)
|
| 204 |
-
|
| 205 |
-
extent = max(float(np.max(high - low)), 1e-6)
|
| 206 |
-
spacing = extent / max(float(np.sqrt(points.shape[0])), 1.0)
|
| 207 |
-
scale = max(spacing * float(point_size) * 0.65, 1e-7)
|
| 208 |
-
|
| 209 |
-
dtype = np.dtype(
|
| 210 |
-
[
|
| 211 |
-
("position", "<f4", (3,)),
|
| 212 |
-
("scale", "<f4", (3,)),
|
| 213 |
-
("color", "u1", (4,)),
|
| 214 |
-
("rotation", "u1", (4,)),
|
| 215 |
-
],
|
| 216 |
-
align=False,
|
| 217 |
-
)
|
| 218 |
-
splats = np.empty(points.shape[0], dtype=dtype)
|
| 219 |
-
splats["position"] = display_points
|
| 220 |
-
splats["scale"] = scale
|
| 221 |
-
splats["color"][:, :3] = colors
|
| 222 |
-
splats["color"][:, 3] = 255
|
| 223 |
-
splats["rotation"] = np.array([255, 128, 128, 128], dtype=np.uint8)
|
| 224 |
-
splats.tofile(path)
|
| 225 |
|
| 226 |
|
| 227 |
def update_viewer(
|
| 228 |
cache_path: Optional[str],
|
| 229 |
filter_points: bool,
|
| 230 |
-
point_size: float,
|
| 231 |
max_points: int,
|
| 232 |
) -> Optional[str]:
|
| 233 |
"""Rebuild the viewer from cached points without running either model."""
|
|
@@ -244,10 +224,10 @@ def update_viewer(
|
|
| 244 |
raise gr.Error("No points remain after filtering.")
|
| 245 |
|
| 246 |
cache_file = Path(cache_path)
|
| 247 |
-
tag = f"{int(max_points)}_{int(filter_points)}
|
| 248 |
-
viewer_path = cache_file.with_name(f"pointcloud_viewer_{tag}.
|
| 249 |
-
|
| 250 |
-
for old_path in cache_file.parent.glob("pointcloud_viewer_*.
|
| 251 |
if old_path != viewer_path:
|
| 252 |
old_path.unlink(missing_ok=True)
|
| 253 |
return str(viewer_path)
|
|
@@ -293,7 +273,6 @@ def on_submit(
|
|
| 293 |
image: Optional[np.ndarray],
|
| 294 |
apply_mask: bool,
|
| 295 |
filter_points: bool,
|
| 296 |
-
point_size: float,
|
| 297 |
max_points: int,
|
| 298 |
request: gr.Request,
|
| 299 |
):
|
|
@@ -351,7 +330,6 @@ def on_submit(
|
|
| 351 |
viewer_path = update_viewer(
|
| 352 |
str(cache_path),
|
| 353 |
filter_points,
|
| 354 |
-
point_size,
|
| 355 |
max_points,
|
| 356 |
)
|
| 357 |
|
|
@@ -380,6 +358,7 @@ See the [paper](https://arxiv.org/abs/2608.16984),
|
|
| 380 |
label="Input Image",
|
| 381 |
image_mode="RGB",
|
| 382 |
type="numpy",
|
|
|
|
| 383 |
elem_id="img-display-input",
|
| 384 |
)
|
| 385 |
with gr.Accordion(label="Settings", open=False):
|
|
@@ -392,14 +371,6 @@ See the [paper](https://arxiv.org/abs/2608.16984),
|
|
| 392 |
info="Statistical outlier filtering; does not rerun the model.",
|
| 393 |
value=False,
|
| 394 |
)
|
| 395 |
-
point_size = gr.Slider(
|
| 396 |
-
0.25,
|
| 397 |
-
3.0,
|
| 398 |
-
value=1.0,
|
| 399 |
-
step=0.05,
|
| 400 |
-
label="Point Size",
|
| 401 |
-
info="Updates only the 3D viewer.",
|
| 402 |
-
)
|
| 403 |
max_points = gr.Slider(
|
| 404 |
50_000,
|
| 405 |
500_000,
|
|
@@ -449,24 +420,18 @@ See the [paper](https://arxiv.org/abs/2608.16984),
|
|
| 449 |
|
| 450 |
submit.click(
|
| 451 |
on_submit,
|
| 452 |
-
[input_image, apply_mask, filter_points,
|
| 453 |
[depth_map, model_3d, downloads, viewer_cache],
|
| 454 |
show_progress="full",
|
| 455 |
concurrency_limit=1,
|
| 456 |
)
|
| 457 |
-
viewer_inputs = [viewer_cache, filter_points,
|
| 458 |
filter_points.change(
|
| 459 |
update_viewer,
|
| 460 |
viewer_inputs,
|
| 461 |
model_3d,
|
| 462 |
show_progress="minimal",
|
| 463 |
)
|
| 464 |
-
point_size.release(
|
| 465 |
-
update_viewer,
|
| 466 |
-
viewer_inputs,
|
| 467 |
-
model_3d,
|
| 468 |
-
show_progress="minimal",
|
| 469 |
-
)
|
| 470 |
max_points.release(
|
| 471 |
update_viewer,
|
| 472 |
viewer_inputs,
|
|
|
|
| 193 |
return (points[keep], colors[keep]) if keep.any() else (points, colors)
|
| 194 |
|
| 195 |
|
| 196 |
+
def write_viewer_glb(
|
| 197 |
path: Path,
|
| 198 |
points: np.ndarray,
|
| 199 |
colors: np.ndarray,
|
|
|
|
| 200 |
) -> None:
|
| 201 |
+
"""Write the browser point cloud using the stable GLB viewer path."""
|
| 202 |
+
import trimesh
|
| 203 |
+
|
| 204 |
display_points = points * np.array([1.0, -1.0, -1.0], dtype=np.float32)
|
| 205 |
+
trimesh.PointCloud(display_points, colors=colors).export(path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
|
| 208 |
def update_viewer(
|
| 209 |
cache_path: Optional[str],
|
| 210 |
filter_points: bool,
|
|
|
|
| 211 |
max_points: int,
|
| 212 |
) -> Optional[str]:
|
| 213 |
"""Rebuild the viewer from cached points without running either model."""
|
|
|
|
| 224 |
raise gr.Error("No points remain after filtering.")
|
| 225 |
|
| 226 |
cache_file = Path(cache_path)
|
| 227 |
+
tag = f"{int(max_points)}_{int(filter_points)}"
|
| 228 |
+
viewer_path = cache_file.with_name(f"pointcloud_viewer_{tag}.glb")
|
| 229 |
+
write_viewer_glb(viewer_path, points, colors)
|
| 230 |
+
for old_path in cache_file.parent.glob("pointcloud_viewer_*.*"):
|
| 231 |
if old_path != viewer_path:
|
| 232 |
old_path.unlink(missing_ok=True)
|
| 233 |
return str(viewer_path)
|
|
|
|
| 273 |
image: Optional[np.ndarray],
|
| 274 |
apply_mask: bool,
|
| 275 |
filter_points: bool,
|
|
|
|
| 276 |
max_points: int,
|
| 277 |
request: gr.Request,
|
| 278 |
):
|
|
|
|
| 330 |
viewer_path = update_viewer(
|
| 331 |
str(cache_path),
|
| 332 |
filter_points,
|
|
|
|
| 333 |
max_points,
|
| 334 |
)
|
| 335 |
|
|
|
|
| 358 |
label="Input Image",
|
| 359 |
image_mode="RGB",
|
| 360 |
type="numpy",
|
| 361 |
+
placeholder="# Drop an image here\n— or —\nClick to upload",
|
| 362 |
elem_id="img-display-input",
|
| 363 |
)
|
| 364 |
with gr.Accordion(label="Settings", open=False):
|
|
|
|
| 371 |
info="Statistical outlier filtering; does not rerun the model.",
|
| 372 |
value=False,
|
| 373 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
max_points = gr.Slider(
|
| 375 |
50_000,
|
| 376 |
500_000,
|
|
|
|
| 420 |
|
| 421 |
submit.click(
|
| 422 |
on_submit,
|
| 423 |
+
[input_image, apply_mask, filter_points, max_points],
|
| 424 |
[depth_map, model_3d, downloads, viewer_cache],
|
| 425 |
show_progress="full",
|
| 426 |
concurrency_limit=1,
|
| 427 |
)
|
| 428 |
+
viewer_inputs = [viewer_cache, filter_points, max_points]
|
| 429 |
filter_points.change(
|
| 430 |
update_viewer,
|
| 431 |
viewer_inputs,
|
| 432 |
model_3d,
|
| 433 |
show_progress="minimal",
|
| 434 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
max_points.release(
|
| 436 |
update_viewer,
|
| 437 |
viewer_inputs,
|
requirements.txt
CHANGED
|
@@ -8,5 +8,6 @@ huggingface_hub>=0.28
|
|
| 8 |
matplotlib>=3.8
|
| 9 |
pillow>=10.0
|
| 10 |
scipy>=1.12
|
|
|
|
| 11 |
git+https://github.com/EasternJournalist/utils3d.git@3fab839f0be9931dac7c8488eb0e1600c236e183
|
| 12 |
git+https://github.com/microsoft/MoGe.git@42acd8f46e974f5e0548ecd72315d1d7df2cb6f4
|
|
|
|
| 8 |
matplotlib>=3.8
|
| 9 |
pillow>=10.0
|
| 10 |
scipy>=1.12
|
| 11 |
+
trimesh>=4.0
|
| 12 |
git+https://github.com/EasternJournalist/utils3d.git@3fab839f0be9931dac7c8488eb0e1600c236e183
|
| 13 |
git+https://github.com/microsoft/MoGe.git@42acd8f46e974f5e0548ecd72315d1d7df2cb6f4
|