use super::*; use core::ffi::*; use opencv::core::prelude::*; pub(crate) unsafe fn ndarray_to_mat_regular< T, S: ndarray::Data, D: ndarray::Dimension, >( input: &ndarray::ArrayBase, ) -> Result { let shape = input.shape(); let strides = input.strides(); // let channels = shape.last().copied().unwrap_or(1); // if channels > opencv::core::CV_CN_MAX as usize { // Err(Report::new(NdCvError).attach_printable(format!( // "Number of channels({channels}) exceeds CV_CN_MAX({}) use the regular version of the function", opencv::core::CV_CN_MAX // )))?; // } // let size_len = shape.len(); let size = shape.iter().copied().map(|f| f as i32).collect::>(); // Step len for ndarray is always 1 less than ndims let step_len = strides.len() - 1; let step = strides .iter() .take(step_len) .copied() .map(|f| f as usize * core::mem::size_of::()) .collect::>(); let data_ptr = input.as_ptr() as *const c_void; let typ = opencv::core::CV_MAKETYPE(type_depth::(), 1); let mat = opencv::core::Mat::new_nd_with_data_unsafe( size.as_slice(), typ, data_ptr.cast_mut(), Some(step.as_slice()), ) .change_context(NdCvError)?; Ok(mat) } pub(crate) unsafe fn ndarray_to_mat_consolidated< T, S: ndarray::Data, D: ndarray::Dimension, >( input: &ndarray::ArrayBase, ) -> Result { let shape = input.shape(); let strides = input.strides(); let channels = shape.last().copied().unwrap_or(1); if channels > opencv::core::CV_CN_MAX as usize { Err(Report::new(NdCvError).attach_printable(format!( "Number of channels({channels}) exceeds CV_CN_MAX({}) use the regular version of the function", opencv::core::CV_CN_MAX )))?; } if shape.len() > 2 { // Basically the second last stride is used to jump from one column to next // But opencv only keeps ndims - 1 strides so we can't have the column stride as that // will be lost if shape.last() != strides.get(strides.len() - 2).map(|x| *x as usize).as_ref() { Err(Report::new(NdCvError).attach_printable( "You cannot slice into the last axis in ndarray when converting to mat", ))?; } } else if shape.len() == 1 { return Err(Report::new(NdCvError).attach_printable( "You cannot convert a 1D array to a Mat while using the consolidated version", )); } // Since this is the consolidated version we should always only have ndims - 1 sizes and // ndims - 2 strides let size_len = shape.len() - 1; // Since we move last axis into the channel let size = shape .iter() .take(size_len) .map(|f| *f as i32) .collect::>(); let step_len = strides.len() - 1; let step = strides .iter() .take(step_len) .map(|f| *f as usize * core::mem::size_of::()) .collect::>(); let data_ptr = input.as_ptr() as *const c_void; let typ = opencv::core::CV_MAKETYPE(type_depth::(), channels as i32); let mat = opencv::core::Mat::new_nd_with_data_unsafe( size.as_slice(), typ, data_ptr.cast_mut(), Some(step.as_slice()), ) .change_context(NdCvError)?; Ok(mat) } pub(crate) unsafe fn mat_to_ndarray( mat: &opencv::core::Mat, ) -> Result, NdCvError> { let depth = mat.depth(); if type_depth::() != depth { return Err(Report::new(NdCvError).attach_printable(format!( "Expected type Mat<{}> ({}), got Mat<{}> ({})", std::any::type_name::(), type_depth::(), crate::depth_type(depth), depth, ))); } // Since a dims always returns >= 2 we can't use this to check if it's a 1D array // So we compare the first axis to the total to see if its a 1D array let is_1d = mat.total() as i32 == mat.rows(); let dims = is_1d.then_some(1).unwrap_or(mat.dims()); let channels = mat.channels(); let ndarray_size = (channels != 1).then_some(dims + 1).unwrap_or(dims) as usize; if let Some(ndim) = D::NDIM { // When channels is not 1, // the last dimension is the channels // Array1 -> Mat(ndims = 1, channels = 1) // Array2 -> Mat(ndims = 1, channels = X) // Array2 -> Mat(ndims = 2, channels = 1) // Array3 -> Mat(ndims = 2, channels = X) // Array3 -> Mat(ndims = 3, channels = 1) // ... if ndim != dims as usize && channels == 1 { return Err(Report::new(NdCvError) .attach_printable(format!("Expected {}D array, got {}D", ndim, ndarray_size))); } } let mat_size = mat.mat_size(); let sizes = (0..dims) .map(|i| mat_size.get(i).change_context(NdCvError)) .chain([Ok(channels)]) .map(|x| x.map(|x| x as usize)) .take(ndarray_size) .collect::, NdCvError>>() .change_context(NdCvError)?; let strides = (0..(dims - 1)) .map(|i| mat.step1(i).change_context(NdCvError)) .chain([ Ok(channels as usize), Ok((channels == 1).then_some(0).unwrap_or(1)), ]) .take(ndarray_size) .collect::, NdCvError>>() .change_context(NdCvError)?; use ndarray::ShapeBuilder; let shape = sizes.strides(strides); let raw_array = ndarray::RawArrayView::from_shape_ptr(shape, mat.data() as *const T) .into_dimensionality() .change_context(NdCvError)?; Ok(unsafe { raw_array.deref_into_view() }) }