arissassina/kaggle-workspace / scripts /phase5_triangulate.py
arissassina's picture
download
raw
4.78 kB
import glob
import json
import os
import cv2
import numpy as np
ROOT = "/root/bdmc_pipeline"
H, W = 1920, 1080
fx = fy = H / (2 * np.tan(np.radians(72.0 / 2)))
K = np.array([[fx, 0, W / 2], [0, fy, H / 2], [0, 0, 1.0]])
Z_MIN, Z_MAX = 1.2, 9.0
RESID_TOL_M = 0.06
def unproj(xs, ys, z):
return np.stack([(xs - W / 2) * z / fx, (ys - H / 2) * z / fy, z], 1).astype(np.float64)
def kabsch_rigid(A, B):
ca, cb = A.mean(0), B.mean(0)
Hm = (A - ca).T @ (B - cb)
U, _, Vt = np.linalg.svd(Hm)
d = np.sign(np.linalg.det(Vt.T @ U.T))
D = np.diag([1, 1, d])
R = Vt.T @ D @ U.T
t = cb - R @ ca
return R, t
def rigid_motion(Xa, Xb):
for _ in range(3):
R, t = kabsch_rigid(Xa, Xb)
res = np.linalg.norm(Xa @ R.T + t - Xb, axis=1)
keep = res < RESID_TOL_M
if keep.sum() < 30:
break
Xa, Xb = Xa[keep], Xb[keep]
return R, t, float(np.median(res[keep])) if keep.sum() else 9e9
def dlt_multiview(K, Rs, ts, pts2d):
rows = []
for R_i, t_i, x in zip(Rs, ts, pts2d):
P = K @ np.hstack([R_i, t_i.reshape(3, 1)])
u, v = x
rows.append(u * P[2] - P[0])
rows.append(v * P[2] - P[1])
A = np.stack(rows)
_, _, vt = np.linalg.svd(A, full_matrices=False)
Xh = vt[-1]
return Xh[:3] / Xh[3]
results = {}
all_stats = []
for path in sorted(glob.glob(f"{ROOT}/outputs/tracks/win_*.npz")):
z = np.load(path)
tracks, vis = z["tracks"], z["vis"]
f0, n = int(z["start"]), int(z["n_frames"])
N = tracks.shape[1]
depths = []
masks = []
for i in range(f0, f0 + n):
dep = np.load(f"{ROOT}/depth/f_{i:04d}.npy").astype(np.float32)
m = cv2.imread(f"{ROOT}/masks/road_surface/f_{i:04d}.png", 0) > 127
depths.append(dep)
masks.append(m)
clouds = []
for i in range(n):
x = tracks[i, :, 0]
y = tracks[i, :, 1]
xi = np.clip(x.round().astype(int), 0, W - 1)
yi = np.clip(y.round().astype(int), 0, H - 1)
ok = (vis[i].reshape(-1).astype(bool)) & masks[i][yi, xi] & (depths[i][yi, xi] > Z_MIN) & (depths[i][yi, xi] < Z_MAX)
c = np.full((N, 3), np.nan)
c[ok] = unproj(x[ok], y[ok], depths[i][yi[ok], xi[ok]])
clouds.append(c)
Rs, ts = [np.eye(3)], [np.zeros(3)]
motion_res = []
for i in range(n - 1):
a, b = clouds[i], clouds[i + 1]
both = np.isfinite(a).all(1) & np.isfinite(b).all(1)
if both.sum() < 30:
Rs.append(Rs[-1].copy())
ts.append(ts[-1].copy())
continue
R, t, rmed = rigid_motion(a[both], b[both])
motion_res.append(rmed)
Rs.append(R @ Rs[-1])
ts.append(R @ ts[-1] + t)
world_pts, reproj_rmse, n_obs = [], [], []
for j in range(N):
obs_idx = [i for i in range(n) if vis[i, j] and np.isfinite(clouds[i][j]).all()]
if len(obs_idx) < 6:
continue
try:
Xw = dlt_multiview(K, [Rs[i] for i in obs_idx], [ts[i] for i in obs_idx],
[tracks[i, j] for i in obs_idx])
except Exception:
continue
errs = []
for i in obs_idx:
p = K @ (Rs[i] @ Xw + ts[i])
p = p[:2] / p[2]
errs.append(np.hypot(*(p - tracks[i, j])))
rmse = float(np.sqrt(np.mean(np.square(errs))))
if rmse < 12.0:
world_pts.append(Xw)
reproj_rmse.append(rmse)
n_obs.append(len(obs_idx))
results[f"win_{f0:04d}"] = {
"start": f0,
"n_frames": n,
"n_tracks_total": int(N),
"n_triangulated": len(world_pts),
"median_reproj_rmse_px": round(float(np.median(reproj_rmse)), 2) if reproj_rmse else None,
"median_motion_residual_cm": round(float(np.median(motion_res)) * 100, 2) if motion_res else None,
}
if world_pts:
np.savez_compressed(f"{ROOT}/outputs/tracks/triangulated_{f0:04d}.npz",
xyz=np.array(world_pts),
rmse=np.array(reproj_rmse),
n_obs=np.array(n_obs),
start=f0,
Rs=np.array(Rs), ts=np.array(ts))
all_stats.append(results[f"win_{f0:04d}"])
print(json.dumps(results[f"win_{f0:04d}"]), flush=True)
tot_t = sum(s["n_triangulated"] for s in all_stats)
tot_p = sum(s["n_tracks_total"] for s in all_stats)
summary = {"windows": all_stats,
"triangulation_yield": round(tot_t / max(tot_p, 1), 3),
"total_triangulated_points": tot_t}
with open(f"{ROOT}/outputs/phase5_triangulation.json", "w") as fj:
json.dump(summary, fj, indent=1)
print("Phase 5b triangulation complete | yield", summary["triangulation_yield"], flush=True)

Xet Storage Details

Size:
4.78 kB
·
Xet hash:
3dbe0e65697a80e0dac5a6a94f151448ab2a8b1d310c8b2d24184a46c436c802

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.