biptv3 / code /pointcept_framework /scripts /visualize_s3dis_superpoints.py
YYYYYYUUU's picture
Backup FULL local core code incl. libs/ CUDA ext + all configs
3499c27 verified
Raw
History Blame Contribute Delete
4.67 kB
#!/usr/bin/env python3
"""
S3DIS 超点可视化(编辑节点 / poplab)
数据侧 superpoint 来源(与预处理一致):
- 每个房间目录下的 superpoint.npy:与 coord.npy 等长的 int32,表示每个点所属超点 id。
- 生成逻辑(无外部分割器时):体素坐标 +(可选)法向粗分箱 -> np.unique 得到逆映射作为 id。
- 见 pointcept/datasets/preprocessing/s3dis/preprocess_s3dis.py 中 generate_superpoint_labels。
用法示例:
python scripts/visualize_s3dis_superpoints.py \\
--room Area_1/office_1 \\
--out /tmp/superpoints_vis.ply
python scripts/visualize_s3dis_superpoints.py --room Area_1/office_1 --recompute --voxel 0.12 --normal-bins 8
"""
from __future__ import annotations
import argparse
import os
from pathlib import Path
import numpy as np
def generate_superpoint_labels(coords, normals=None, voxel_size=0.12, normal_bins=8):
"""与 preprocess_s3dis.generate_superpoint_labels 一致的几何 fallback。"""
coords = np.asarray(coords, dtype=np.float32)
coord_min = coords.min(axis=0, keepdims=True)
voxel_coord = np.floor((coords - coord_min) / max(float(voxel_size), 1e-4)).astype(np.int64)
if normals is not None and len(normals) == len(coords):
normals = np.asarray(normals, dtype=np.float32)
normals = normals / (np.linalg.norm(normals, axis=1, keepdims=True) + 1e-8)
normal_q = np.floor((normals + 1.0) * 0.5 * normal_bins).astype(np.int64)
normal_q = np.clip(normal_q, 0, normal_bins)
tokens = np.concatenate([voxel_coord, normal_q], axis=1)
else:
tokens = voxel_coord
_, inverse = np.unique(tokens, axis=0, return_inverse=True)
return inverse.astype(np.int32)
def sp_ids_to_colors(sp: np.ndarray) -> np.ndarray:
"""稳定伪彩色:对每个 superpoint id 做哈希映射到 [0,1]^3。"""
sp = sp.astype(np.int64).reshape(-1)
n = len(sp)
colors = np.zeros((n, 3), dtype=np.float64)
for uid in np.unique(sp):
h = (int(uid) * 1103515245 + 12345) & 0x7FFFFFFF
r = ((h >> 0) & 255) / 255.0
g = ((h >> 8) & 255) / 255.0
b = ((h >> 16) & 255) / 255.0
colors[sp == uid] = (r, g, b)
return np.clip(colors, 0.0, 1.0)
def main():
ap = argparse.ArgumentParser()
ap.add_argument(
"--data_root",
type=Path,
default=Path(__file__).resolve().parents[1] / "data" / "s3dis_official",
help="S3DIS 处理根目录(含 Area_*/*/)",
)
ap.add_argument("--room", type=str, required=True, help="例如 Area_1/office_1")
ap.add_argument("--out", type=Path, default=Path("superpoints_vis.ply"))
ap.add_argument(
"--recompute",
action="store_true",
help="不读 superpoint.npy,按 generate_superpoint_labels 现场重算(用于对照)",
)
ap.add_argument("--voxel", type=float, default=0.12)
ap.add_argument("--normal-bins", type=int, default=8)
args = ap.parse_args()
room_dir = args.data_root / args.room
coord_p = room_dir / "coord.npy"
if not coord_p.is_file():
raise FileNotFoundError(coord_p)
coord = np.load(coord_p)
normal = None
npy_n = room_dir / "normal.npy"
if npy_n.is_file():
normal = np.load(npy_n)
if args.recompute:
sp = generate_superpoint_labels(
coord, normals=normal, voxel_size=args.voxel, normal_bins=args.normal_bins
)
tag = "recomputed"
else:
sp_p = room_dir / "superpoint.npy"
if not sp_p.is_file():
raise FileNotFoundError(
f"{sp_p} 不存在,可先跑预处理加 --generate_superpoint,或改用 --recompute"
)
sp = np.load(sp_p).reshape(-1).astype(np.int32)
tag = "disk"
if len(sp) != len(coord):
raise ValueError(f"superpoint 长度 {len(sp)} != coord 长度 {len(coord)}")
colors = sp_ids_to_colors(sp)
try:
import open3d as o3d
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(coord.astype(np.float64))
pcd.colors = o3d.utility.Vector3dVector(colors)
args.out.parent.mkdir(parents=True, exist_ok=True)
o3d.io.write_point_cloud(str(args.out), pcd)
print(f"[{tag}] wrote {args.out} points={len(coord)} unique_superpoints={len(np.unique(sp))}")
print("若在本机有显示器,可取消注释 o3d.visualization.draw_geometries([pcd])")
# o3d.visualization.draw_geometries([pcd])
except ImportError:
print("未安装 open3d,无法写 PLY;请: pip install open3d")
if __name__ == "__main__":
main()