use crate::{Camera, IntrinsicParametersPerspective, Points, WorldFrame};
use nalgebra::{storage::Storage, MatrixMN, RealField, U1, U2, U3, U4};
pub struct JacobianPerspectiveCache<R: RealField> {
m: MatrixMN<R, U3, U4>,
}
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 / m[(2, 3)];
Self { m }
}
pub fn linearize_at<STORAGE>(
&self,
p: &Points<WorldFrame, R, U1, STORAGE>,
) -> MatrixMN<R, U2, U3>
where
STORAGE: Storage<R, U1, U3>,
{
let pt3d = &p.data;
let x = pt3d[(0, 0)];
let y = pt3d[(0, 1)];
let z = pt3d[(0, 2)];
let p = &self.m;
let denom = p[(2, 0)] * x + p[(2, 1)] * y + p[(2, 2)] * z + p[(2, 3)];
let denom_sqrt = denom.powi(-2);
let factor_u = p[(0, 0)] * x + p[(0, 1)] * y + p[(0, 2)] * z + p[(0, 3)];
let ux = -p[(2, 0)] * denom_sqrt * factor_u + p[(0, 0)] / denom;
let uy = -p[(2, 1)] * denom_sqrt * factor_u + p[(0, 1)] / denom;
let uz = -p[(2, 2)] * denom_sqrt * factor_u + p[(0, 2)] / denom;
let factor_v = p[(1, 0)] * x + p[(1, 1)] * y + p[(1, 2)] * z + p[(1, 3)];
let vx = -p[(2, 0)] * denom_sqrt * factor_v + p[(1, 0)] / denom;
let vy = -p[(2, 1)] * denom_sqrt * factor_v + p[(1, 1)] / denom;
let vz = -p[(2, 2)] * denom_sqrt * factor_v + p[(1, 2)] / denom;
MatrixMN::<R, U2, U3>::new(ux, uy, uz, vx, vy, vz)
}
}
#[test]
fn test_jacobian_perspective() {
use nalgebra::{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: MatrixMN<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.data[0] + o[0],
center_projected.data[1] + o[1],
);
approx::assert_relative_eq!(linear_prediction, nonlin, epsilon = nalgebra::convert(1e-4));
}