v12_gtmask — final report (2026-07-10)

Headline: v12_gtmask step 3000 bakes the GT off-frame fix into training itself. Result: 116 mm abs MPJPE / 34 px pixel err / 0.27 depth log-res on ARCTIC-lite (40 clips) — surpasses v11 + eval-time filter (124 mm / 39 px) and closes 48 % / 90 % of the gap to the Iter 206 oracle (54 mm / 0 px).


What v12 changed

v11: trained with hand_valid = URDS present flag (a world-3D flag). Learned to regress hand poses even when the GT wrist projected off-frame, because those "valid" GT joints were still supervising the model. Predicted 2D pixels landed in-frame; the resulting per-frame supervision was noise.

v12_gtmask: hand_valid is now present AND (GT wrist projects inside [-20, W+20] × [-20, H+20]). The 5-11 % of formerly-"valid" frames where GT is off-camera no longer supervise. On the worst-100 arctic clips, 21-58 % of frames were mis-labeled; those are all now excluded from training loss.

Everything else (init from v10 s3000, w_pix=10, w_s=2, LR schedule) is identical to v11.


v12 training arc — same-split ARCTIC-lite eval, N=40

Step abs (mm) rr (mm) PA (mm) wrist (mm) pixel (px) depth log-res
500 122 82 14 75 41 0.31
1000 119 82 14 72 38 0.31
1500 116 80 14 72 38 0.29
2000 119 80 14 73 36 0.27
2500 116 80 14 72 36 0.28
3000 (FINAL) 🥇 116 78 14 72 34 0.27

Step 3000 is the first ckpt to be the leader (or tied) on every metric simultaneously.


v12 vs v11 — same eval, no filter

Metric v11 s3000 v12 s3000 Δ
abs MPJPE 149 mm 116 mm −22 %
rr MPJPE 87 mm 78 mm −10 %
PA MPJPE 14 mm 14 mm 0 %
wrist trans 103 mm 72 mm −30 %
pixel err 93 px 34 px −63 %
depth log-res 0.28 0.27 −3 %

v12 vs (v11 + eval-time GT filter)

Metric v11 + filter v12 s3000 Δ
abs MPJPE 124 mm 116 mm −6 %
pixel err 39 px 34 px −13 %

The eval-time filter removed 11 % of frames from the metric. v12 removes those same frames from training, and additionally learns better on the remaining frames — hence beats the filter on the same eval set.


What v12 didn't fix


Iter progression — the 12h arc, condensed

v9b (baseline)                       222 mm abs / 327 px pixel
  ↓ v10_scale — full 7-head training
v10 s3000 (FINAL)                    159 mm / 105 px
  ↓ v11_pushpix — w_pix=10, lower LR
v11 s3000 (FINAL)                    149 mm / 93 px
  ↓ diagnose per-source / best-worst / depth (findings: none)
  ↓ discover GT off-frame mislabel → apply as eval filter
v11 + eval-time filter               124 mm / 39 px  (−17% abs, −58% pixel!)
  ↓ push the fix into training: hand_valid = present AND in-frame
v12_gtmask s3000 (FINAL)             116 mm / 34 px  🏆

Total: v9b → v12 = 222 → 116 mm (−48 %) and 327 → 34 px (−90 %).


What's next

The Iter 206 oracle at 54 mm remains 62 mm out of reach. From this floor, the levers are (in expected impact order):

  1. 6D-rot MANO orient (v13, ~200 LoC). Fixes rodrigues-MSE mean collapse. Estimated −5 to −40 mm on abs — depends on how much of the 62 mm residual is orientation vs. depth.
  2. Multi-resolution root_head (v14, ~300 LoC). Coarse-to-fine at higher resolution. Estimated pixel err −20 to −34 px (to Somantis territory).
  3. Data pool: drop HO3Dv3 (data engine, ~30 LoC). Per-source finding showed HO3Dv3 is worse than any other source (255 mm vs 116 mm). Iter 204 flagged it as noise. Estimated −5 to −15 mm on abs.
  4. VGGT point_head reuse (v15, ~500 LoC). Bigger + pretrained depth head. Estimated depth log-res 0.27 → 0.15-0.20.

Reproducibility

# 1. Train v12_gtmask (~3 hours on H200)
python data_engine/train_egowm_v10_scale.py \
    --out _ckpt/lane_b/v12_gtmask \
    --init-from _ckpt/lane_b/v10_scale/ckpt_step003000.pt \
    --steps 3000 --lr-heads 1e-4 --lr-trunk 5e-6 \
    --w-pix 10.0 --w-s 2.0

# The fix is inside data_engine/lane_b_dataset.py.LaneBCachedDataset:
#   hand_valid[t, hi] = base_valid AND (GT projects in-frame with 20 px margin)

# 2. Eval v12 s3000 (baseline eval; no additional filter needed)
python data_engine/eval_v10_scale.py \
    --ckpt _ckpt/lane_b/v12_gtmask/ckpt_step003000.pt \
    --n-clips 40 --source-filter arctic \
    --out benchmark/results/v12_gtmask_step3000.arctic-lite.json

# 3. Rebuild leaderboard + report
python benchmark/scripts/leaderboard.py
python report/build.py

Meta