use std::str::FromStr;
use super::generic_functions::*;
use super::{EUCM, EUCMT, FovCamera, Ftheta, KannalaBrandt4, OpenCVModel5, UCM};
use image::DynamicImage;
use nalgebra as na;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub enum GenericModel<T: na::RealField> {
EUCM(EUCM<T>),
UCM(UCM<T>),
OpenCVModel5(OpenCVModel5<T>),
KannalaBrandt4(KannalaBrandt4<T>),
EUCMT(EUCMT<T>),
Ftheta(Ftheta<T>),
FovCamera(FovCamera<T>),
}
macro_rules! generic_impl {
($fn_name:tt, $out:ty, $($v:tt: $t:ty),+) => {
pub fn $fn_name(&self, $($v: $t),+) -> $out{
match self {
GenericModel::EUCM(eucm) => $fn_name(eucm, $($v),+),
GenericModel::UCM(ucm) => $fn_name(ucm, $($v),+),
GenericModel::OpenCVModel5(open_cvmodel5) => $fn_name(open_cvmodel5, $($v),+),
GenericModel::KannalaBrandt4(kannala_brandt4) => $fn_name(kannala_brandt4, $($v),+),
GenericModel::EUCMT(eucmt) => $fn_name(eucmt, $($v),+),
GenericModel::Ftheta(ftheta) => $fn_name(ftheta, $($v),+),
GenericModel::FovCamera(fov_camera) => $fn_name(fov_camera, $($v),+),
}
}
};
}
macro_rules! generic_impl_self {
($fn_name:tt -> $out:ty) => {
pub fn $fn_name(&self) -> $out{
match self {
GenericModel::EUCM(eucm) => eucm.$fn_name(),
GenericModel::UCM(ucm) => ucm.$fn_name(),
GenericModel::OpenCVModel5(open_cvmodel5) => open_cvmodel5.$fn_name(),
GenericModel::KannalaBrandt4(kannala_brandt4) => kannala_brandt4.$fn_name(),
GenericModel::EUCMT(eucmt) => eucmt.$fn_name(),
GenericModel::Ftheta(ftheta) => ftheta.$fn_name(),
GenericModel::FovCamera(fov_camera) => fov_camera.$fn_name(),
}
}
};
($fn_name:tt, $out:ty, $($v:tt: $t:ty),+) => {
pub fn $fn_name(&self, $($v: $t),+) -> $out{
match self {
GenericModel::EUCM(eucm) => eucm.$fn_name($($v),+),
GenericModel::UCM(ucm) => ucm.$fn_name($($v),+),
GenericModel::OpenCVModel5(open_cvmodel5) => open_cvmodel5.$fn_name($($v),+),
GenericModel::KannalaBrandt4(kannala_brandt4) => kannala_brandt4.$fn_name($($v),+),
GenericModel::EUCMT(eucmt) => eucmt.$fn_name($($v),+),
GenericModel::Ftheta(ftheta) => ftheta.$fn_name($($v),+),
GenericModel::FovCamera(fov_camera) => fov_camera.$fn_name($($v),+),
}
}
};
($fn_name:tt, $($v:tt: $t:ty),+) => {
pub fn $fn_name(&mut self, $($v: $t),+){
match self {
GenericModel::EUCM(eucm) => eucm.$fn_name($($v),+),
GenericModel::UCM(ucm) => ucm.$fn_name($($v),+),
GenericModel::OpenCVModel5(open_cvmodel5) => open_cvmodel5.$fn_name($($v),+),
GenericModel::KannalaBrandt4(kannala_brandt4) => kannala_brandt4.$fn_name($($v),+),
GenericModel::EUCMT(eucmt) => eucmt.$fn_name($($v),+),
GenericModel::Ftheta(ftheta) => ftheta.$fn_name($($v),+),
GenericModel::FovCamera(fov_camera) => fov_camera.$fn_name($($v),+),
}
}
};
}
impl GenericModel<f64> {
generic_impl!(init_undistort_map, (na::DMatrix<f32>, na::DMatrix<f32>), projection_mat: &na::Matrix3<f64>, new_w_h: (u32, u32), rotation: Option<na::Rotation3<f64>>);
generic_impl!(estimate_new_camera_matrix_for_undistort, na::Matrix3<f64>, balance: f64, new_image_w_h: Option<(u32, u32)>);
}
impl FromStr for GenericModel<f64> {
type Err = std::fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ucm" | "UCM" => Ok(GenericModel::UCM(UCM::zeros())),
"eucm" | "EUCM" => Ok(GenericModel::EUCM(EUCM::zeros())),
"kb4" | "KB4" => Ok(GenericModel::KannalaBrandt4(KannalaBrandt4::zeros())),
"opencv5" | "OPENCV5" => Ok(GenericModel::OpenCVModel5(OpenCVModel5::zeros())),
"eucmt" | "EUCMT" => Ok(GenericModel::EUCMT(EUCMT::zeros())),
"ftheta" | "FTHETA" => Ok(GenericModel::Ftheta(Ftheta::zeros())),
"fov_camera" | "FOV_CAMERA" => Ok(GenericModel::FovCamera(FovCamera::zeros())),
_ => Err(std::fmt::Error),
}
}
}
impl<T: na::RealField + Clone> GenericModel<T> {
generic_impl_self!(width -> T);
generic_impl_self!(height -> T);
generic_impl_self!(params -> na::DVector<T>);
generic_impl_self!(set_params, params: &na::DVector<T>);
generic_impl_self!(set_w_h, w: u32, h: u32);
generic_impl_self!(camera_params -> na::DVector<T>);
generic_impl_self!(distortion_params -> na::DVector<T>);
generic_impl_self!(distortion_params_bound -> Vec<(usize, (f64, f64))>);
generic_impl_self!(project_one, na::Vector2<T>, pt: &na::Vector3<T>);
generic_impl_self!(unproject_one, na::Vector3<T>, pt: &na::Vector2<T>);
generic_impl_self!(project, Vec<Option<na::Vector2<T>>>, p3d: &[na::Vector3<T>]);
generic_impl_self!(unproject, Vec<Option<na::Vector3<T>>>, p2d: &[na::Vector2<T>]);
pub fn cast<U: na::RealField + Clone>(&self) -> GenericModel<U> {
match self {
GenericModel::EUCM(eucm) => GenericModel::EUCM(EUCM::from(eucm)),
GenericModel::UCM(ucm) => GenericModel::UCM(UCM::from(ucm)),
GenericModel::OpenCVModel5(open_cvmodel5) => {
GenericModel::OpenCVModel5(OpenCVModel5::from(open_cvmodel5))
}
GenericModel::KannalaBrandt4(kannala_brandt4) => {
GenericModel::KannalaBrandt4(KannalaBrandt4::from(kannala_brandt4))
}
GenericModel::EUCMT(eucmt) => GenericModel::EUCMT(EUCMT::from(eucmt)),
GenericModel::Ftheta(ftheta) => GenericModel::Ftheta(Ftheta::from(ftheta)),
GenericModel::FovCamera(fov_camera) => {
GenericModel::FovCamera(FovCamera::from(fov_camera))
}
}
}
pub fn new_from_params(&self, params: &na::DVector<T>) -> GenericModel<T> {
match self {
GenericModel::EUCM(m) => GenericModel::EUCM(EUCM::new(params, m.width, m.height)),
GenericModel::UCM(m) => GenericModel::UCM(UCM::new(params, m.width, m.height)),
GenericModel::OpenCVModel5(m) => {
GenericModel::OpenCVModel5(OpenCVModel5::new(params, m.width, m.height))
}
GenericModel::KannalaBrandt4(m) => {
GenericModel::KannalaBrandt4(KannalaBrandt4::new(params, m.width, m.height))
}
GenericModel::EUCMT(m) => GenericModel::EUCMT(EUCMT::new(params, m.width, m.height)),
GenericModel::Ftheta(m) => GenericModel::Ftheta(Ftheta::new(params, m.width, m.height)),
GenericModel::FovCamera(m) => {
GenericModel::FovCamera(FovCamera::new(params, m.width, m.height))
}
}
}
}
macro_rules! remap_impl {
($src:expr, $map0:expr, $map1:expr, $($img_type:ident => ($inner_type:ident, $default_value:expr)),*) => {
match $src {
$(
DynamicImage::$img_type(img) => {
let (r, c) = $map0.shape();
let out_img = image::ImageBuffer::from_par_fn(c as u32, r as u32, |x, y| {
let idx = y as usize * c + x as usize;
let (x_cor, y_cor) = unsafe { ($map0.get_unchecked(idx), $map1.get_unchecked(idx)) };
if x_cor.is_nan() || y_cor.is_nan() {
return image::$inner_type($default_value);
}
image::imageops::interpolate_bilinear(img, *x_cor, *y_cor)
.unwrap_or(image::$inner_type($default_value))
});
DynamicImage::$img_type(out_img)
}
)*
_ => {
panic!("Not support this image type.");
}
}
};
}
pub fn remap(src: &DynamicImage, map0: &na::DMatrix<f32>, map1: &na::DMatrix<f32>) -> DynamicImage {
remap_impl!(src, map0, map1,
ImageLuma8 => (Luma, [0]),
ImageLumaA8 => (LumaA, [0, 0]),
ImageLuma16 => (Luma, [0]),
ImageLumaA16 => (LumaA, [0, 0]),
ImageRgb8 => (Rgb, [0, 0, 0]),
ImageRgba8 => (Rgba, [0, 0, 0, 0]),
ImageRgb16 => (Rgb, [0, 0, 0]),
ImageRgba16 => (Rgba, [0, 0, 0, 0]),
ImageRgb32F => (Rgb, [0.0, 0.0, 0.0]),
ImageRgba32F => (Rgba, [0.0, 0.0, 0.0, 0.0])
)
}
pub trait ModelCast<T: na::RealField + Clone>: CameraModel<T> {
fn cast<U: na::RealField>(&self) -> na::DVector<U> {
let v: Vec<_> = self
.params()
.iter()
.map(|i| U::from_f64(i.to_subset().unwrap()).unwrap())
.collect();
na::DVector::from_vec(v)
}
}
pub trait CameraModel<T: na::RealField + Clone>
where
Self: Sync,
{
fn set_params(&mut self, params: &na::DVector<T>);
fn params(&self) -> na::DVector<T>;
fn camera_params(&self) -> na::DVector<T>;
fn distortion_params(&self) -> na::DVector<T>;
fn width(&self) -> T;
fn height(&self) -> T;
fn set_w_h(&mut self, w: u32, h: u32);
fn project_one(&self, pt: &na::Vector3<T>) -> na::Vector2<T>;
fn distortion_params_bound(&self) -> Vec<(usize, (f64, f64))>;
fn project(&self, p3d: &[na::Vector3<T>]) -> Vec<Option<na::Vector2<T>>> {
p3d.par_iter()
.map(|pt| {
let p2d = self.project_one(pt);
if p2d[0] < T::from_f64(0.0).unwrap()
|| p2d[0] > self.width()
|| p2d[1] < T::from_f64(0.0).unwrap()
|| p2d[1] > self.height()
{
None
} else {
Some(p2d)
}
})
.collect()
}
fn unproject_one(&self, pt: &na::Vector2<T>) -> na::Vector3<T>;
fn unproject(&self, p2d: &[na::Vector2<T>]) -> Vec<Option<na::Vector3<T>>> {
p2d.par_iter()
.map(|pt| {
if pt[0] < T::from_f64(0.0).unwrap()
|| pt[0] > self.width() - T::from_f64(1.0).unwrap()
|| pt[1] < T::from_f64(0.0).unwrap()
|| pt[1] > self.height() - T::from_f64(1.0).unwrap()
{
None
} else {
let p3d = self.unproject_one(pt);
Some(p3d)
}
})
.collect()
}
}