Add SUM Parts reproduction and Seosan Myeongcheon application pipeline
Reproduces the SUM Parts (CVPR 2025) face-labeling benchmark on a single consumer GPU, then applies it to drone-photogrammetry road survey meshes. Verified on RTX 3060 12GB / WSL2 Ubuntu 22.04 / CUDA 11.8 / torch 2.0.1: - CUDA extensions build (pointnet2_batch, pointops, chamfer_dist, emd, subsampling) - PointNet 100 epochs reaches mIoU 17.19, matching the paper's reported 15.1 - OBJ -> PLY conversion round-trips through the model and yields per-point predictions Four upstream source patches, all idempotent, originals preserved: - numpy aliases removed in 1.24 (np.long etc.) and collections ABCs moved in python 3.10 - the blind test split ships label = -1, which crashed ConfusionMatrix - mode=val referenced `epoch` before assignment Documents the traps that cost the most time, including VRAM overflow silently falling back to host RAM on WSL2 (25-100x slowdown, no OOM) and the colour scale mismatch between r/g/b float32 and red/green/blue uint8. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Stream an OBJ and report the numbers that decide how to tile it for SUM Parts.
|
||||
|
||||
Reads line by line so a multi-GB mesh does not have to fit in RAM. Reports
|
||||
vertex/face counts, the bounding box, whether UVs and vertex colours are
|
||||
present, and the material/texture references.
|
||||
|
||||
Usage:
|
||||
python inspect_obj.py path/to/Block.obj [more.obj ...]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def inspect(path: Path) -> dict:
|
||||
n_v = n_vt = n_vn = n_f = 0
|
||||
has_vertex_color = False
|
||||
mtllib: list[str] = []
|
||||
usemtl: set[str] = set()
|
||||
lo = [float("inf")] * 3
|
||||
hi = [float("-inf")] * 3
|
||||
|
||||
with path.open("r", encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
if line.startswith("v "):
|
||||
n_v += 1
|
||||
parts = line.split()
|
||||
x, y, z = float(parts[1]), float(parts[2]), float(parts[3])
|
||||
if x < lo[0]: lo[0] = x
|
||||
if y < lo[1]: lo[1] = y
|
||||
if z < lo[2]: lo[2] = z
|
||||
if x > hi[0]: hi[0] = x
|
||||
if y > hi[1]: hi[1] = y
|
||||
if z > hi[2]: hi[2] = z
|
||||
# "v x y z r g b" is how some exporters carry per-vertex colour
|
||||
if len(parts) >= 7:
|
||||
has_vertex_color = True
|
||||
elif line.startswith("vt "):
|
||||
n_vt += 1
|
||||
elif line.startswith("vn "):
|
||||
n_vn += 1
|
||||
elif line.startswith("f "):
|
||||
n_f += 1
|
||||
elif line.startswith("mtllib"):
|
||||
mtllib.append(line.split(maxsplit=1)[1].strip())
|
||||
elif line.startswith("usemtl"):
|
||||
usemtl.add(line.split(maxsplit=1)[1].strip())
|
||||
|
||||
return {
|
||||
"path": path,
|
||||
"size_mb": path.stat().st_size / 1024 / 1024,
|
||||
"n_v": n_v, "n_vt": n_vt, "n_vn": n_vn, "n_f": n_f,
|
||||
"vertex_color": has_vertex_color,
|
||||
"mtllib": mtllib, "usemtl": sorted(usemtl),
|
||||
"lo": lo, "hi": hi,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
raise SystemExit(2)
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
p = Path(arg)
|
||||
if not p.exists():
|
||||
print(f"{arg}: not found")
|
||||
continue
|
||||
|
||||
r = inspect(p)
|
||||
ext = [r["hi"][i] - r["lo"][i] for i in range(3)]
|
||||
area = ext[0] * ext[1]
|
||||
|
||||
print(f"=== {p.name} ({r['size_mb']:,.0f} MB) ===")
|
||||
print(f" vertices : {r['n_v']:>12,}")
|
||||
print(f" faces : {r['n_f']:>12,}")
|
||||
print(f" texcoords : {r['n_vt']:>12,} {'(UV present)' if r['n_vt'] else '(NO UV)'}")
|
||||
print(f" normals : {r['n_vn']:>12,}")
|
||||
print(f" vtx color : {r['vertex_color']}")
|
||||
print(f" mtllib : {r['mtllib']}")
|
||||
print(f" materials : {len(r['usemtl'])} -> {r['usemtl'][:5]}"
|
||||
f"{' ...' if len(r['usemtl']) > 5 else ''}")
|
||||
print(f" bbox min : {[round(v, 2) for v in r['lo']]}")
|
||||
print(f" bbox max : {[round(v, 2) for v in r['hi']]}")
|
||||
print(f" extent : {[round(v, 2) for v in ext]} (metres, local)")
|
||||
print(f" footprint : {area / 1e6:.3f} km^2")
|
||||
if area:
|
||||
print(f" density : {r['n_f'] / area:,.1f} faces/m^2")
|
||||
# SUM Parts tiles are ~252 m square; this is how many that footprint is
|
||||
print(f" ~252m tiles: {max(1, round(ext[0] / 252)) * max(1, round(ext[1] / 252))}")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user