Download code/scripts/make_demo.py from changh95/ptv3-p150: direct link, hf CLI and curl.
- Browser
- Download file 8.31 kB
-
https://huggingface.co/changh95/ptv3-p150/resolve/main/code/scripts/make_demo.py
- Command line
-
hf download hf://changh95/ptv3-p150/code/scripts/make_demo.py
-
curl -L -o make_demo.py https://huggingface.co/changh95/ptv3-p150/resolve/main/code/scripts/make_demo.py
8.31 kB
| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: Apache-2.0 | |
| """Demo media of ptv3-p150: run the p150 on point clouds and save its outputs, then render bird's-eye views | |
| (``tt_ptv3.viz``, numpy + Pillow) next to the fp32 CPU reference. | |
| # 1. on the device (one devrun window): the served model (serve profile seg_det, default runtime params) | |
| bin/devrun -t 1800 -- python code/scripts/make_demo.py run --out OUT \ | |
| --input code/tt_ptv3/samples/test_pcd.npz --input research/ptv3/public_data/inputs/ps019_f40.pcd5.npy \ | |
| [--min-voxels-zero research/ptv3/public_data/inputs/ns0103_k20.pcd5.npy] [--glob 'DIR/*.pcd5.npy'] | |
| # 2. on the host: BEV renders (p150 | CPU reference | differences) and a GIF of a sequence | |
| python code/scripts/make_demo.py compare OUT/test_pcd.tt.npz --reference code/tt_ptv3/samples/test_pcd.reference.json \ | |
| --points code/tt_ptv3/samples/test_pcd.npz --out media/ptv3_test_pcd_tt_vs_cpu.png --title "..." | |
| python code/scripts/make_demo.py gif 'OUT/ps019_f*.tt.npz' --points-dir DIR --out media/x.gif --title "..." | |
| ``run`` writes per input ``<stem>.tt.npz`` (``label_ids``, ``scores``, ``classification``, ``entropy``, | |
| ``filter_keep``: the per-point outputs in input order) and ``<stem>.tt.json`` (detections as ``/predict`` dicts, | |
| ``timing_ms``, ``meta``, the runtime params). ``.npy`` inputs with 5 columns (the research ``pcd5`` layout: x, y, z, | |
| intensity 0-255, ring) are cut to the first 4. A reference is either a stored ``/predict`` body (``*.json``) or a | |
| research golden (``*.golden.npz``: ``point_label15``, ``boxes`` [M, 11]). | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import base64 | |
| import glob | |
| import io | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| import numpy as np | |
| HERE = Path(__file__).resolve().parent | |
| sys.path.insert(0, str(HERE.parent)) | |
| DET = ["CAR", "TRUCK", "BUS", "BICYCLE", "PEDESTRIAN", "TRAFFIC_CONE", "BARRIER"] | |
| def _stem(path: Path) -> str: | |
| name = path.name | |
| for suf in (".pcd5.npy", ".npz", ".npy", ".pcd"): | |
| if name.endswith(suf): | |
| return name[: -len(suf)] | |
| return path.stem | |
| def _points(path: Path) -> np.ndarray: | |
| from tt_ptv3 import io as tio | |
| if path.suffix == ".npy": | |
| a = np.load(path, allow_pickle=False) | |
| return np.ascontiguousarray(a[:, :4], np.float32) | |
| return np.asarray(tio.load_points(str(path)).points, np.float32) | |
| def cmd_run(a) -> int: | |
| from tt_ptv3 import PTv3 | |
| jobs = [(Path(p), {}) for p in a.input] + [(Path(p), {"min_voxels": 0}) for p in a.min_voxels_zero] | |
| for pattern in a.glob: | |
| jobs += [(Path(p), {}) for p in sorted(glob.glob(pattern))] | |
| out = Path(a.out) | |
| out.mkdir(parents=True, exist_ok=True) | |
| kw = {"weights_dir": a.weights_dir} if a.weights_dir else {} | |
| t0 = time.time() | |
| with PTv3.from_pretrained(device_id=a.device_id, variant="seg_det", **kw) as model: | |
| print(f"from_pretrained {time.time() - t0:.1f} s; {model.info.get('device')}", flush=True) | |
| for path, params in jobs: | |
| o = model(_points(path), **params) | |
| stem = _stem(path) | |
| np.savez_compressed(out / f"{stem}.tt.npz", label_ids=np.asarray(o.label_ids), scores=np.asarray(o.scores), | |
| classification=np.asarray(o.classification), entropy=np.asarray(o.entropy), | |
| filter_keep=np.asarray(o.filter_keep)) | |
| dets = o.to_dict().get("detections") or [] | |
| (out / f"{stem}.tt.json").write_text(json.dumps({"input": str(path), "params": params, "detections": dets, | |
| "timing_ms": o.timing_ms, "meta": o.meta}, | |
| default=lambda v: v.tolist() if hasattr(v, "tolist") else str(v))) | |
| print(f"{stem}: {len(o.label_ids)} points, {len(dets)} boxes, total {o.timing_ms.get('total', 0):.1f} ms", | |
| flush=True) | |
| return 0 | |
| def _decode(spec): | |
| return np.load(io.BytesIO(base64.b64decode(spec["data"])))[spec["key"]] | |
| def load_tt(path: Path): | |
| with np.load(path) as z: | |
| labels = z["label_ids"] | |
| side = json.loads(Path(str(path)[: -len(".tt.npz")] + ".tt.json").read_text()) | |
| return labels, side["detections"] | |
| def load_reference(path: Path): | |
| """``(labels per point, detection dicts)`` of a stored /predict body or a research golden.""" | |
| if path.suffix == ".json": | |
| b = json.loads(path.read_text()) | |
| return _decode(b["labels"]), b.get("detections") or [] | |
| with np.load(path) as z: | |
| labels, boxes = z["point_label15"], z["boxes"] | |
| dets = [{"label": DET[int(r[0])], "score": float(r[1]), "center": [float(r[2]), float(r[3]), float(r[4])], | |
| "size": [float(r[5]), float(r[6]), float(r[7])], "yaw": float(r[8])} for r in boxes] | |
| return labels, dets | |
| def cmd_compare(a) -> int: | |
| from tt_ptv3.viz import render_compare, save | |
| labels, dets = load_tt(Path(a.tt)) | |
| ref_labels, refs = load_reference(Path(a.reference)) | |
| xyz = _points(Path(a.points))[:, :3] | |
| img = render_compare(xyz, labels, ref_labels, dets, refs, title=a.title, lines=a.line, half_range=a.range, | |
| px_per_m=a.px_per_m) # None: the panels fit 960 px (2 per row) | |
| print(save(img, a.out), img.size, f"labels agree {float((labels == ref_labels).mean()):.4%}") | |
| return 0 | |
| def cmd_bev(a) -> int: | |
| from tt_ptv3.viz import render_bev, save | |
| labels, dets = load_tt(Path(a.tt)) | |
| img = render_bev(_points(Path(a.points))[:, :3], labels, dets, title=a.title, lines=a.line, half_range=a.range, | |
| px_per_m=a.px_per_m, caption="p150: per-point class + 3D boxes") | |
| print(save(img, a.out), img.size) | |
| return 0 | |
| def cmd_gif(a) -> int: | |
| from tt_ptv3.viz import render_bev | |
| frames = [] | |
| for p in sorted(glob.glob(a.tt)): | |
| stem = Path(p).name[: -len(".tt.npz")] | |
| labels, dets = load_tt(Path(p)) | |
| xyz = _points(Path(a.points_dir) / f"{stem}.pcd5.npy")[:, :3] | |
| img = render_bev(xyz, labels, dets, title=f"{a.title} ({stem})", lines=a.line, half_range=a.range, | |
| px_per_m=a.px_per_m, caption="p150: per-point class + 3D boxes") | |
| frames.append(img.convert("P", palette=1, colors=64)) # adaptive palette: small GIF | |
| frames[0].save(a.out, save_all=True, append_images=frames[1:], duration=int(1000 / a.fps), loop=0, optimize=True) | |
| print(a.out, len(frames), "frames", Path(a.out).stat().st_size, "bytes") | |
| return 0 | |
| def main(argv=None) -> int: | |
| ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| sub = ap.add_subparsers(dest="cmd", required=True) | |
| r = sub.add_parser("run") | |
| r.add_argument("--out", required=True) | |
| r.add_argument("--input", action="append", default=[]) | |
| r.add_argument("--min-voxels-zero", action="append", default=[], help="inputs below the node's 16k-voxel floor") | |
| r.add_argument("--glob", action="append", default=[]) | |
| r.add_argument("--device-id", type=int, default=0) | |
| r.add_argument("--weights-dir") | |
| for name, extra in (("compare", ("--reference",)), ("bev", ())): | |
| c = sub.add_parser(name) | |
| c.add_argument("tt") | |
| c.add_argument("--points", required=True) | |
| for e in extra: | |
| c.add_argument(e, required=True) | |
| c.add_argument("--out", required=True) | |
| c.add_argument("--title", default="") | |
| c.add_argument("--line", action="append", default=[]) | |
| c.add_argument("--range", type=float, default=40.0) | |
| c.add_argument("--px-per-m", type=float, default=None if name == "compare" else 8.0) | |
| g = sub.add_parser("gif") | |
| g.add_argument("tt", help="glob of <stem>.tt.npz") | |
| g.add_argument("--points-dir", required=True) | |
| g.add_argument("--out", required=True) | |
| g.add_argument("--title", default="") | |
| g.add_argument("--line", action="append", default=[]) | |
| g.add_argument("--range", type=float, default=40.0) | |
| g.add_argument("--px-per-m", type=float, default=6.0) | |
| g.add_argument("--fps", type=float, default=5.0) | |
| a = ap.parse_args(argv) | |
| return {"run": cmd_run, "compare": cmd_compare, "bev": cmd_bev, "gif": cmd_gif}[a.cmd](a) | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |