use opencv::prelude::*;
use std::error::Error;
use crate::msgs::{
std_msgs::Header,
sensor_msgs::Image,
};
use crate::utils::{
image_encodings,
image_byteorder_ops,
};
#[derive(Debug)]
pub enum DataContainer {
VecU8(Vec<u8>),
VecI8(Vec<i8>),
VecU16(Vec<u16>),
VecI16(Vec<i16>),
VecI32(Vec<u32>),
VecF32(Vec<f32>),
VecF64(Vec<f64>),
}
#[derive(Debug)]
pub struct CvImage {
header: Header,
height: usize,
width: usize,
encoding: String,
data: DataContainer,
}
impl CvImage {
pub fn from_imgmsg(image: Image) -> Result<CvImage, Box<dyn Error>> {
let bit_depth = image_encodings::get_bit_depth(image.encoding.as_str());
let data = match bit_depth {
8 => DataContainer::VecU8(image.data),
16 => DataContainer::VecU8(match image.is_bigendian {
0 => image.data,
1 => image_byteorder_ops::from_be_to_le(&image.data),
_ => Err(format!("Unsupported endianness [endianness: {}]", image.is_bigendian))?
}),
_ => Err(format!("Unsupported bit depth for container [bit depth: {}]", bit_depth))?
};
Ok(CvImage {
header: image.header,
height: image.height as usize,
width: image.width as usize,
encoding: image.encoding,
data: data
})
}
pub fn from_cvmat(mat: Mat, encoding: &str) -> Result<CvImage, Box<dyn Error>> {
let (width, height) = (mat.cols(), mat.rows());
let bit_depth = image_encodings::get_bit_depth(encoding);
let data = match mat.data_bytes() {
Ok(data) => data,
Err(_) => Err(format!("Could not get data from Mat"))?
};
let data = match bit_depth {
8 => DataContainer::VecU8(data.to_vec()),
16 => DataContainer::VecU8(data.to_vec()),
_ => Err(format!("Unsupported bit depth for container [bit depth: {}]", bit_depth))?
};
Ok(CvImage {
header: Header::default(),
height: height as usize,
width: width as usize,
encoding: encoding.to_string(),
data: data,
})
}
pub fn into_imgmsg(self, is_bigendian: u8) -> Result<Image, Box<dyn Error>> {
let step = self.width as u32 * image_encodings::get_num_channels(&self.encoding) as u32;
let data = match self.data {
DataContainer::VecU8(data) => data,
DataContainer::VecU16(data) => image_byteorder_ops::from_u16_to_u8(&data, is_bigendian == 1),
_ => Err(format!("Unsupported container type"))?
};
Ok(Image {
header: self.header,
height: self.height as u32,
width: self.width as u32,
encoding: self.encoding,
is_bigendian: is_bigendian,
step: step,
data: data
})
}
pub fn to_cvimage(&mut self, desired_encoding: &str) -> Result<CvImage, Box<dyn Error>> {
let src_enc = image_encodings::from_encstr_to_cvenc(&self.encoding)?;
let dst_enc = image_encodings::from_encstr_to_cvenc(desired_encoding)?;
let convertion_code = image_encodings::get_conversion_code(src_enc, dst_enc)?;
let src_mat = self.as_cvmat()?;
let mut dst_mat = Mat::default();
opencv::imgproc::cvt_color(&src_mat, &mut dst_mat, convertion_code, 0)?;
let cvtype = image_encodings::from_encstr_to_cvtype(desired_encoding)?;
let scaling = image_encodings::get_scaling_factor(&self.encoding, desired_encoding);
let mut dst2_mat = Mat::default();
dst_mat.convert_to(&mut dst2_mat, cvtype, scaling, 0.0)?;
Ok(CvImage::from_cvmat(dst2_mat, desired_encoding)?)
}
pub fn as_cvmat(&mut self) -> Result<Mat, Box<dyn Error>> {
let cvtype = image_encodings::from_encstr_to_cvtype(&self.encoding)?;
let buffer_mut_ptr = match self.data {
DataContainer::VecU8(ref mut data) => data.as_mut_ptr() as *mut _,
DataContainer::VecU16(ref mut data) => data.as_mut_ptr() as *mut _,
_ => Err(format!("Unsupported container type"))?
};
let mat;
unsafe {
mat = Mat::new_rows_cols_with_data(
self.height as i32,
self.width as i32,
cvtype,
buffer_mut_ptr,
opencv::core::Mat_AUTO_STEP
)?;
}
Ok(mat)
}
pub fn as_container(&self) -> &DataContainer {
&self.data
}
pub fn as_mut_container(&mut self) -> &mut DataContainer {
&mut self.data
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn header_mut(&mut self) -> &mut Header {
&mut self.header
}
pub fn encoding(&self) -> &String {
&self.encoding
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
}