| |
| """ |
| 无头环境生成超点 / 语义 GT 点云预览 PNG(matplotlib,非 Blender)。 |
| |
| 若需要 Blender(bpy)真渲染,请用: |
| PAMI2026/run_blender_superpoints.sh |
| 或直接 blender --background --python PAMI2026/blender_visualize_superpoints.py -- ... |
| |
| 输出目录默认:PAMI2026/outputs/superpoint_vis/ |
| |
| 用法: |
| /mnt/data/AODUOLI/miniconda_envs/Aoduo/bin/python \\ |
| /mnt/data/AODUOLI/PAMI2026/render_superpoints_image.py \\ |
| --room Area_1/office_1 |
| """ |
| from __future__ import annotations |
|
|
| import argparse |
| import math |
| import os |
| from pathlib import Path |
|
|
| import matplotlib |
|
|
| matplotlib.use("Agg") |
| import matplotlib.pyplot as plt |
| import numpy as np |
|
|
|
|
| def _hash_color(uid: int): |
| h = (int(uid) * 1103515245 + 12345) & 0x7FFFFFFF |
| r = ((h >> 0) & 255) / 255.0 |
| g = ((h >> 8) & 255) / 255.0 |
| b = ((h >> 16) & 255) / 255.0 |
| return r, g, b |
|
|
|
|
| def _class_color(cid: int, n_cls: int = 13): |
| t = (int(cid) % max(n_cls, 1)) / max(n_cls, 1) |
| return ( |
| 0.5 + 0.5 * math.cos(2 * math.pi * t), |
| 0.5 + 0.5 * math.cos(2 * math.pi * t + 2.09), |
| 0.5 + 0.5 * math.cos(2 * math.pi * t + 4.18), |
| ) |
|
|
|
|
| def _labels_to_rgb(labels: np.ndarray, mode: str) -> np.ndarray: |
| labels = labels.reshape(-1).astype(np.int64) |
| n = labels.shape[0] |
| rgb = np.zeros((n, 3), dtype=np.float64) |
| for lid in np.unique(labels): |
| if mode == "superpoint": |
| c = _hash_color(int(lid)) |
| else: |
| c = _class_color(int(lid)) |
| m = labels == lid |
| rgb[m] = c |
| return rgb |
|
|
|
|
| def _subsample(coord: np.ndarray, labels: np.ndarray, max_points: int, seed: int): |
| n = coord.shape[0] |
| if n <= max_points: |
| return coord, labels, np.arange(n) |
| rng = np.random.default_rng(seed) |
| idx = rng.choice(n, size=max_points, replace=False) |
| return coord[idx], labels[idx], idx |
|
|
|
|
| def render_png( |
| coord: np.ndarray, |
| rgb: np.ndarray, |
| out_path: Path, |
| title: str, |
| elev: float = 20.0, |
| azim: float = -60.0, |
| point_size: float = 0.15, |
| ): |
| fig = plt.figure(figsize=(12, 10), dpi=150) |
| ax = fig.add_subplot(111, projection="3d") |
| ax.scatter( |
| coord[:, 0], |
| coord[:, 1], |
| coord[:, 2], |
| c=rgb, |
| s=point_size, |
| linewidths=0, |
| alpha=0.85, |
| depthshade=False, |
| ) |
| ax.set_title(title, fontsize=12) |
| ax.set_xlabel("X") |
| ax.set_ylabel("Y") |
| ax.set_zlabel("Z") |
| try: |
| ax.set_box_aspect( |
| ( |
| float(coord[:, 0].ptp()), |
| float(coord[:, 1].ptp()), |
| float(coord[:, 2].ptp()), |
| ) |
| ) |
| except Exception: |
| pass |
| ax.view_init(elev=elev, azim=azim) |
| ax.set_axis_off() |
| plt.tight_layout() |
| out_path.parent.mkdir(parents=True, exist_ok=True) |
| fig.savefig(out_path, bbox_inches="tight", facecolor="white") |
| plt.close(fig) |
|
|
|
|
| def main(): |
| root = Path(__file__).resolve().parent |
| default_data = ( |
| root.parent |
| / "_work_biptv3" |
| / "pointcept_framework" |
| / "data" |
| / "s3dis_official" |
| ) |
| default_out = root / "outputs" / "superpoint_vis" |
|
|
| ap = argparse.ArgumentParser() |
| ap.add_argument("--data_root", type=Path, default=default_data) |
| ap.add_argument("--room", type=str, default="Area_1/office_1") |
| ap.add_argument("--out_dir", type=Path, default=default_out) |
| ap.add_argument("--max_points", type=int, default=120000) |
| ap.add_argument("--seed", type=int, default=0) |
| ap.add_argument("--elev", type=float, default=22.0) |
| ap.add_argument("--azim", type=float, default=-58.0) |
| args = ap.parse_args() |
|
|
| room_dir = args.data_root / args.room |
| coord = np.load(room_dir / "coord.npy") |
| sp = np.load(room_dir / "superpoint.npy").reshape(-1) |
| seg = np.load(room_dir / "segment.npy").reshape(-1) |
| assert len(sp) == len(coord) == len(seg) |
|
|
| room_tag = args.room.replace("/", "_") |
| out_dir = args.out_dir.resolve() |
|
|
| |
| c1, l1, _ = _subsample(coord, sp, args.max_points, args.seed) |
| rgb_sp = _labels_to_rgb(l1, "superpoint") |
| path_sp = out_dir / f"{room_tag}_superpoint.png" |
| render_png( |
| c1, |
| rgb_sp, |
| path_sp, |
| title="Superpoint ids (geometry, subsampled)", |
| elev=args.elev, |
| azim=args.azim, |
| ) |
|
|
| |
| c2, l2, _ = _subsample(coord, seg, args.max_points, args.seed + 1) |
| rgb_seg = _labels_to_rgb(l2, "segment") |
| path_seg = out_dir / f"{room_tag}_segment_gt.png" |
| render_png( |
| c2, |
| rgb_seg, |
| path_seg, |
| title="Semantic GT (segment.npy, subsampled)", |
| elev=args.elev, |
| azim=args.azim, |
| ) |
|
|
| print("WROTE_SUPERPOINT", str(path_sp)) |
| print("WROTE_SEGMENT", str(path_seg)) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|