File size: 4,519 Bytes
7b95dc2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | """Batch generate pycut superpoint.npy for all S3DIS rooms."""
import os
import sys
import time
import numpy as np
from scipy.spatial import cKDTree
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"
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,
):
n = xyz.shape[0]
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
if __name__ == "__main__":
out_root = os.path.join(SCRIPT_DIR, "outputs", "superpoint_pycut_all")
os.makedirs(out_root, exist_ok=True)
areas = sorted([d for d in os.listdir(S3DIS_ROOT) if d.startswith("Area_")])
total_rooms = 0
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))])
total_rooms += len(rooms)
print(f"Total: {total_rooms} rooms across {len(areas)} areas")
done = 0
t_global = time.time()
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:
done += 1
room_dir = os.path.join(area_dir, 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):
print(f"[{done}/{total_rooms}] SKIP {area}/{room}: no coord.npy")
continue
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):
print(f"[{done}/{total_rooms}] EXISTS {area}/{room}")
continue
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)
print(f"[{done}/{total_rooms}] {area}/{room}: {coord.shape[0]} pts -> {n_sp} sp ({dt:.1f}s)")
print(f"\nDone! Total time: {time.time() - t_global:.0f}s")
|