use crate::{Camera, IntrinsicParametersPerspective, Points, WorldFrame};
use nalgebra::{storage::Storage, RealField, SMatrix, U1, U3};
pub struct JacobianPerspectiveCache<R: RealField> {
m: SMatrix<R, 3, 4>,
}
impl<R: RealField> JacobianPerspectiveCache<R> {
pub fn new(cam: &Camera<R, IntrinsicParametersPerspective<R>>) -> Self {
let m = {
let p33 = cam.intrinsics().as_intrinsics_matrix();
p33 * cam.extrinsics().matrix()
};
let m = if m[(0, 0)] < nalgebra::zero() { -m } else { m };
let m = m.clone() / m[(2, 3)].clone(); Self { m }
}
pub fn linearize_at<STORAGE>(&self, p: &Points<WorldFrame, R, U1, STORAGE>) -> SMatrix<R, 2, 3>
where
STORAGE: Storage<R, U1, U3>,
{
let pt3d = &p.data;
let x = pt3d[(0, 0)].clone();
let y = pt3d[(0, 1)].clone();
let z = pt3d[(0, 2)].clone();
let p = &self.m;
let denom = p[(2, 0)].clone() * x.clone()
+ p[(2, 1)].clone() * y.clone()
+ p[(2, 2)].clone() * z.clone()
+ p[(2, 3)].clone();
let denom_sqrt = denom.clone().powi(-2);
let factor_u = p[(0, 0)].clone() * x.clone()
+ p[(0, 1)].clone() * y.clone()
+ p[(0, 2)].clone() * z.clone()
+ p[(0, 3)].clone();
let ux = -p[(2, 0)].clone() * denom_sqrt.clone() * factor_u.clone()
+ p[(0, 0)].clone() / denom.clone();
let uy = -p[(2, 1)].clone() * denom_sqrt.clone() * factor_u.clone()
+ p[(0, 1)].clone() / denom.clone();
let uz =
-p[(2, 2)].clone() * denom_sqrt.clone() * factor_u + p[(0, 2)].clone() / denom.clone();
let factor_v = p[(1, 0)].clone() * x
+ p[(1, 1)].clone() * y
+ p[(1, 2)].clone() * z
+ p[(1, 3)].clone();
let vx = -p[(2, 0)].clone() * denom_sqrt.clone() * factor_v.clone()
+ p[(1, 0)].clone() / denom.clone();
let vy = -p[(2, 1)].clone() * denom_sqrt.clone() * factor_v.clone()
+ p[(1, 1)].clone() / denom.clone();
let vz = -p[(2, 2)].clone() * denom_sqrt * factor_v + p[(1, 2)].clone() / denom;
SMatrix::<R, 2, 3>::new(ux, uy, uz, vx, vy, vz)
}
}
#[test]
fn test_jacobian_perspective() {
use nalgebra::{OMatrix, RowVector2, RowVector3, Unit, Vector3};
use super::*;
use crate::{Camera, ExtrinsicParameters, IntrinsicParametersPerspective};
let params = PerspectiveParams {
fx: 100.0,
fy: 102.0,
skew: 0.1,
cx: 321.0,
cy: 239.9,
};
let intrinsics: IntrinsicParametersPerspective<_> = params.into();
let camcenter = Vector3::new(10.0, 0.0, 10.0);
let lookat = Vector3::new(0.0, 0.0, 0.0);
let up = Unit::new_normalize(Vector3::new(0.0, 0.0, 1.0));
let pose = ExtrinsicParameters::from_view(&camcenter, &lookat, &up);
let cam = Camera::new(intrinsics, pose);
let cam_jac = JacobianPerspectiveCache::new(&cam);
let center = Points::new(RowVector3::new(0.01, 0.02, 0.03));
let offset = Vector3::new(0.0, 0.0, 0.01);
let center_projected: OMatrix<f64, U1, U2> = cam.world_to_pixel(¢er).data;
let linearized_cam = cam_jac.linearize_at(¢er);
let new_point = Points::new(center.data + offset.transpose());
let nonlin = cam.world_to_pixel(&new_point).data;
let o = linearized_cam * offset;
let linear_prediction = RowVector2::new(center_projected.x + o[0], center_projected.y + o[1]);
approx::assert_relative_eq!(linear_prediction, nonlin, epsilon = nalgebra::convert(1e-4));
}