Revert: roll back COLMAP refine + junction + snap changes
Browse filesThe previous commit's changes regressed the leaderboard score:
- hss_mean: 0.4583 -> 0.4443 (-0.014)
- corner_f1: 0.5104 -> 0.5003 (-0.010)
- edge_iou: 0.4268 -> 0.4101 (-0.017)
Most likely culprits without local validation:
- COLMAP multi-view plane refinement: snapped some vertices onto wall
or ground planes instead of roof planes (corner_f1 hit).
- Junction constraints: the leaf_min_len=0.4m and collinear_cos=0.97
thresholds removed valid short edges and corner vertices (edge_iou hit).
- Loosened hybrid_merge admitted incorrect track edges between
existing predicted vertices.
Restore script.py and s23dr_2026_example/postprocess_v2.py to the state
from commit 9deb9e1 (the previous best, hss_mean=0.4583). Tomorrow we
can attempt smaller, targeted experiments one at a time.
- s23dr_2026_example/postprocess_v2.py +8 -33
- script.py +11 -64
|
@@ -6,7 +6,7 @@ def snap_to_point_cloud(vertices, xyz, class_id, snap_radius=0.5,
|
|
| 6 |
target_classes=None):
|
| 7 |
"""Snap vertices to nearby point cloud clusters of specific semantic classes."""
|
| 8 |
if target_classes is None:
|
| 9 |
-
target_classes = [1, 2
|
| 10 |
|
| 11 |
snapped = vertices.copy()
|
| 12 |
mask = np.isin(class_id, target_classes)
|
|
@@ -26,39 +26,14 @@ def snap_to_point_cloud(vertices, xyz, class_id, snap_radius=0.5,
|
|
| 26 |
|
| 27 |
|
| 28 |
def snap_horizontal(vertices, edges, max_slope=0.05):
|
| 29 |
-
"""Snap near-horizontal edges to be exactly horizontal.
|
| 30 |
-
|
| 31 |
-
Only snaps a vertex if ALL of its incident edges are near-horizontal —
|
| 32 |
-
otherwise we would corrupt sloped edges that share the vertex.
|
| 33 |
-
"""
|
| 34 |
verts = vertices.copy()
|
| 35 |
-
|
| 36 |
-
if n == 0 or len(edges) == 0:
|
| 37 |
-
return verts
|
| 38 |
-
|
| 39 |
-
edge_is_h = []
|
| 40 |
-
incident = [[] for _ in range(n)]
|
| 41 |
-
for ei, (a, b) in enumerate(edges):
|
| 42 |
a, b = int(a), int(b)
|
| 43 |
dy = abs(verts[a, 1] - verts[b, 1])
|
| 44 |
-
dxz = np.sqrt((verts[a, 0] - verts[b, 0])
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
purely_h = [
|
| 51 |
-
len(incident[v]) > 0 and all(edge_is_h[ei] for ei in incident[v])
|
| 52 |
-
for v in range(n)
|
| 53 |
-
]
|
| 54 |
-
|
| 55 |
-
for ei, (a, b) in enumerate(edges):
|
| 56 |
-
if not edge_is_h[ei]:
|
| 57 |
-
continue
|
| 58 |
-
a, b = int(a), int(b)
|
| 59 |
-
if not (purely_h[a] and purely_h[b]):
|
| 60 |
-
continue
|
| 61 |
-
avg_y = 0.5 * (verts[a, 1] + verts[b, 1])
|
| 62 |
-
verts[a, 1] = avg_y
|
| 63 |
-
verts[b, 1] = avg_y
|
| 64 |
return verts
|
|
|
|
| 6 |
target_classes=None):
|
| 7 |
"""Snap vertices to nearby point cloud clusters of specific semantic classes."""
|
| 8 |
if target_classes is None:
|
| 9 |
+
target_classes = [1, 2] # apex, eave_end_point
|
| 10 |
|
| 11 |
snapped = vertices.copy()
|
| 12 |
mask = np.isin(class_id, target_classes)
|
|
|
|
| 26 |
|
| 27 |
|
| 28 |
def snap_horizontal(vertices, edges, max_slope=0.05):
|
| 29 |
+
"""Snap near-horizontal edges to be exactly horizontal."""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
verts = vertices.copy()
|
| 31 |
+
for a, b in edges:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
a, b = int(a), int(b)
|
| 33 |
dy = abs(verts[a, 1] - verts[b, 1])
|
| 34 |
+
dxz = np.sqrt((verts[a, 0] - verts[b, 0])**2 + (verts[a, 2] - verts[b, 2])**2)
|
| 35 |
+
if dxz > 0.1 and dy / dxz < max_slope:
|
| 36 |
+
avg_y = 0.5 * (verts[a, 1] + verts[b, 1])
|
| 37 |
+
verts[a, 1] = avg_y
|
| 38 |
+
verts[b, 1] = avg_y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
return verts
|
|
@@ -293,38 +293,18 @@ def hybrid_merge(pred_v, pred_e, track_v, track_e, merge_radius=0.8):
|
|
| 293 |
for u, v in final_e:
|
| 294 |
existing_edges.add((min(u, v), max(u, v)))
|
| 295 |
|
| 296 |
-
# Tight-confidence radius for accepting a track edge between two
|
| 297 |
-
# already-existing predicted vertices (topology correction).
|
| 298 |
-
tight_radius = 0.3
|
| 299 |
-
|
| 300 |
for u_t, v_t in track_e:
|
| 301 |
u_f = track_to_final.get(u_t)
|
| 302 |
v_f = track_to_final.get(v_t)
|
| 303 |
-
if u_f is None
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
if u_is_new or v_is_new:
|
| 313 |
-
# New vertex: trust the track edge.
|
| 314 |
-
final_e.append(e)
|
| 315 |
-
existing_edges.add(e)
|
| 316 |
-
continue
|
| 317 |
-
|
| 318 |
-
# Both endpoints are existing predicted vertices — only accept the
|
| 319 |
-
# track edge as a topology correction when both are very confidently
|
| 320 |
-
# matched (tight distance). This recovers missing edges without
|
| 321 |
-
# letting the tracker rewire the learned topology.
|
| 322 |
-
d_u = dists[u_t] if u_t < len(dists) else np.inf
|
| 323 |
-
d_v = dists[v_t] if v_t < len(dists) else np.inf
|
| 324 |
-
if d_u <= tight_radius and d_v <= tight_radius:
|
| 325 |
-
final_e.append(e)
|
| 326 |
-
existing_edges.add(e)
|
| 327 |
-
|
| 328 |
return np.array(final_v), final_e
|
| 329 |
|
| 330 |
# ---------------------------------------------------------------------------
|
|
@@ -417,44 +397,11 @@ if __name__ == "__main__":
|
|
| 417 |
from triangulation import predict_wireframe_tracks
|
| 418 |
# Use min_views=3 for highly precise, conservative geometric tracks
|
| 419 |
track_v, track_e = predict_wireframe_tracks(sample, min_views=3)
|
| 420 |
-
|
| 421 |
pred_v, pred_e = hybrid_merge(pred_v, pred_e, track_v, track_e, merge_radius=0.8)
|
| 422 |
except Exception as track_e_err:
|
| 423 |
print(f" Track ensemble failed for {order_id}: {track_e_err}")
|
| 424 |
-
|
| 425 |
-
# COLMAP multi-view plane refinement: snaps each vertex to the
|
| 426 |
-
# roof plane visible at its 2D projection. Targets corner_f1.
|
| 427 |
-
try:
|
| 428 |
-
if isinstance(pred_v, np.ndarray) and len(pred_v) >= 1:
|
| 429 |
-
from colmap_refine import refine_vertices_multiview_plane
|
| 430 |
-
refined, snapped_mask = refine_vertices_multiview_plane(
|
| 431 |
-
pred_v, sample,
|
| 432 |
-
knn_2d_px=30.0, knn_k=12, min_neighbours=6,
|
| 433 |
-
max_displacement=0.5, min_quality=0.5, min_views=2,
|
| 434 |
-
)
|
| 435 |
-
if snapped_mask.any():
|
| 436 |
-
pred_v = refined
|
| 437 |
-
except Exception as refine_err:
|
| 438 |
-
print(f" COLMAP refine failed for {order_id}: {refine_err}")
|
| 439 |
-
|
| 440 |
-
# Junction constraints: drop collinear/duplicate/short-leaf edges
|
| 441 |
-
try:
|
| 442 |
-
if isinstance(pred_v, np.ndarray) and len(pred_v) >= 2 and len(pred_e) >= 1:
|
| 443 |
-
from junction import apply_junction_constraints
|
| 444 |
-
jv, je = apply_junction_constraints(
|
| 445 |
-
pred_v, pred_e,
|
| 446 |
-
collinear_cos=0.97,
|
| 447 |
-
duplicate_cos=0.985,
|
| 448 |
-
leaf_min_len=0.4,
|
| 449 |
-
max_passes=3,
|
| 450 |
-
)
|
| 451 |
-
# Only accept if at least one edge survives — never let
|
| 452 |
-
# the post-processing reduce us to an empty graph.
|
| 453 |
-
if len(je) >= 1:
|
| 454 |
-
pred_v, pred_e = jv, je
|
| 455 |
-
except Exception as junc_err:
|
| 456 |
-
print(f" Junction constraints failed for {order_id}: {junc_err}")
|
| 457 |
-
|
| 458 |
except Exception as e:
|
| 459 |
import traceback
|
| 460 |
print(f" Predict failed for {order_id}:\n{traceback.format_exc()}")
|
|
|
|
| 293 |
for u, v in final_e:
|
| 294 |
existing_edges.add((min(u, v), max(u, v)))
|
| 295 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
for u_t, v_t in track_e:
|
| 297 |
u_f = track_to_final.get(u_t)
|
| 298 |
v_f = track_to_final.get(v_t)
|
| 299 |
+
if u_f is not None and v_f is not None and u_f != v_f:
|
| 300 |
+
e = (min(u_f, v_f), max(u_f, v_f))
|
| 301 |
+
if e not in existing_edges:
|
| 302 |
+
# ONLY append the tracked edge if it connects to a NEWLY DISCOVERED vertex.
|
| 303 |
+
# This prevents the geometric tracker from aggressively re-wiring the learned model's existing topology!
|
| 304 |
+
if u_f >= len(pred_v) or v_f >= len(pred_v):
|
| 305 |
+
final_e.append(e)
|
| 306 |
+
existing_edges.add(e)
|
| 307 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
return np.array(final_v), final_e
|
| 309 |
|
| 310 |
# ---------------------------------------------------------------------------
|
|
|
|
| 397 |
from triangulation import predict_wireframe_tracks
|
| 398 |
# Use min_views=3 for highly precise, conservative geometric tracks
|
| 399 |
track_v, track_e = predict_wireframe_tracks(sample, min_views=3)
|
| 400 |
+
|
| 401 |
pred_v, pred_e = hybrid_merge(pred_v, pred_e, track_v, track_e, merge_radius=0.8)
|
| 402 |
except Exception as track_e_err:
|
| 403 |
print(f" Track ensemble failed for {order_id}: {track_e_err}")
|
| 404 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
except Exception as e:
|
| 406 |
import traceback
|
| 407 |
print(f" Predict failed for {order_id}:\n{traceback.format_exc()}")
|