#!/usr/bin/env python3 """Open viser viewer with all PLYs from one AUV. Usage: viser_auv.py --ply-dir /path/to/auv/ply --port 9210 """ from __future__ import annotations import argparse import sys import time from pathlib import Path import numpy as np def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--ply-dir", required=True) ap.add_argument("--port", type=int, default=9210) ap.add_argument("--point-size", type=float, default=0.01) ap.add_argument("--max-points-per-ply", type=int, default=1_500_000) args = ap.parse_args() try: import open3d as o3d import viser except ImportError as e: sys.exit(f"missing dep: {e}") ply_dir = Path(args.ply_dir) plys = sorted(ply_dir.glob("**/*.ply")) print(f"Found {len(plys)} PLY files in {ply_dir}", flush=True) if not plys: sys.exit("no PLY found") server = viser.ViserServer(host="0.0.0.0", port=args.port) palette = [ (1.0, 0.30, 0.30), (0.30, 1.0, 0.30), (0.30, 0.55, 1.0), (1.0, 0.85, 0.20), (1.0, 0.30, 1.0), (0.30, 1.0, 1.0), (1.0, 0.55, 0.20), (0.55, 0.30, 1.0), ] for i, p in enumerate(plys): pcd = o3d.io.read_point_cloud(str(p)) pts = np.asarray(pcd.points, dtype=np.float32) if len(pts) == 0: print(f" ! {p.name}: empty", flush=True) continue if pcd.has_colors(): cols = np.asarray(pcd.colors, dtype=np.float32) else: cols = np.tile(palette[i % len(palette)], (len(pts), 1)).astype(np.float32) if len(pts) > args.max_points_per_ply: idx = np.random.choice(len(pts), args.max_points_per_ply, replace=False) pts = pts[idx] cols = cols[idx] # viser wants uint8 colors cols_u8 = (cols * 255).clip(0, 255).astype(np.uint8) name = f"/{p.parent.name}_{p.stem}" server.scene.add_point_cloud( name=name, points=pts, colors=cols_u8, point_size=args.point_size ) print(f" + {p.name}: {len(pts):,} pts", flush=True) print(f"Viser ready on port {args.port}", flush=True) while True: time.sleep(60) if __name__ == "__main__": main()