biptv3 / code /superpoint_ops /batch_gen_superpoints_mp.py
YYYYYYUUU's picture
Add core reproduction code (binarization layers, PTv3, superpoint ops, min-repro pack)
7b95dc2 verified
Raw
History Blame Contribute Delete
4.99 kB
"""Batch generate pycut superpoint.npy for all S3DIS rooms (multiprocessing)."""
import os
import sys
import time
import numpy as np
from scipy.spatial import cKDTree
from multiprocessing import Pool, cpu_count
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LIBCP_DIR = os.path.join(SCRIPT_DIR, "_cut_pursuit", "build", "src")
sys.path.insert(0, LIBCP_DIR)
sys.path.insert(0, SCRIPT_DIR)
import libcp
from lib_geo import (
_normalize_xyz_np,
_normalize_normals_np,
_local_geom_features_chunked_np,
_build_adj_graph_np,
_edge_weights_chunked_np,
_relabel_contiguous_np,
)
S3DIS_ROOT = "/mnt/data/AODUOLI/_work_biptv3/pointcept_framework/data/s3dis_official"
OUT_ROOT = os.path.join(SCRIPT_DIR, "outputs", "superpoint_pycut_all")
def merge_small_components(xyz, labels, min_size=50):
counts = np.bincount(labels)
small_mask = counts[labels] < min_size
if not small_mask.any():
return labels
labels = labels.copy()
large_mask = ~small_mask
if not large_mask.any():
return labels
tree = cKDTree(xyz[large_mask])
_, nn_idx = tree.query(xyz[small_mask], k=1)
large_indices = np.where(large_mask)[0]
labels[small_mask] = labels[large_indices[nn_idx]]
return _relabel_contiguous_np(labels)
def generate_superpoints_pycut(
xyz, normals=None,
k_feat=10, k_adj=10, chunk_size=8192,
normal_scale=0.25, lam=0.03, sigma=0.5,
min_comp_weight=20, weight_decay=0.7,
merge_min_size=50,
):
xyz_norm = _normalize_xyz_np(xyz)
geom_feat = _local_geom_features_chunked_np(xyz_norm, k_feat=k_feat, chunk_size=chunk_size)
feat_parts = [geom_feat]
if normals is not None:
nn = _normalize_normals_np(normals)
feat_parts.append(nn * normal_scale)
Y = np.hstack(feat_parts).astype(np.float32)
src, dst = _build_adj_graph_np(xyz_norm, k_adj=k_adj, mutual=False, undirected=True)
ew = _edge_weights_chunked_np(Y.T, src, dst, lam=1.0, sigma=sigma)
components, in_component = libcp.cutpursuit(
Y,
src.astype(np.uint32),
dst.astype(np.uint32),
ew.astype(np.float32),
float(lam),
int(min_comp_weight),
0,
float(weight_decay),
)
labels = _relabel_contiguous_np(np.asarray(in_component, dtype=np.int32))
if merge_min_size > 0:
labels = merge_small_components(xyz, labels, min_size=merge_min_size)
return labels
def process_room(args):
area, room = args
room_dir = os.path.join(S3DIS_ROOT, area, room)
coord_path = os.path.join(room_dir, "coord.npy")
normal_path = os.path.join(room_dir, "normal.npy")
if not os.path.exists(coord_path):
return (area, room, -1, 0, 0.0, "no coord.npy")
out_dir = os.path.join(OUT_ROOT, area, room)
os.makedirs(out_dir, exist_ok=True)
out_path = os.path.join(out_dir, "superpoint.npy")
if os.path.exists(out_path):
return (area, room, -1, 0, 0.0, "exists")
coord = np.load(coord_path).astype(np.float32)
normals = None
if os.path.exists(normal_path):
normals = np.load(normal_path).astype(np.float32)
t0 = time.time()
labels = generate_superpoints_pycut(
coord, normals=normals,
lam=0.03, sigma=0.5,
k_feat=10, k_adj=10,
merge_min_size=50,
)
dt = time.time() - t0
n_sp = int(labels.max()) + 1
np.save(out_path, labels)
return (area, room, coord.shape[0], n_sp, dt, "done")
if __name__ == "__main__":
os.makedirs(OUT_ROOT, exist_ok=True)
tasks = []
areas = sorted([d for d in os.listdir(S3DIS_ROOT) if d.startswith("Area_")])
for area in areas:
area_dir = os.path.join(S3DIS_ROOT, area)
rooms = sorted([r for r in os.listdir(area_dir)
if os.path.isdir(os.path.join(area_dir, r))])
for room in rooms:
tasks.append((area, room))
total = len(tasks)
pending = []
for area, room in tasks:
out_path = os.path.join(OUT_ROOT, area, room, "superpoint.npy")
if not os.path.exists(out_path):
pending.append((area, room))
print(f"Total: {total} rooms, already done: {total - len(pending)}, pending: {len(pending)}")
n_workers = min(12, cpu_count())
print(f"Using {n_workers} workers")
t_global = time.time()
done_count = 0
with Pool(n_workers) as pool:
for result in pool.imap_unordered(process_room, pending):
done_count += 1
area, room, n_pts, n_sp, dt, status = result
if status == "done":
print(f"[{done_count}/{len(pending)}] {area}/{room}: {n_pts} pts -> {n_sp} sp ({dt:.1f}s)")
elif status == "exists":
print(f"[{done_count}/{len(pending)}] {area}/{room}: EXISTS")
else:
print(f"[{done_count}/{len(pending)}] {area}/{room}: SKIP ({status})")
sys.stdout.flush()
print(f"\nDone! Total time: {time.time() - t_global:.0f}s")