use crate::{KeyPointWorldMatch, NormalizedKeyPoint, WorldPoint};
use derive_more::{AsMut, AsRef, Constructor, Deref, DerefMut, From, Into};
use nalgebra::{
dimension::{U2, U3, U7},
Isometry3, Matrix3, Matrix3x2, MatrixMN, Quaternion, Translation3, UnitQuaternion, Vector2,
Vector3, Vector4, VectorN,
};
use sample_consensus::Model;
#[derive(Debug, Clone, Copy, PartialEq, AsMut, AsRef, Constructor, Deref, DerefMut, From, Into)]
pub struct WorldPose(pub Isometry3<f32>);
impl Model<KeyPointWorldMatch> for WorldPose {
fn residual(&self, data: &KeyPointWorldMatch) -> f32 {
let WorldPose(iso) = *self;
let KeyPointWorldMatch(image, world) = *data;
let new_bearing = (iso * world.coords).normalize();
let bearing_vector = image.to_homogeneous().normalize();
1.0 - bearing_vector.dot(&new_bearing)
}
}
impl WorldPose {
pub fn projection_error(&self, correspondence: KeyPointWorldMatch) -> Vector2<f32> {
let KeyPointWorldMatch(NormalizedKeyPoint(image), world) = correspondence;
let NormalizedKeyPoint(projection) = self.project(world);
image - projection
}
pub fn project(&self, WorldPoint(point): WorldPoint) -> NormalizedKeyPoint {
let WorldPose(iso) = *self;
let camera_point = iso * point;
NormalizedKeyPoint(camera_point.xy() / camera_point.z)
}
#[rustfmt::skip]
pub fn projection_pose_jacobian(&self, point: WorldPoint) -> MatrixMN<f32, U7, U2> {
let q = self.0.rotation.quaternion().coords;
let p = point.0.coords;
let pc = (self.0 * point.0).coords;
let dp_dt = Matrix3::identity();
let qv_qv_p = Matrix3::new(
q.y * p.y + q.z * p.z, q.y * p.x - 2.0 * q.x * p.y, q.z * p.x - 2.0 * q.x * p.z,
q.x * p.y - 2.0 * q.y * p.x, q.x * p.x + q.z * p.z, q.z * p.y - 2.0 * q.y * p.z,
q.x * p.z - 2.0 * q.z * p.x, q.y * p.z - 2.0 * q.z * p.y, q.x * p.x + q.y * p.y
);
let qv_p = Matrix3::new(
0.0, -p.z, p.y,
p.z, 0.0, -p.x,
-p.y, p.x, 0.0,
);
let dp_dqv = 2.0 * (q.w * qv_p + qv_qv_p);
let dp_ds = 2.0 * q.xyz().cross(&p);
let dp_dtq = MatrixMN::<f32, U7, U3>::from_rows(&[
dp_dt.row(0).into(),
dp_dt.row(1).into(),
dp_dt.row(2).into(),
dp_dqv.row(0).into(),
dp_dqv.row(1).into(),
dp_dqv.row(2).into(),
dp_ds.transpose(),
]);
let pcz = pc.z.recip();
let npcz2 = -(pcz * pcz);
let dk_dp = Matrix3x2::new(
pcz, 0.0,
0.0, pcz,
npcz2, npcz2,
);
dp_dtq * dk_dp
}
pub fn to_vec(&self) -> VectorN<f32, U7> {
let Self(iso) = *self;
let t = iso.translation.vector;
let rc = iso.rotation.quaternion().coords;
t.push(rc.x).push(rc.y).push(rc.z).push(rc.w)
}
pub fn from_vec(v: VectorN<f32, U7>) -> Self {
Self(Isometry3::from_parts(
Translation3::from(Vector3::new(v[0], v[1], v[2])),
UnitQuaternion::from_quaternion(Quaternion::from(Vector4::new(v[3], v[4], v[5], v[6]))),
))
}
}
impl From<CameraPose> for WorldPose {
fn from(camera: CameraPose) -> Self {
Self(camera.inverse())
}
}
#[derive(Debug, Clone, Copy, PartialEq, AsMut, AsRef, Constructor, Deref, DerefMut, From, Into)]
pub struct CameraPose(pub Isometry3<f32>);
impl From<WorldPose> for CameraPose {
fn from(world: WorldPose) -> Self {
Self(world.inverse())
}
}