[Bug]: negative robust_scale_sq yields NaN-parameterised robust loss in refine_pose_lm
#1,117 opened on Aug 19, 2026
Repository metrics
- Stars
- (675 stars)
- PR merge metrics
- (Avg merge 3d 9h) (39 merged PRs in 30d)
Description
🐛 Describe the bug
build_robust_loss in crates/kornia-3d/src/pnp/refine.rs (added in #1111) guards only against non-finite robust_scale_sq, not against negative values. A negative-but-finite scale produces a HuberLoss/CauchyLoss with a NaN parameter, which silently poisons the normal equations for every factor in the solve.
if params.robust == RobustKernelKind::Identity || !params.robust_scale_sq.is_finite() {
return Ok(None);
}
let delta = params.robust_scale_sq.sqrt();
With robust_scale_sq = -25.0:
is_finite()istrue, so the early return is skipped.delta = (-25.0f32).sqrt()=NaN.HuberLoss::new(delta)rejects onlydelta <= 0.0, andNaN <= 0.0isfalse— so it returnsOk(HuberLoss { delta: NaN }).HuberLoss::weight()takes theelsebranch (squared_norm <= NaNisfalse) and returnsself.delta / squared_norm.sqrt()=NaN.linear_system.rsscales every residual and Jacobian block bysqrt(weight)=NaN, soJ^T JandJ^T rare entirelyNaNand the pose result is garbage — with no error returned to the caller.
crates/kornia-3d/src/ba.rs already gets this right and checks both conditions:
if !params.robust_scale_sq.is_finite() || params.robust_scale_sq <= 0.0 {
return None;
}
🔄 Steps to Reproduce
1. Call refine_pose_lm with LMRefineParams { robust: RobustKernelKind::Huber, robust_scale_sq: -25.0, ..Default::default() }
2. Observe the returned rotation/translation contain NaN, with Ok(_) status
💻 Minimal Code Example
let params = LMRefineParams {
robust: RobustKernelKind::Huber,
robust_scale_sq: -25.0, // negative, finite -> NaN delta
..LMRefineParams::default()
};
let result = refine_pose_lm(&pts_w, &pts_i, &k, &r_init, &t_init, None, ¶ms)?;
assert!(result.translation.x.is_nan()); // currently passes
✅ Expected behaviour
A non-positive robust_scale_sq should either fall back to plain L2 (matching ba.rs) or be reported as an invalid-parameter error. It should never produce a NaN-parameterised loss.
🔧 Suggested fix
One extra clause, mirroring ba.rs (NaN is already covered by is_finite):
if params.robust == RobustKernelKind::Identity
|| !params.robust_scale_sq.is_finite()
|| params.robust_scale_sq <= 0.0
{
return Ok(None);
}
Worth a regression test asserting a finite result for negative/zero scales, and ideally the same guard audited in ba_schur.rs and pose/lm_pose.rs.
📎 Context
Introduced by #1111 (Huber PnP refine).