#[cfg(feature = "serde-serialize")]
use std::io::Read;
use na::{
allocator::Allocator,
core::{dimension::DimMin, Matrix3, OMatrix, RowVector5, SMatrix},
};
use na::{DefaultAllocator, DimName, RealField};
use nalgebra as na;
use crate::{Error, Result, RosOpenCvIntrinsics};
#[cfg(feature = "serde-serialize")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct RosCameraInfo<R: RealField> {
pub image_width: usize,
pub image_height: usize,
pub camera_name: String,
pub camera_matrix: RosMatrix<R>,
pub distortion_model: String,
pub distortion_coefficients: RosMatrix<R>,
pub rectification_matrix: RosMatrix<R>,
pub projection_matrix: RosMatrix<R>,
}
impl<R: RealField> From<NamedIntrinsicParameters<R>> for RosCameraInfo<R> {
fn from(orig: NamedIntrinsicParameters<R>) -> Self {
let d = &orig.intrinsics.distortion;
let distortion = vec![
d.radial1(),
d.radial2(),
d.tangential1(),
d.tangential2(),
d.radial3(),
];
Self {
image_width: orig.width,
image_height: orig.height,
camera_name: orig.name,
camera_matrix: to_ros(orig.intrinsics.k),
distortion_model: "plumb_bob".to_string(),
distortion_coefficients: to_ros_matrix(1, 5, distortion.as_slice()),
rectification_matrix: to_ros(orig.intrinsics.rect),
projection_matrix: to_ros(orig.intrinsics.p),
}
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct RosMatrix<R: RealField> {
pub rows: usize,
pub cols: usize,
pub data: Vec<R>,
}
fn to_ros<R: RealField, SS: DimName, OS: DimName>(arr: na::OMatrix<R, SS, OS>) -> RosMatrix<R>
where
DefaultAllocator: Allocator<R, SS, SS>,
DefaultAllocator: Allocator<R, SS>,
DefaultAllocator: Allocator<R, OS, SS>,
DefaultAllocator: Allocator<R, SS, OS>,
DefaultAllocator: Allocator<R, OS, OS>,
DefaultAllocator: Allocator<R, OS>,
DefaultAllocator: Allocator<(usize, usize), OS>,
OS: DimMin<OS, Output = OS>,
{
let a2 = arr.transpose();
RosMatrix {
rows: arr.nrows(),
cols: arr.ncols(),
data: a2.as_slice().to_vec(),
}
}
#[inline]
pub(crate) fn to_ros_matrix<R: RealField>(rows: usize, cols: usize, data: &[R]) -> RosMatrix<R> {
RosMatrix {
rows,
cols,
data: Vec::from(data),
}
}
pub(crate) fn get_nalgebra_matrix<R, D1, D2>(
ros_matrix: &RosMatrix<R>,
) -> Result<OMatrix<R, D1, D2>>
where
R: RealField,
D1: DimName,
D2: DimName,
DefaultAllocator: Allocator<R, D1, D1>,
DefaultAllocator: Allocator<R, D1>,
DefaultAllocator: Allocator<R, D2, D1>,
DefaultAllocator: Allocator<R, D1, D2>,
DefaultAllocator: Allocator<R, D2, D2>,
DefaultAllocator: Allocator<R, D2>,
{
if ros_matrix.rows as usize != D1::dim() {
return Err(Error::BadMatrixSize);
}
if ros_matrix.cols as usize != D2::dim() {
return Err(Error::BadMatrixSize);
}
if ros_matrix.data.len() != (ros_matrix.rows * ros_matrix.cols) as usize {
return Err(Error::BadMatrixSize);
}
let data_converted: Vec<R> = ros_matrix
.data
.clone()
.into_iter()
.map(na::convert)
.collect();
Ok(OMatrix::from_row_slice_generic(
D1::name(),
D2::name(),
&data_converted,
))
}
#[derive(Debug, Clone)]
pub struct NamedIntrinsicParameters<R: RealField> {
pub name: String,
pub width: usize,
pub height: usize,
pub intrinsics: RosOpenCvIntrinsics<R>,
}
impl<R: RealField> std::convert::TryFrom<RosCameraInfo<R>> for NamedIntrinsicParameters<R> {
type Error = Error;
fn try_from(ros_camera: RosCameraInfo<R>) -> Result<NamedIntrinsicParameters<R>> {
let intrinsics = {
let p = get_nalgebra_matrix(&ros_camera.projection_matrix)?;
let k: SMatrix<R, 3, 3> = get_nalgebra_matrix(&ros_camera.camera_matrix)?;
if ros_camera.distortion_model != "plumb_bob" {
return Err(Error::UnknownDistortionModel);
}
let d: RowVector5<R> = get_nalgebra_matrix(&ros_camera.distortion_coefficients)?;
let rect: Matrix3<R> = get_nalgebra_matrix(&ros_camera.rectification_matrix)?;
let distortion = crate::Distortion::from_opencv_vec(d.transpose());
RosOpenCvIntrinsics::from_components(p, k, distortion, rect)?
};
Ok(NamedIntrinsicParameters {
name: ros_camera.camera_name,
width: ros_camera.image_width,
height: ros_camera.image_height,
intrinsics,
})
}
}
#[cfg(feature = "serde-serialize")]
pub fn from_ros_yaml<R, Rd>(reader: Rd) -> Result<NamedIntrinsicParameters<R>>
where
R: RealField + serde::de::DeserializeOwned,
Rd: Read,
{
let ros_camera: RosCameraInfo<R> = serde_yaml::from_reader(reader)?;
Ok(std::convert::TryInto::try_into(ros_camera)?)
}