Skip to content

Outlier-handling benchmark — better weighting, no rejection

Per-view PnP on a synthetic camera, median over 60 views, with a controlled fraction of corners corrupted by a 50 px gross shift.

Note

Measured at the per-view pose level, before any downstream bundle adjustment — so the front-end's own robustness is the whole story, not masked by an outer optimizer.

  • L2solvePnP over all corners, no robustness (naive baseline).
  • rejectRANSAC P3P
  • inlier refine (hard rejection, MC-Calib-style).
  • reweightrobust_pose_irls: RANSAC warm-start + redescending Cauchy IRLS (MAD auto-scale, GNC, studentized leverage) over every corner — no rejection.
outlier % L2 dR° L2 dt reject dR° reject dt reweight dR° reweight dt dR° reduction vs L2
0% 0.045 0.0015 0.045 0.0015 0.046 0.0015 -3.5%
5% 1.054 0.0359 0.046 0.0017 0.050 0.0017 95.2%
10% 164.051 3.9544 0.050 0.0018 0.051 0.0019 100.0%
20% 177.290 4.1124 0.048 0.0017 0.049 0.0017 100.0%
30% 177.837 4.1943 0.060 0.0019 0.059 0.0019 100.0%
40% 177.702 4.2501 0.059 0.0021 0.070 0.0023 100.0%

Mean rotation-error reduction vs naive L2 at ≥10 % outliers: 100.0% (PASS >50%) — by weighting, every corner is kept.

$ python scripts/benchmark_outliers.py
mean rotation-error reduction vs naive L2 at >=10% outliers: 100.0%  (PASS >50%)
self-masking leverage outlier — studentize cuts rotation error by 79.9% (0.820° -> 0.165°)
wrote docs/RIG_OUTLIER_BENCHMARK.md

Tip

The reject/reweight columns reproduce bit-for-bit run to run. The naive L2 blow-up values are the ill-conditioned regime itself — unweighted L2 pose recovery is chaotic once outliers dominate, landing anywhere in the 140–180° range across repeated runs rather than a single fixed number. That instability is the point being measured, not noise in the measurement.

Studentized leverage — the self-masking outlier a residual kernel cannot see

One far-off-axis corner with a modest mis-decode: leverage — how much sway a point's position gives it over the fit — lets it pull the pose while keeping its own residual small, so a residual-only kernel never down-weights it.

Studentizing the residual recovers it: median rotation error 0.820° → 0.165° (79.9% lower).


Source: this page is generated by scripts/benchmark_outliers.py — run it to reproduce this benchmark. Robust estimator: robust_pose_irls.