Never crash: trackers and camera solver without expect()
The camera solver, KLT pyramid and subspace (warp stabilizer) tracker expect()ed data-structure invariants: a track point seen in a frame, a solved camera, a pyramid level. A missing value now skips that point/track (or, for finish(), returns a SolveError: "no frame was solved") instead of panicking. Denies the panic lints in the crate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
10644a83aa
commit
bf8962becf
|
|
@ -0,0 +1,4 @@
|
|||
allow-unwrap-in-tests = true
|
||||
allow-expect-in-tests = true
|
||||
allow-panic-in-tests = true
|
||||
allow-indexing-slicing-in-tests = true
|
||||
|
|
@ -113,7 +113,7 @@ fn keyframes(d: &Data) -> Vec<usize> {
|
|||
j += 1;
|
||||
}
|
||||
if let Some(lastf) = (0..d.n).rev().find(|k| d.by_frame[*k].len() >= 8)
|
||||
&& *kf.last().expect("one") < lastf
|
||||
&& kf.last().is_some_and(|l| *l < lastf)
|
||||
{
|
||||
kf.push(lastf);
|
||||
}
|
||||
|
|
@ -237,10 +237,10 @@ impl Recon {
|
|||
.iter()
|
||||
.map(|k| {
|
||||
let c = &self.cams[k];
|
||||
let uv = d.at(*ti, *k).expect("seen");
|
||||
(c.pose, [uv[0] / c.f, uv[1] / c.f], c)
|
||||
let uv = d.at(*ti, *k)?;
|
||||
Some((c.pose, [uv[0] / c.f, uv[1] / c.f], c))
|
||||
})
|
||||
.collect();
|
||||
.collect::<Option<_>>()?;
|
||||
let x = match kind {
|
||||
Kind::Tripod => normalize(views.iter().fold([0.0; 3], |a, (p, o, _)| add(a, normalize(mtv(&p.r, [o[0], o[1], 1.0]))))),
|
||||
Kind::General => {
|
||||
|
|
@ -255,7 +255,7 @@ impl Recon {
|
|||
}
|
||||
};
|
||||
for (k, (_, _, c)) in ks.iter().zip(&views) {
|
||||
let uv = d.at(*ti, *k).expect("seen");
|
||||
let uv = d.at(*ti, *k)?;
|
||||
let r = bundle::residual(c, x, uv)?;
|
||||
if r[0] * r[0] + r[1] * r[1] > thr2 {
|
||||
return None;
|
||||
|
|
@ -675,7 +675,7 @@ fn undistorted(tracks: &CameraTracks, dist: &Distortion) -> CameraTracks {
|
|||
pub fn solve(tracks: &CameraTracks, s: &SolveSettings, cancel: Option<&AtomicBool>) -> Result<CameraSolve, SolveError> {
|
||||
if !s.lens_distortion {
|
||||
let c = solve_core(tracks, s, cancel)?;
|
||||
return Ok(finish(&c.d, tracks, s, c.r, c.method, c.kind));
|
||||
return finish(&c.d, tracks, s, c.r, c.method, c.kind);
|
||||
}
|
||||
// Lens distortion: solve, adjust (k1, k2) jointly on the raw tracks, then solve again on
|
||||
// tracks undistorted with the estimate and adjust jointly once more.
|
||||
|
|
@ -734,7 +734,7 @@ pub fn solve(tracks: &CameraTracks, s: &SolveSettings, cancel: Option<&AtomicBoo
|
|||
if debug() {
|
||||
eprintln!("lens distortion pass {pass}: k1 {:.5} k2 {:.5}", dist.k1, dist.k2);
|
||||
}
|
||||
let mut sol = finish(&d, tracks, s, r, c.method, c.kind);
|
||||
let mut sol = finish(&d, tracks, s, r, c.method, c.kind)?;
|
||||
sol.distortion = Some(dist);
|
||||
out = Some(sol);
|
||||
}
|
||||
|
|
@ -817,8 +817,8 @@ fn solve_core(tracks: &CameraTracks, s: &SolveSettings, cancel: Option<&AtomicBo
|
|||
let rg = &results[g].0.recon;
|
||||
let cs: Vec<V3> = rg.cams.values().map(|c| c.pose.c).collect();
|
||||
let base = cs.iter().flat_map(|a| cs.iter().map(move |b| norm(sub(*a, *b)))).fold(0.0, f64::max);
|
||||
let c0 = rg.cams.values().next().expect("camera");
|
||||
let mut depth: Vec<f64> = rg.pts.values().map(|x| norm(sub(*x, c0.pose.c))).collect();
|
||||
let c0 = rg.cams.values().next().map_or([0.0; 3], |c| c.pose.c);
|
||||
let mut depth: Vec<f64> = rg.pts.values().map(|x| norm(sub(*x, c0))).collect();
|
||||
depth.sort_by(f64::total_cmp);
|
||||
let med = depth.get(depth.len() / 2).copied().unwrap_or(1.0);
|
||||
if debug() {
|
||||
|
|
@ -969,8 +969,8 @@ fn cov_ratio(p: &[V3]) -> f64 {
|
|||
}
|
||||
|
||||
/// Canonical frame, per-frame output (unsolved frames interpolated), points and errors.
|
||||
fn finish(d: &Data, tracks: &CameraTracks, s: &SolveSettings, r: Recon, method: SolveMethod, kind: Kind) -> CameraSolve {
|
||||
let (k0, c0) = r.cams.iter().next().map(|(k, c)| (*k, *c)).expect("solved");
|
||||
fn finish(d: &Data, tracks: &CameraTracks, s: &SolveSettings, r: Recon, method: SolveMethod, kind: Kind) -> Result<CameraSolve, SolveError> {
|
||||
let Some((k0, c0)) = r.cams.iter().next().map(|(k, c)| (*k, *c)) else { return err("the camera could not be solved (no frame was solved)") };
|
||||
// Scale: median depth of the points seen by the first solved camera is 1.
|
||||
let mut depths: Vec<f64> = d.by_frame[k0].iter().filter_map(|(ti, _)| r.pts.get(ti)).map(|x| c0.pose.to_cam(*x)[2]).filter(|z| *z > 0.0).collect();
|
||||
depths.sort_by(f64::total_cmp);
|
||||
|
|
@ -1038,7 +1038,7 @@ fn finish(d: &Data, tracks: &CameraTracks, s: &SolveSettings, r: Recon, method:
|
|||
en += n1;
|
||||
points.push(SolvedPoint { id: *id, pos: to_world(x), error: (s1 / n1 as f64) as f32, first: *start as u32, last: (start + pts.len() - 1) as u32 });
|
||||
}
|
||||
CameraSolve {
|
||||
Ok(CameraSolve {
|
||||
version: 1,
|
||||
size: tracks.size,
|
||||
start: tracks.start,
|
||||
|
|
@ -1050,5 +1050,5 @@ fn finish(d: &Data, tracks: &CameraTracks, s: &SolveSettings, r: Recon, method:
|
|||
points,
|
||||
ground: None,
|
||||
distortion: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ impl GrayPyramid {
|
|||
pub fn from_plane(base: Plane, offset: [f64; 2], factor: f64, levels: usize) -> GrayPyramid {
|
||||
let mut lv = vec![base];
|
||||
for _ in 0..levels {
|
||||
let last = lv.last().expect("level");
|
||||
let Some(last) = lv.last() else { break };
|
||||
if last.w < 16 || last.h < 16 {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
//! Coordinates are *layer* pixels: the frame image's pixel `(i, j)` covers `[i, i+1) × [j, j+1)`
|
||||
//! after subtracting the frame's `offset` (see [`Frame`]).
|
||||
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
pub mod camtrack;
|
||||
pub mod face;
|
||||
pub mod fit;
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ pub fn smooth_trajectories(tracks: &[Track2D], frames: usize, sigma: f64, rank:
|
|||
let mut ata = vec![0.0; k * k];
|
||||
let mut atb = [vec![0.0; k], vec![0.0; k]];
|
||||
for &c in &cols {
|
||||
let p = tr.at((lo + c) as u32).expect("covered");
|
||||
let Some(p) = tr.at((lo + c) as u32) else { continue };
|
||||
for a in 0..k {
|
||||
for d in 0..2 {
|
||||
atb[d][a] += cmat[a][c] * p[d];
|
||||
|
|
@ -391,8 +391,8 @@ pub fn smooth_trajectories(tracks: &[Track2D], frames: usize, sigma: f64, rank:
|
|||
}
|
||||
dst[d] = x.iter().zip(&chat).map(|(a, b)| a * b).sum();
|
||||
}
|
||||
if ok {
|
||||
out.push((tr.at(t32).expect("seen"), dst));
|
||||
if ok && let Some(p) = tr.at(t32) {
|
||||
out.push((p, dst));
|
||||
}
|
||||
}
|
||||
out
|
||||
|
|
|
|||
Loading…
Reference in New Issue