viewer on-demand — relancer viser à la demande depuis le dashboard
Le viser de demo.py était tué dès que le PLY était écrit (pour libérer la VRAM),
donc les liens dans le dashboard menaient vers ERR_CONNECTION_REFUSED.
Ajout d'un viewer standalone indépendant :
- scripts/viser_ply.py : charge un PLY via open3d + sert via viser (sans GPU),
subsample random à 2M pts max pour rester fluide
- app/main.py : routes POST /jobs/{id}/view et /stitches/{id}/view qui scp
le script sur le worker et lancent un viser détaché (nohup+setsid+disown via
wrapper shell déposé sur le worker)
- templates : remplace <a href> par <button class=viewer-btn> qui POST puis
window.open de l'URL retournée
- Dockerfile : copie scripts/ dans l'image (nécessaire pour scp-er viser_ply.py)
This commit is contained in:
53
scripts/viser_ply.py
Normal file
53
scripts/viser_ply.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Minimal standalone viser viewer for a single PLY point cloud.
|
||||
|
||||
Usage:
|
||||
python3 viser_ply.py path/to/cloud.ply --port 8200
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("ply", help="PLY file to visualize")
|
||||
ap.add_argument("--port", type=int, default=8200)
|
||||
ap.add_argument("--point-size", type=float, default=0.01)
|
||||
ap.add_argument("--downsample", type=float, default=0.0,
|
||||
help="Voxel downsample size (0 = no downsample)")
|
||||
ap.add_argument("--max-points", type=int, default=2_000_000,
|
||||
help="Random subsample to this many points if cloud is larger")
|
||||
args = ap.parse_args()
|
||||
|
||||
try:
|
||||
import open3d as o3d
|
||||
import viser
|
||||
except ImportError as e:
|
||||
sys.exit(f"missing dep: {e}")
|
||||
|
||||
pcd = o3d.io.read_point_cloud(args.ply)
|
||||
if args.downsample > 0:
|
||||
pcd = pcd.voxel_down_sample(args.downsample)
|
||||
pts = np.asarray(pcd.points, dtype=np.float32)
|
||||
cols = np.asarray(pcd.colors, dtype=np.float32) if pcd.has_colors() else np.ones_like(pts) * 0.7
|
||||
if len(pts) > args.max_points:
|
||||
idx = np.random.choice(len(pts), args.max_points, replace=False)
|
||||
pts, cols = pts[idx], cols[idx]
|
||||
print(f"loaded {len(pts):,} pts from {args.ply}", flush=True)
|
||||
|
||||
server = viser.ViserServer(host="0.0.0.0", port=args.port)
|
||||
server.scene.add_point_cloud(
|
||||
"/cloud", points=pts, colors=(cols * 255).astype(np.uint8), point_size=args.point_size
|
||||
)
|
||||
print(f"viser listening on *:{args.port}")
|
||||
while True:
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user