use crate::{Bearing, CameraPoint, FeatureWorldMatch, Projective, Skew3, WorldPoint};
use derive_more::{AsMut, AsRef, From, Into};
use nalgebra::{
IsometryMatrix3, Matrix4, Matrix4x6, Matrix6x4, Rotation3, Vector3, Vector4, Vector6,
};
use sample_consensus::Model;
pub trait Pose: From<IsometryMatrix3<f64>> + Clone + Copy {
type InputPoint: Projective;
type OutputPoint: Projective;
type Inverse: Pose;
fn isometry(self) -> IsometryMatrix3<f64>;
fn identity() -> Self {
IsometryMatrix3::identity().into()
}
fn inverse(self) -> Self::Inverse {
self.isometry().inverse().into()
}
fn scale(self, scale: f64) -> Self {
let mut isometry = self.isometry();
isometry.translation.vector *= scale;
isometry.into()
}
fn from_parts(translation: Vector3<f64>, rotation: Rotation3<f64>) -> Self {
IsometryMatrix3::from_parts(translation.into(), rotation).into()
}
fn homogeneous(self) -> Matrix4<f64> {
self.isometry().to_homogeneous()
}
fn se3(self) -> Vector6<f64> {
let isometry = self.isometry();
let t = isometry.translation.vector;
let r: Skew3 = isometry.rotation.into();
Vector6::new(t.x, t.y, t.z, r.x, r.y, r.z)
}
fn from_se3(se3: Vector6<f64>) -> Self {
let translation = se3.xyz();
let rotation = Skew3(Vector3::new(se3[3], se3[4], se3[5])).into();
Self::from_parts(translation, rotation)
}
fn transform_jacobians(
self,
input: Self::InputPoint,
) -> (Self::OutputPoint, Matrix4<f64>, Matrix4x6<f64>) {
let (rotated, output) = pose_rotated_output(self, input);
let jacobian_input = pose_jacobian_input(self);
let jacobian_self = pose_jacobian_self(self, rotated, output);
(output.into(), jacobian_input, jacobian_self)
}
fn transform_jacobian_input(
self,
input: Self::InputPoint,
) -> (Self::OutputPoint, Matrix4<f64>) {
let output = pose_output(self, input);
let jacobian_input = pose_jacobian_input(self);
(output.into(), jacobian_input)
}
fn transform_jacobian_self(
self,
input: Self::InputPoint,
) -> (Self::OutputPoint, Matrix4x6<f64>) {
let (rotated, output) = pose_rotated_output(self, input);
let jacobian_self = pose_jacobian_self(self, rotated, output);
(output.into(), jacobian_self)
}
fn transform(self, input: Self::InputPoint) -> Self::OutputPoint {
pose_output(self, input).into()
}
}
fn pose_output<P: Pose>(pose: P, input: P::InputPoint) -> Vector4<f64>
where
P::InputPoint: From<Vector4<f64>>,
{
pose.isometry().to_homogeneous() * input.homogeneous()
}
fn pose_rotated_output<P: Pose>(pose: P, input: P::InputPoint) -> (Vector4<f64>, Vector4<f64>)
where
P::InputPoint: From<Vector4<f64>>,
{
let rotated = pose.isometry().rotation.to_homogeneous() * input.homogeneous();
let output = pose.isometry().to_homogeneous() * input.homogeneous();
(rotated, output)
}
fn pose_jacobian_input<P: Pose>(pose: P) -> Matrix4<f64> {
pose.isometry().to_homogeneous()
}
fn pose_jacobian_self<P: Pose>(
pose: P,
rotated: Vector4<f64>,
output: Vector4<f64>,
) -> Matrix4x6<f64> {
let translation = pose.isometry().translation.to_homogeneous();
let dp_dt = Matrix4::<f64>::identity() * output.w;
let dp_ds = translation * Skew3::jacobian_self(rotated.xyz()).to_homogeneous();
Matrix6x4::<f64>::from_rows(&[
dp_dt.row(0),
dp_dt.row(1),
dp_dt.row(2),
dp_ds.row(0),
dp_ds.row(1),
dp_ds.row(2),
])
.transpose()
}
#[derive(Debug, Clone, Copy, PartialEq, AsMut, AsRef, From, Into)]
pub struct WorldToCamera(pub IsometryMatrix3<f64>);
impl Pose for WorldToCamera {
type InputPoint = WorldPoint;
type OutputPoint = CameraPoint;
type Inverse = CameraToWorld;
fn isometry(self) -> IsometryMatrix3<f64> {
self.into()
}
}
impl<P> Model<FeatureWorldMatch<P>> for WorldToCamera
where
P: Bearing,
{
fn residual(&self, data: &FeatureWorldMatch<P>) -> f64 {
let WorldToCamera(iso) = *self;
let FeatureWorldMatch(feature, world) = data;
let new_bearing = (iso.to_homogeneous() * world.0).xyz().normalize();
let bearing_vector = feature.bearing();
1.0 - bearing_vector.dot(&new_bearing)
}
}
#[derive(Debug, Clone, Copy, PartialEq, AsMut, AsRef, From, Into)]
pub struct CameraToWorld(pub IsometryMatrix3<f64>);
impl Pose for CameraToWorld {
type InputPoint = CameraPoint;
type OutputPoint = WorldPoint;
type Inverse = WorldToCamera;
fn isometry(self) -> IsometryMatrix3<f64> {
self.into()
}
}
#[derive(Debug, Clone, Copy, PartialEq, AsMut, AsRef, From, Into)]
pub struct CameraToCamera(pub IsometryMatrix3<f64>);
impl Pose for CameraToCamera {
type InputPoint = CameraPoint;
type OutputPoint = CameraPoint;
type Inverse = CameraToCamera;
fn isometry(self) -> IsometryMatrix3<f64> {
self.into()
}
}