use core::{
borrow::Borrow,
marker::PhantomData,
};
use log::warn;
use sophus_autodiff::{
linalg::EPS_F64,
params::IsParamsImpl,
};
use crate::{
distortions::affine::AffineDistortionImpl,
prelude::*,
traits::IsCameraDistortionImpl,
};
extern crate alloc;
#[derive(Debug, Clone, Copy)]
pub struct BrownConradyDistortionImpl<
S: IsScalar<BATCH, DM, DN>,
const BATCH: usize,
const DM: usize,
const DN: usize,
> {
phantom: PhantomData<S>,
}
impl<S: IsScalar<BATCH, DM, DN>, const BATCH: usize, const DM: usize, const DN: usize>
IsParamsImpl<S, 12, BATCH, DM, DN> for BrownConradyDistortionImpl<S, BATCH, DM, DN>
{
fn are_params_valid(_params: S::Vector<12>) -> S::Mask {
S::Mask::all_true()
}
fn params_examples() -> alloc::vec::Vec<S::Vector<12>> {
alloc::vec![S::Vector::<12>::from_f64_array([
286.0,
286.0,
424.0,
400.0,
0.726405,
-0.0148413,
1.38447e-05,
0.000419742,
-0.00514224,
1.06774,
0.128429,
-0.019901,
])]
}
fn invalid_params_examples() -> alloc::vec::Vec<S::Vector<12>> {
alloc::vec![]
}
}
impl<S: IsScalar<BATCH, DM, DN>, const BATCH: usize, const DM: usize, const DN: usize>
BrownConradyDistortionImpl<S, BATCH, DM, DN>
{
fn distortion_impl(
params: &S::Vector<8>,
proj_point_in_camera_z1_plane: &S::Vector<2>,
) -> S::Vector<2> {
let x = proj_point_in_camera_z1_plane.elem(0);
let y = proj_point_in_camera_z1_plane.elem(1);
let d0 = params.elem(0);
let d1 = params.elem(1);
let d2 = params.elem(2);
let k3 = params.elem(3);
let d4 = params.elem(4);
let d5 = params.elem(5);
let d6 = params.elem(6);
let d7 = params.elem(7);
let one = S::from_f64(1.0);
let two = S::from_f64(2.0);
let r2 = proj_point_in_camera_z1_plane.squared_norm();
let r4 = r2 * r2;
let r6 = r4 * r2;
let a1 = two * x * y;
let a2 = r2 + two * x * x;
let a3 = r2 + two * y * y;
let cdist = one + d0 * r2 + d1 * r4 + d4 * r6;
let icdist2 = one / (one + d5 * r2 + d6 * r4 + d7 * r6);
let xd0 = x * cdist * icdist2 + d2 * a1 + k3 * a2;
let yd0 = y * cdist * icdist2 + d2 * a3 + k3 * a1;
S::Vector::<2>::from_array([xd0, yd0])
}
fn undistort_impl(
distortion_params: &S::Vector<8>,
distorted_point: &S::Vector<2>,
dbg_info_distorted_point: &S::Vector<2>,
) -> S::Vector<2> {
let mut xy = *distorted_point;
let d0 = distortion_params.elem(0);
let d1 = distortion_params.elem(1);
let d2 = distortion_params.elem(2);
let k3 = distortion_params.elem(3);
let d4 = distortion_params.elem(4);
let d5 = distortion_params.elem(5);
let d6 = distortion_params.elem(6);
let d7 = distortion_params.elem(7);
for _i in 0..50 {
let x = xy.elem(0);
let y = xy.elem(1);
let f_xy = Self::distortion_impl(distortion_params, &xy.clone()) - *distorted_point;
let du_dx;
let du_dy;
let dv_dx;
let dv_dy;
{
let a = x;
let b = y;
let c0 = a * a; let c1 = b * b; let c2 = c0 + c1;
let c3 = c2 * c2; let c4 = c3 * c2; let c5 = c2 * d5 + c3 * d6 + c4 * d7 + S::from_f64(1.0);
let c6 = c5 * c5; let c7 = S::from_f64(1.0) / c6;
let c8 = a * k3;
let c9 = S::from_f64(2.0) * d2;
let c10 = S::from_f64(2.0) * c2;
let c11 = S::from_f64(3.0) * c3;
let c12 = c2 * d0;
let c13 = c3 * d1;
let c14 = c4 * d4;
let c15 = S::from_f64(2.0)
* (c10 * d6 + c11 * d7 + d5)
* (c12 + c13 + c14 + S::from_f64(1.0));
let c16 = S::from_f64(2.0) * c10 * d1
+ S::from_f64(2.0) * c11 * d4
+ S::from_f64(2.0) * d0;
let c17 = c12 + c13 + c14 + S::from_f64(1.0);
let c18 = b * k3;
let c19 = a * b;
let c20 = -c15 * c19 + c16 * c19 * c5;
du_dx = c7
* (-c0 * c15 + c5 * (c0 * c16 + c17) + c6 * (b * c9 + S::from_f64(6.0) * c8));
du_dy = c7 * (c20 + c6 * (a * c9 + S::from_f64(2.0) * c18));
dv_dx = c7 * (c20 + c6 * (S::from_f64(2.0) * a * d2 + S::from_f64(2.0) * c18));
dv_dy = c7
* (-c1 * c15
+ c5 * (c1 * c16 + c17)
+ c6 * (S::from_f64(6.0) * b * d2 + S::from_f64(2.0) * c8));
}
let a = du_dx;
let b = du_dy;
let c = dv_dx;
let d = dv_dy;
let m: S::Matrix<2, 2> = S::Matrix::from_array2([[d, -b], [-c, a]]);
let j_inv: S::Matrix<2, 2> = m.scaled(S::from_f64(1.0) / (a * d - b * c));
let step: S::Vector<2> = j_inv * f_xy;
if step
.norm()
.real_part()
.less_equal(&S::RealScalar::from_f64(EPS_F64))
.all()
{
break;
}
*xy.elem_mut(0) = xy.elem(0) - step.elem(0);
*xy.elem_mut(1) = xy.elem(1) - step.elem(1);
}
let f_xy = Self::distortion_impl(distortion_params, &xy.clone()) - *distorted_point;
if !f_xy
.norm()
.real_part()
.less_equal(&S::RealScalar::from_f64(EPS_F64))
.any()
{
warn!("Newton did not converge: f_xy: {f_xy:?}: pixel {dbg_info_distorted_point:?}");
}
xy
}
}
impl<S: IsScalar<BATCH, DM, DN>, const BATCH: usize, const DM: usize, const DN: usize>
IsCameraDistortionImpl<S, 8, 12, BATCH, DM, DN>
for BrownConradyDistortionImpl<S, BATCH, DM, DN>
{
fn distort<PA, PO>(params: PA, proj_point_in_camera_z1_plane: PO) -> S::Vector<2>
where
PA: Borrow<S::Vector<12>>,
PO: Borrow<S::Vector<2>>,
{
let params = params.borrow();
let proj_point_in_camera_z1_plane = proj_point_in_camera_z1_plane.borrow();
let distorted_point_in_camera_z1_plane =
Self::distortion_impl(¶ms.get_fixed_subvec(4), proj_point_in_camera_z1_plane);
AffineDistortionImpl::<S, BATCH, DM, DN>::distort(
params.get_fixed_subvec(0),
distorted_point_in_camera_z1_plane,
)
}
fn undistort<PA, PO>(params: PA, distorted_point: PO) -> S::Vector<2>
where
PA: Borrow<S::Vector<12>>,
PO: Borrow<S::Vector<2>>,
{
let params = params.borrow();
let distorted_point = distorted_point.borrow();
let undistorted_point_in_camera_z1_plane =
AffineDistortionImpl::<S, BATCH, DM, DN>::undistort(
params.get_fixed_subvec(0),
distorted_point,
);
Self::undistort_impl(
¶ms.get_fixed_subvec(4),
&undistorted_point_in_camera_z1_plane,
distorted_point,
)
}
fn dx_distort_x<PA, PO>(params: PA, proj_point_in_camera_z1_plane: PO) -> S::Matrix<2, 2>
where
PA: Borrow<S::Vector<12>>,
PO: Borrow<S::Vector<2>>,
{
let params = params.borrow();
let proj_point_in_camera_z1_plane = proj_point_in_camera_z1_plane.borrow();
const OFFSET: usize = 4;
let fx = params.elem(0);
let fy = params.elem(1);
let d0 = params.elem(OFFSET);
let d1 = params.elem(1 + OFFSET);
let d2 = params.elem(2 + OFFSET);
let d3 = params.elem(3 + OFFSET);
let d4 = params.elem(4 + OFFSET);
let d5 = params.elem(5 + OFFSET);
let d6 = params.elem(6 + OFFSET);
let d7 = params.elem(7 + OFFSET);
let one = S::from_f64(1.0);
let two = S::from_f64(2.0);
let a = proj_point_in_camera_z1_plane.elem(0);
let b = proj_point_in_camera_z1_plane.elem(1);
let c0 = a * d3;
let c1 = two * d2;
let c2 = a * a; let c3 = b * b; let c4 = c2 + c3;
let c5 = c4 * c4; let c6 = c5 * c4; let c7 = c4 * d5 + c5 * d6 + c6 * d7 + one;
let c8 = c7 * c7; let c9 = two * c4;
let c10 = S::from_f64(3.0) * c5;
let c11 = c4 * d0;
let c12 = c5 * d1;
let c13 = c6 * d4;
let c14 = two * (c10 * d7 + c9 * d6 + d5) * (c11 + c12 + c13 + one);
let c15 = two * c10 * d4 + two * c9 * d1 + two * d0;
let c16 = one * c11 + c12 + c13 + one;
let c17 = one / c8;
let c18 = c17 * fx;
let c19 = b * d3;
let c20 = a * b;
let c21 = -c14 * c20 + c15 * c20 * c7;
let c22 = c17 * fy;
let dfx_dfx =
c18 * (-c14 * c2 + c7 * (c15 * c2 + c16) + c8 * (b * c1 + S::from_f64(6.0) * c0));
let dfy_dfx = c22 * (c21 + c8 * (two * a * d2 + two * c19));
let dfx_dfy = c18 * (c21 + c8 * (a * c1 + two * c19));
let dfy_dfy =
c22 * (-c14 * c3 + c7 * (c15 * c3 + c16) + c8 * (S::from_f64(6.0) * b * d2 + two * c0));
S::Matrix::from_array2([[dfx_dfx, dfx_dfy], [dfy_dfx, dfy_dfy]])
}
fn dx_distort_params<PA, PO>(
_params: PA,
_proj_point_in_camera_z1_plane: PO,
) -> S::Matrix<2, 12>
where
PA: Borrow<S::Vector<12>>,
PO: Borrow<S::Vector<2>>,
{
todo!()
}
}