use core::borrow::Borrow;
use sophus_image::ImageSize;
use crate::{
BrownConradyCamera,
KannalaBrandtCamera,
PinholeCamera,
camera_enum::{
EnhancedUnifiedCamera,
GeneralCameraEnum,
PerspectiveCameraEnum,
},
prelude::*,
};
extern crate alloc;
#[derive(Debug, Clone)]
pub struct DynCameraFacade<
S: IsScalar<BATCH, DM, DN>,
const BATCH: usize,
const DM: usize,
const DN: usize,
CameraType: IsPerspectiveCamera<S, BATCH, DM, DN>,
> {
camera_type: CameraType,
phantom: core::marker::PhantomData<S>,
}
pub type DynGeneralCamera<S, const BATCH: usize, const DM: usize, const DN: usize> =
DynCameraFacade<S, BATCH, DM, DN, GeneralCameraEnum<S, BATCH, DM, DN>>;
pub type DynCamera<S, const BATCH: usize, const DM: usize, const DN: usize> =
DynCameraFacade<S, BATCH, DM, DN, PerspectiveCameraEnum<S, BATCH, DM, DN>>;
pub type DynCameraF64 = DynCamera<f64, 1, 0, 0>;
impl<
S: IsScalar<BATCH, DM, DN> + 'static + Send + Sync,
const BATCH: usize,
const DM: usize,
const DN: usize,
CameraType: IsPerspectiveCamera<S, BATCH, DM, DN> + IsCamera<S, BATCH, DM, DN>,
> DynCameraFacade<S, BATCH, DM, DN, CameraType>
{
pub fn default_pinhole(image_size: ImageSize) -> Self {
let w = image_size.width as f64;
let h = image_size.height as f64;
let focal_length = (w + h) * 0.5;
Self {
camera_type: CameraType::new_pinhole(
S::Vector::<4>::from_f64_array([
focal_length,
focal_length,
0.5 * w - 0.5,
0.5 * h - 0.5,
]),
image_size,
),
phantom: core::marker::PhantomData,
}
}
pub fn default_distorted(image_size: ImageSize) -> Self {
let w = image_size.width as f64;
let h = image_size.height as f64;
let focal_length = (w + h) * 0.5;
Self {
camera_type: CameraType::new_kannala_brandt(
S::Vector::<8>::from_f64_array([
focal_length,
focal_length,
0.5 * w - 0.5,
0.5 * h - 0.5,
2.0,
0.0,
0.0,
0.0,
]),
image_size,
),
phantom: core::marker::PhantomData,
}
}
pub fn from_model(camera_type: CameraType) -> Self {
Self {
camera_type,
phantom: core::marker::PhantomData,
}
}
pub fn new_pinhole(params: S::Vector<4>, image_size: ImageSize) -> Self {
Self::from_model(CameraType::new_pinhole(params, image_size))
}
pub fn new_kannala_brandt(params: S::Vector<8>, image_size: ImageSize) -> Self {
Self::from_model(CameraType::new_kannala_brandt(params, image_size))
}
pub fn new_brown_conrady(params: S::Vector<12>, image_size: ImageSize) -> Self {
Self::from_model(CameraType::new_brown_conrady(params, image_size))
}
pub fn new_enhanced_unified(params: S::Vector<6>, image_size: ImageSize) -> Self {
Self::from_model(CameraType::new_enhanced_unified(params, image_size))
}
pub fn cam_proj(&self, point_in_camera: S::Vector<3>) -> S::Vector<2> {
self.camera_type.cam_proj(point_in_camera)
}
pub fn cam_unproj(&self, pixel: S::Vector<2>) -> S::Vector<3> {
self.cam_unproj_with_z(pixel, S::ones())
}
pub fn cam_unproj_with_z(&self, pixel: S::Vector<2>, z: S) -> S::Vector<3> {
self.camera_type.cam_unproj_with_z(pixel, z)
}
pub fn distort(
&self,
proj_point_in_camera_z1_plane: impl Borrow<S::Vector<2>>,
) -> S::Vector<2> {
self.camera_type
.distort(proj_point_in_camera_z1_plane.borrow())
}
pub fn undistort(&self, pixel: impl Borrow<S::Vector<2>>) -> S::Vector<2> {
self.camera_type.undistort(pixel.borrow())
}
pub fn dx_distort_x(&self, point_in_camera: impl Borrow<S::Vector<2>>) -> S::Matrix<2, 2> {
self.camera_type.dx_distort_x(point_in_camera.borrow())
}
pub fn image_size(&self) -> ImageSize {
self.camera_type.image_size()
}
pub fn try_get_brown_conrady(self) -> Option<BrownConradyCamera<S, BATCH, DM, DN>> {
self.camera_type.try_get_brown_conrady()
}
pub fn try_get_kannala_brandt(self) -> Option<KannalaBrandtCamera<S, BATCH, DM, DN>> {
self.camera_type.try_get_kannala_brandt()
}
pub fn try_get_pinhole(self) -> Option<PinholeCamera<S, BATCH, DM, DN>> {
self.camera_type.try_get_pinhole()
}
pub fn try_get_enhanced_unified(self) -> Option<EnhancedUnifiedCamera<S, BATCH, DM, DN>> {
self.camera_type.try_get_enhanced_unified()
}
pub fn model_enum(&self) -> &CameraType {
&self.camera_type
}
}
impl<
S: IsScalar<BATCH, DM, DN> + 'static + Send + Sync,
const BATCH: usize,
const DM: usize,
const DN: usize,
> DynCamera<S, BATCH, DM, DN>
{
pub fn pinhole_params(&self) -> S::Vector<4> {
self.camera_type.pinhole_params()
}
}
#[test]
fn dyn_camera_tests() {
use approx::assert_relative_eq;
use sophus_autodiff::{
linalg::{
EPS_F64,
VecF64,
},
maps::VectorValuedVectorMap,
};
use sophus_image::ImageSize;
use crate::{
distortions::{
AffineDistortionImpl,
EnhancedUnifiedDistortionImpl,
KannalaBrandtDistortionImpl,
},
traits::IsCameraDistortionImpl,
};
{
let mut cameras: alloc::vec::Vec<DynCameraF64> = alloc::vec![];
cameras.push(DynCameraF64::new_pinhole(
VecF64::<4>::new(600.0, 599.0, 1.0, 0.5),
ImageSize {
width: 3,
height: 2,
},
));
cameras.push(DynCamera::new_enhanced_unified(
VecF64::<6>::from_vec(alloc::vec![998.0, 1000.0, 320.0, 280.0, 0.5, 1.2]),
ImageSize {
width: 640,
height: 480,
},
));
cameras.push(DynCamera::new_kannala_brandt(
VecF64::<8>::from_vec(alloc::vec![
999.0, 1000.0, 320.0, 280.0, 0.1, 0.01, 0.001, 0.0001
]),
ImageSize {
width: 640,
height: 480,
},
));
cameras.push(DynCamera::new_brown_conrady(
VecF64::<12>::from_vec(alloc::vec![
288.0,
284.0,
0.5 * (424.0 - 1.0),
0.5 * (400.0 - 1.0),
0.726405,
-0.0148413,
1.38447e-05,
0.000419742,
-0.00514224,
1.06774,
0.128429,
-0.019901,
]),
ImageSize {
width: 640,
height: 480,
},
));
for camera in cameras {
let pixels_in_image = alloc::vec![
VecF64::<2>::new(2.0, 1.0),
VecF64::<2>::new(2.9, 1.9),
VecF64::<2>::new(2.5, 1.5),
VecF64::<2>::new(3.0, 2.0),
VecF64::<2>::new(-0.5, -0.5),
VecF64::<2>::new(1.0, 400.0),
VecF64::<2>::new(320.0, 240.0),
VecF64::<2>::new(319.5, 239.5),
VecF64::<2>::new(100.0, 40.0),
VecF64::<2>::new(639.0, 479.0),
VecF64::<2>::new(0.0, 1.0),
VecF64::<2>::new(1.0, 0.0),
VecF64::<2>::new(0.0, 0.0),
];
for pixel in pixels_in_image.clone() {
if pixel[0] > camera.image_size().width as f64
|| pixel[1] > camera.image_size().height as f64
{
continue;
}
for d in [1.0, 0.1, 0.5, 1.1, 3.0, 15.0] {
let point_in_camera = camera.cam_unproj_with_z(pixel, d);
assert_relative_eq!(point_in_camera[2], d, epsilon = EPS_F64);
let pixel_in_image2 = camera.cam_proj(point_in_camera);
assert_relative_eq!(pixel_in_image2, pixel, epsilon = 1e-4);
}
let ab_in_z1plane = camera.undistort(pixel);
let pixel_in_image3 = camera.distort(ab_in_z1plane);
assert_relative_eq!(pixel_in_image3, pixel, epsilon = 1e-4);
let dx = camera.dx_distort_x(pixel);
let numeric_dx = VectorValuedVectorMap::sym_diff_quotient_jacobian(
|x: VecF64<2>| camera.distort(x),
pixel,
EPS_F64,
);
assert_relative_eq!(dx, numeric_dx, epsilon = 1e-4);
match camera.camera_type {
PerspectiveCameraEnum::Pinhole(camera) => {
let dx_params = camera.dx_distort_params(pixel);
let params = *camera.params();
let numeric_dx_params = VectorValuedVectorMap::sym_diff_quotient_jacobian(
|p: VecF64<4>| AffineDistortionImpl::<f64, 1, 0, 0>::distort(p, pixel),
params,
EPS_F64,
);
assert_relative_eq!(dx_params, numeric_dx_params, epsilon = 1e-4);
}
PerspectiveCameraEnum::KannalaBrandt(camera) => {
let dx_params = camera.dx_distort_params(pixel);
let params = *camera.params();
let numeric_dx_params = VectorValuedVectorMap::sym_diff_quotient_jacobian(
|p: VecF64<8>| {
KannalaBrandtDistortionImpl::<f64, 1, 0, 0>::distort(p, pixel)
},
params,
EPS_F64,
);
assert_relative_eq!(dx_params, numeric_dx_params, epsilon = 1e-4);
}
PerspectiveCameraEnum::BrownConrady(_camera) => {
}
PerspectiveCameraEnum::EnhancedUnified(camera) => {
let dx_params = camera.dx_distort_params(pixel);
let params = camera.params();
let numeric_dx_params = VectorValuedVectorMap::sym_diff_quotient_jacobian(
|p: VecF64<6>| {
EnhancedUnifiedDistortionImpl::<f64, 1, 0, 0>::distort(p, pixel)
},
*params,
EPS_F64,
);
assert_relative_eq!(dx_params, numeric_dx_params, epsilon = 1e-4);
}
}
}
}
}
}