Add learned + classical pipeline ensemble (LOCAL only, push tomorrow)
Browse filesAfter hybrid_merge, also run predict_wireframe_sklearn (the full
classical pipeline with line cloud, winner candidates, sklearn edge
classifier, track ensemble) and union its output with the
learned-pipeline output via ensemble_merge.
ensemble_merge:
- Vertex union with close-pair merging (0.4m radius via cKDTree).
- Edge union after re-mapping classical edge endpoints onto merged
vertex indices, deduplicating undirected edge pairs.
- Robust to NaN/Inf vertices from the classical side.
Wrapped in try/except so a sklearn-pipeline failure on any sample
falls back to the existing learned + hybrid_merge output. Independent
error modes between the two pipelines should complement each other
(semantic 2D detection + line cloud + sklearn classifier vs learned 3D
segment regression).
DO NOT PUSH today — daily submission slot already used (commit 56f1ec6
scored 0.4584). Push tomorrow when budget refreshes.
|
@@ -307,6 +307,71 @@ def hybrid_merge(pred_v, pred_e, track_v, track_e, merge_radius=0.8):
|
|
| 307 |
|
| 308 |
return np.array(final_v), final_e
|
| 309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
# ---------------------------------------------------------------------------
|
| 311 |
# Main
|
| 312 |
# ---------------------------------------------------------------------------
|
|
@@ -397,11 +462,27 @@ if __name__ == "__main__":
|
|
| 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()}")
|
|
|
|
| 307 |
|
| 308 |
return np.array(final_v), final_e
|
| 309 |
|
| 310 |
+
|
| 311 |
+
def ensemble_merge(v1, e1, v2, e2, vertex_merge_radius=0.4):
|
| 312 |
+
"""Merge two pipeline outputs (learned + classical) into a single wireframe.
|
| 313 |
+
|
| 314 |
+
Strategy: vertex union with merging of close pairs, edge union after
|
| 315 |
+
re-mapping classical-pipeline edge endpoints onto merged vertex indices.
|
| 316 |
+
|
| 317 |
+
Both inputs are expected to be (vertices ndarray/list, edges list-of-tuples).
|
| 318 |
+
"""
|
| 319 |
+
v1 = np.array(v1, dtype=np.float64) if isinstance(v1, list) else np.asarray(v1, dtype=np.float64)
|
| 320 |
+
v2 = np.array(v2, dtype=np.float64) if isinstance(v2, list) else np.asarray(v2, dtype=np.float64)
|
| 321 |
+
|
| 322 |
+
if v2.size == 0 or len(v2) == 0:
|
| 323 |
+
return v1, list(e1)
|
| 324 |
+
if v1.size == 0 or len(v1) == 0:
|
| 325 |
+
return v2, list(e2)
|
| 326 |
+
|
| 327 |
+
# Filter out non-finite vertices in v2
|
| 328 |
+
valid2 = np.isfinite(v2).all(axis=1)
|
| 329 |
+
if not valid2.all():
|
| 330 |
+
idx_map_2 = {int(old): new for new, old in enumerate(np.where(valid2)[0])}
|
| 331 |
+
v2 = v2[valid2]
|
| 332 |
+
e2 = [(idx_map_2[int(u)], idx_map_2[int(v)]) for u, v in e2
|
| 333 |
+
if int(u) in idx_map_2 and int(v) in idx_map_2]
|
| 334 |
+
|
| 335 |
+
if len(v2) == 0:
|
| 336 |
+
return v1, list(e1)
|
| 337 |
+
|
| 338 |
+
from scipy.spatial import cKDTree
|
| 339 |
+
tree = cKDTree(v1)
|
| 340 |
+
dists, indices = tree.query(v2, k=1)
|
| 341 |
+
|
| 342 |
+
v2_to_merged = {}
|
| 343 |
+
new_vertices = []
|
| 344 |
+
for i, (d, j) in enumerate(zip(dists, indices)):
|
| 345 |
+
if d <= vertex_merge_radius:
|
| 346 |
+
v2_to_merged[i] = int(j)
|
| 347 |
+
else:
|
| 348 |
+
v2_to_merged[i] = len(v1) + len(new_vertices)
|
| 349 |
+
new_vertices.append(v2[i])
|
| 350 |
+
|
| 351 |
+
final_v = np.vstack([v1, np.array(new_vertices, dtype=np.float64)]) if new_vertices else v1
|
| 352 |
+
final_e_set = set()
|
| 353 |
+
final_e = []
|
| 354 |
+
for u, v in e1:
|
| 355 |
+
u, v = int(u), int(v)
|
| 356 |
+
if u == v:
|
| 357 |
+
continue
|
| 358 |
+
e = (min(u, v), max(u, v))
|
| 359 |
+
if e not in final_e_set:
|
| 360 |
+
final_e_set.add(e)
|
| 361 |
+
final_e.append(e)
|
| 362 |
+
for u, v in e2:
|
| 363 |
+
u_m = v2_to_merged.get(int(u))
|
| 364 |
+
v_m = v2_to_merged.get(int(v))
|
| 365 |
+
if u_m is None or v_m is None or u_m == v_m:
|
| 366 |
+
continue
|
| 367 |
+
e = (min(u_m, v_m), max(u_m, v_m))
|
| 368 |
+
if e not in final_e_set:
|
| 369 |
+
final_e_set.add(e)
|
| 370 |
+
final_e.append(e)
|
| 371 |
+
|
| 372 |
+
return final_v, final_e
|
| 373 |
+
|
| 374 |
+
|
| 375 |
# ---------------------------------------------------------------------------
|
| 376 |
# Main
|
| 377 |
# ---------------------------------------------------------------------------
|
|
|
|
| 462 |
from triangulation import predict_wireframe_tracks
|
| 463 |
# Use min_views=3 for highly precise, conservative geometric tracks
|
| 464 |
track_v, track_e = predict_wireframe_tracks(sample, min_views=3)
|
| 465 |
+
|
| 466 |
pred_v, pred_e = hybrid_merge(pred_v, pred_e, track_v, track_e, merge_radius=0.8)
|
| 467 |
except Exception as track_e_err:
|
| 468 |
print(f" Track ensemble failed for {order_id}: {track_e_err}")
|
| 469 |
+
|
| 470 |
+
# Pipeline ensemble: also run the classical sklearn pipeline
|
| 471 |
+
# and union with the learned-pipeline output. Independent
|
| 472 |
+
# error modes (semantic 2D detection + line cloud + sklearn
|
| 473 |
+
# edge classifier) complement the learned 3D segment regression.
|
| 474 |
+
try:
|
| 475 |
+
from sklearn_submission import predict_wireframe_sklearn
|
| 476 |
+
skl_v, skl_e = predict_wireframe_sklearn(sample)
|
| 477 |
+
if isinstance(pred_v, np.ndarray) and len(pred_v) >= 1 and \
|
| 478 |
+
skl_v is not None and len(skl_v) >= 1:
|
| 479 |
+
pred_v, pred_e = ensemble_merge(
|
| 480 |
+
pred_v, pred_e, skl_v, skl_e,
|
| 481 |
+
vertex_merge_radius=0.4,
|
| 482 |
+
)
|
| 483 |
+
except Exception as ens_err:
|
| 484 |
+
print(f" Ensemble merge failed for {order_id}: {ens_err}")
|
| 485 |
+
|
| 486 |
except Exception as e:
|
| 487 |
import traceback
|
| 488 |
print(f" Predict failed for {order_id}:\n{traceback.format_exc()}")
|