338 lines
11 KiB
Rust
338 lines
11 KiB
Rust
//! Mat <--> ndarray conversion traits
|
|
//!
|
|
//! Conversion Table
|
|
//!
|
|
//! | ndarray | Mat |
|
|
//! |--------- |----- |
|
|
//! | Array<T, Ix1> | Mat(ndims = 1, channels = 1) |
|
|
//! | Array<T, Ix2> | Mat(ndims = 2, channels = 1) |
|
|
//! | Array<T, Ix2> | Mat(ndims = 1, channels = X) |
|
|
//! | Array<T, Ix3> | Mat(ndims = 3, channels = 1) |
|
|
//! | Array<T, Ix3> | Mat(ndims = 2, channels = X) |
|
|
//! | Array<T, Ix4> | Mat(ndims = 4, channels = 1) |
|
|
//! | Array<T, Ix4> | Mat(ndims = 3, channels = X) |
|
|
//! | Array<T, Ix5> | Mat(ndims = 5, channels = 1) |
|
|
//! | Array<T, Ix5> | Mat(ndims = 4, channels = X) |
|
|
//! | Array<T, Ix6> | Mat(ndims = 6, channels = 1) |
|
|
//! | Array<T, Ix6> | Mat(ndims = 5, channels = X) |
|
|
//!
|
|
//! // X is the last dimension
|
|
use crate::NdCvError;
|
|
use crate::type_depth;
|
|
use error_stack::*;
|
|
use ndarray::{Ix2, Ix3};
|
|
use opencv::core::MatTraitConst;
|
|
mod impls;
|
|
pub(crate) mod matref;
|
|
use matref::{MatRef, MatRefMut};
|
|
|
|
pub(crate) mod seal {
|
|
pub trait SealedInternal {}
|
|
impl<T, S: ndarray::Data<Elem = T>, D> SealedInternal for ndarray::ArrayBase<S, D> {}
|
|
// impl<T, S: ndarray::DataMut<Elem = T>, D> SealedInternal for ndarray::ArrayBase<S, D> {}
|
|
}
|
|
|
|
pub trait NdCvConversion<T: bytemuck::Pod + Copy, D: ndarray::Dimension>:
|
|
seal::SealedInternal + Sized
|
|
{
|
|
fn to_mat(&self) -> Result<opencv::core::Mat, NdCvError>;
|
|
fn from_mat(
|
|
mat: opencv::core::Mat,
|
|
) -> Result<ndarray::ArrayBase<ndarray::OwnedRepr<T>, D>, NdCvError>;
|
|
}
|
|
|
|
impl<T: bytemuck::Pod + Copy, S: ndarray::Data<Elem = T>, D: ndarray::Dimension>
|
|
NdCvConversion<T, D> for ndarray::ArrayBase<S, D>
|
|
where
|
|
Self: NdAsImage<T, D>,
|
|
{
|
|
fn to_mat(&self) -> Result<opencv::core::Mat, NdCvError> {
|
|
Ok(self.as_image_mat()?.mat.clone())
|
|
}
|
|
|
|
fn from_mat(
|
|
mat: opencv::core::Mat,
|
|
) -> Result<ndarray::ArrayBase<ndarray::OwnedRepr<T>, D>, NdCvError> {
|
|
let ndarray = unsafe { impls::mat_to_ndarray::<T, D>(&mat) }.change_context(NdCvError)?;
|
|
Ok(ndarray.to_owned())
|
|
}
|
|
}
|
|
|
|
pub trait MatAsNd {
|
|
fn as_ndarray<T: bytemuck::Pod, D: ndarray::Dimension>(
|
|
&self,
|
|
) -> Result<ndarray::ArrayView<T, D>, NdCvError>;
|
|
}
|
|
|
|
impl MatAsNd for opencv::core::Mat {
|
|
fn as_ndarray<T: bytemuck::Pod, D: ndarray::Dimension>(
|
|
&self,
|
|
) -> Result<ndarray::ArrayView<T, D>, NdCvError> {
|
|
unsafe { impls::mat_to_ndarray::<T, D>(self) }.change_context(NdCvError)
|
|
}
|
|
}
|
|
|
|
pub trait NdAsMat<T: bytemuck::Pod + Copy, D: ndarray::Dimension> {
|
|
fn as_single_channel_mat(&self) -> Result<MatRef, NdCvError>;
|
|
fn as_multi_channel_mat(&self) -> Result<MatRef, NdCvError>;
|
|
}
|
|
|
|
pub trait NdAsMatMut<T: bytemuck::Pod + Copy, D: ndarray::Dimension>: NdAsMat<T, D> {
|
|
fn as_single_channel_mat_mut(&mut self) -> Result<MatRefMut, NdCvError>;
|
|
fn as_multi_channel_mat_mut(&mut self) -> Result<MatRefMut, NdCvError>;
|
|
}
|
|
|
|
impl<T: bytemuck::Pod, S: ndarray::Data<Elem = T>, D: ndarray::Dimension> NdAsMat<T, D>
|
|
for ndarray::ArrayBase<S, D>
|
|
{
|
|
fn as_single_channel_mat(&self) -> Result<MatRef, NdCvError> {
|
|
let mat = unsafe { impls::ndarray_to_mat_regular(self) }.change_context(NdCvError)?;
|
|
Ok(MatRef::new(mat))
|
|
}
|
|
fn as_multi_channel_mat(&self) -> Result<MatRef, NdCvError> {
|
|
let mat = unsafe { impls::ndarray_to_mat_consolidated(self) }.change_context(NdCvError)?;
|
|
Ok(MatRef::new(mat))
|
|
}
|
|
}
|
|
|
|
impl<T: bytemuck::Pod, S: ndarray::DataMut<Elem = T>, D: ndarray::Dimension> NdAsMatMut<T, D>
|
|
for ndarray::ArrayBase<S, D>
|
|
{
|
|
fn as_single_channel_mat_mut(&mut self) -> Result<MatRefMut, NdCvError> {
|
|
let mat = unsafe { impls::ndarray_to_mat_regular(self) }.change_context(NdCvError)?;
|
|
Ok(MatRefMut::new(mat))
|
|
}
|
|
|
|
fn as_multi_channel_mat_mut(&mut self) -> Result<MatRefMut, NdCvError> {
|
|
let mat = unsafe { impls::ndarray_to_mat_consolidated(self) }.change_context(NdCvError)?;
|
|
Ok(MatRefMut::new(mat))
|
|
}
|
|
}
|
|
|
|
pub trait NdAsImage<T: bytemuck::Pod, D: ndarray::Dimension> {
|
|
fn as_image_mat(&self) -> Result<MatRef, NdCvError>;
|
|
}
|
|
|
|
pub trait NdAsImageMut<T: bytemuck::Pod, D: ndarray::Dimension> {
|
|
fn as_image_mat_mut(&mut self) -> Result<MatRefMut, NdCvError>;
|
|
}
|
|
|
|
impl<T, S> NdAsImage<T, Ix2> for ndarray::ArrayBase<S, Ix2>
|
|
where
|
|
T: bytemuck::Pod + Copy,
|
|
S: ndarray::Data<Elem = T>,
|
|
{
|
|
fn as_image_mat(&self) -> Result<MatRef, NdCvError> {
|
|
self.as_single_channel_mat()
|
|
}
|
|
}
|
|
|
|
impl<T, S> NdAsImageMut<T, Ix2> for ndarray::ArrayBase<S, Ix2>
|
|
where
|
|
T: bytemuck::Pod + Copy,
|
|
S: ndarray::DataMut<Elem = T>,
|
|
{
|
|
fn as_image_mat_mut(&mut self) -> Result<MatRefMut, NdCvError> {
|
|
self.as_single_channel_mat_mut()
|
|
}
|
|
}
|
|
|
|
impl<T, S> NdAsImage<T, Ix3> for ndarray::ArrayBase<S, Ix3>
|
|
where
|
|
T: bytemuck::Pod + Copy,
|
|
S: ndarray::Data<Elem = T>,
|
|
{
|
|
fn as_image_mat(&self) -> Result<MatRef, NdCvError> {
|
|
self.as_multi_channel_mat()
|
|
}
|
|
}
|
|
|
|
impl<T, S> NdAsImageMut<T, Ix3> for ndarray::ArrayBase<S, Ix3>
|
|
where
|
|
T: bytemuck::Pod + Copy,
|
|
S: ndarray::DataMut<Elem = T>,
|
|
{
|
|
fn as_image_mat_mut(&mut self) -> Result<MatRefMut, NdCvError> {
|
|
self.as_multi_channel_mat_mut()
|
|
}
|
|
}
|
|
|
|
// #[test]
|
|
// fn test_1d_mat_to_ndarray() {
|
|
// let mat = opencv::core::Mat::new_nd_with_default(
|
|
// &[10],
|
|
// opencv::core::CV_MAKE_TYPE(opencv::core::CV_8U, 1),
|
|
// 200.into(),
|
|
// )
|
|
// .expect("failed");
|
|
// let array: ndarray::ArrayView1<u8> = mat.as_ndarray().expect("failed");
|
|
// array.into_iter().for_each(|&x| assert_eq!(x, 200));
|
|
// }
|
|
|
|
// #[test]
|
|
// fn test_2d_mat_to_ndarray() {
|
|
// let mat = opencv::core::Mat::new_nd_with_default(
|
|
// &[10],
|
|
// opencv::core::CV_16SC3,
|
|
// (200, 200, 200).into(),
|
|
// )
|
|
// .expect("failed");
|
|
// let array2: ndarray::ArrayView2<i16> = mat.as_ndarray().expect("failed");
|
|
// assert_eq!(array2.shape(), [10, 3]);
|
|
// array2.into_iter().for_each(|&x| {
|
|
// assert_eq!(x, 200);
|
|
// });
|
|
// }
|
|
|
|
// #[test]
|
|
// fn test_3d_mat_to_ndarray() {
|
|
// let mat = opencv::core::Mat::new_nd_with_default(
|
|
// &[20, 30],
|
|
// opencv::core::CV_32FC3,
|
|
// (200, 200, 200).into(),
|
|
// )
|
|
// .expect("failed");
|
|
// let array2: ndarray::ArrayView3<f32> = mat.as_ndarray().expect("failed");
|
|
// array2.into_iter().for_each(|&x| {
|
|
// assert_eq!(x, 200f32);
|
|
// });
|
|
// }
|
|
|
|
// #[test]
|
|
// fn test_mat_to_dyn_ndarray() {
|
|
// let mat = opencv::core::Mat::new_nd_with_default(&[10], opencv::core::CV_8UC1, 200.into())
|
|
// .expect("failed");
|
|
// let array2: ndarray::ArrayViewD<u8> = mat.as_ndarray().expect("failed");
|
|
// array2.into_iter().for_each(|&x| assert_eq!(x, 200));
|
|
// }
|
|
|
|
// #[test]
|
|
// fn test_3d_mat_to_ndarray_4k() {
|
|
// let mat = opencv::core::Mat::new_nd_with_default(
|
|
// &[4096, 4096],
|
|
// opencv::core::CV_8UC3,
|
|
// (255, 0, 255).into(),
|
|
// )
|
|
// .expect("failed");
|
|
// let array2: ndarray::ArrayView3<u8> = (mat).as_ndarray().expect("failed");
|
|
// array2.exact_chunks((1, 1, 3)).into_iter().for_each(|x| {
|
|
// assert_eq!(x[(0, 0, 0)], 255);
|
|
// assert_eq!(x[(0, 0, 1)], 0);
|
|
// assert_eq!(x[(0, 0, 2)], 255);
|
|
// });
|
|
// }
|
|
|
|
// // #[test]
|
|
// // fn test_3d_mat_to_ndarray_8k() {
|
|
// // let mat = opencv::core::Mat::new_nd_with_default(
|
|
// // &[8192, 8192],
|
|
// // opencv::core::CV_8UC3,
|
|
// // (255, 0, 255).into(),
|
|
// // )
|
|
// // .expect("failed");
|
|
// // let array2 = ndarray::Array3::<u8>::from_mat(mat).expect("failed");
|
|
// // array2.exact_chunks((1, 1, 3)).into_iter().for_each(|x| {
|
|
// // assert_eq!(x[(0, 0, 0)], 255);
|
|
// // assert_eq!(x[(0, 0, 1)], 0);
|
|
// // assert_eq!(x[(0, 0, 2)], 255);
|
|
// // });
|
|
// // }
|
|
|
|
// #[test]
|
|
// pub fn test_mat_to_nd_default_strides() {
|
|
// let mat = opencv::core::Mat::new_rows_cols_with_default(
|
|
// 10,
|
|
// 10,
|
|
// opencv::core::CV_8UC3,
|
|
// opencv::core::VecN([10f64, 0.0, 0.0, 0.0]),
|
|
// )
|
|
// .expect("failed");
|
|
// let array = unsafe { impls::mat_to_ndarray::<u8, Ix3>(&mat) }.expect("failed");
|
|
// assert_eq!(array.shape(), [10, 10, 3]);
|
|
// assert_eq!(array.strides(), [30, 3, 1]);
|
|
// assert_eq!(array[(0, 0, 0)], 10);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_mat_to_nd_custom_strides() {
|
|
// let mat = opencv::core::Mat::new_rows_cols_with_default(
|
|
// 10,
|
|
// 10,
|
|
// opencv::core::CV_8UC3,
|
|
// opencv::core::VecN([10f64, 0.0, 0.0, 0.0]),
|
|
// )
|
|
// .unwrap();
|
|
// let mat_roi = opencv::core::Mat::roi(&mat, opencv::core::Rect::new(3, 2, 3, 5))
|
|
// .expect("failed to get roi");
|
|
// let array = unsafe { impls::mat_to_ndarray::<u8, Ix3>(&mat_roi) }.expect("failed");
|
|
// assert_eq!(array.shape(), [5, 3, 3]);
|
|
// assert_eq!(array.strides(), [30, 3, 1]);
|
|
// assert_eq!(array[(0, 0, 0)], 10);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_non_continuous_3d() {
|
|
// let array = ndarray::Array3::<f32>::from_shape_fn((10, 10, 4), |(i, j, k)| {
|
|
// ((i + 1) * (j + 1) * (k + 1)) as f32
|
|
// });
|
|
// let slice = array.slice(ndarray::s![3..7, 3..7, 0..4]);
|
|
// let mat = unsafe { impls::ndarray_to_mat_consolidated(&slice) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, Ix3>(&mat).unwrap() };
|
|
// assert!(slice == arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_5d_array() {
|
|
// let array = ndarray::Array5::<f32>::ones((1, 2, 3, 4, 5));
|
|
// let mat = unsafe { impls::ndarray_to_mat_consolidated(&array) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, ndarray::Ix5>(&mat).unwrap() };
|
|
// assert_eq!(array, arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_3d_array() {
|
|
// let array = ndarray::Array3::<f32>::ones((23, 31, 33));
|
|
// let mat = unsafe { impls::ndarray_to_mat_consolidated(&array) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, ndarray::Ix3>(&mat).unwrap() };
|
|
// assert_eq!(array, arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_2d_array() {
|
|
// let array = ndarray::Array2::<f32>::ones((23, 31));
|
|
// let mat = unsafe { impls::ndarray_to_mat_consolidated(&array) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, ndarray::Ix2>(&mat).unwrap() };
|
|
// assert_eq!(array, arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// #[should_panic]
|
|
// pub fn test_1d_array_consolidated() {
|
|
// let array = ndarray::Array1::<f32>::ones(23);
|
|
// let mat = unsafe { impls::ndarray_to_mat_consolidated(&array) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, ndarray::Ix1>(&mat).unwrap() };
|
|
// assert_eq!(array, arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_1d_array_regular() {
|
|
// let array = ndarray::Array1::<f32>::ones(23);
|
|
// let mat = unsafe { impls::ndarray_to_mat_regular(&array) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, ndarray::Ix1>(&mat).unwrap() };
|
|
// assert_eq!(array, arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_2d_array_regular() {
|
|
// let array = ndarray::Array2::<f32>::ones((23, 31));
|
|
// let mat = unsafe { impls::ndarray_to_mat_regular(&array) }.unwrap();
|
|
// let arr = unsafe { impls::mat_to_ndarray::<f32, ndarray::Ix2>(&mat).unwrap() };
|
|
// assert_eq!(array, arr);
|
|
// }
|
|
|
|
// #[test]
|
|
// pub fn test_ndcv_1024_1024_to_mat() {
|
|
// let array = ndarray::Array2::<f32>::ones((1024, 1024));
|
|
// let _mat = array.to_mat().unwrap();
|
|
// }
|