diff --git a/src/manual/core/mat.rs b/src/manual/core/mat.rs index 23c674f1..77a437a9 100644 --- a/src/manual/core/mat.rs +++ b/src/manual/core/mat.rs @@ -2,7 +2,7 @@ use std::convert::TryInto; use std::ffi::c_void; use std::marker::PhantomData; use std::ops::Deref; -use std::{fmt, ptr, slice}; +use std::{fmt, mem, ptr, slice}; pub use mat_::*; @@ -97,23 +97,29 @@ fn match_is_continuous(mat: &(impl MatTraitConst + ?Sized)) -> Result<()> { } } -fn match_length(sizes: &[i32], len: usize) -> Result<()> { +fn match_length(sizes: &[i32], slice_len: usize, size_mul: usize) -> Result<()> { if sizes.is_empty() { return Err(Error::new(core::StsUnmatchedSizes, "Dimensions must not be empty")); } - let mut volume: u64 = 1; + let mut expected_len: u64 = 1; for (i, size) in sizes.iter().enumerate() { let size = u64::try_from(*size).map_err(|_| Error::new(core::StsOutOfRange, format!("Dimension {i} must not be negative")))?; - volume = volume.saturating_mul(size); + expected_len = expected_len.saturating_mul(size); } - let data_len = u64::try_from(len).map_err(|_| Error::new(core::StsOutOfRange, "Length must fit in u64"))?; - if volume != data_len { + if size_mul > 1 { + // cast is safe because of the `> 1` check above + expected_len = expected_len.saturating_mul(size_mul as u64); + } + let slice_len = u64::try_from(slice_len).map_err(|_| Error::new(core::StsOutOfRange, "Length must fit in u64"))?; + if expected_len != slice_len { let msg = match sizes { [rows, cols] => { - format!("The length of the slice: {data_len} must match the passed row: {rows} and column: {cols} counts exactly") + format!("The length of the slice: {slice_len} must be: {expected_len} to match the passed row: {rows} and column: {cols} counts") + } + _ => { + format!("The length of the slice: {slice_len} must be: {expected_len} to match the passed dimensions: {sizes:?}") } - _ => format!("The length of the slice: {data_len} must match the passed dimensions: {sizes:?} exactly"), }; return Err(Error::new(core::StsUnmatchedSizes, msg)); } @@ -165,6 +171,42 @@ impl Mat { Self::new_rows_cols_with_data(1, i32::try_from(s.len())?, s) } + /// Create a new `Mat` from a single-dimensional byte slice + #[inline] + pub fn from_bytes(s: &[u8]) -> Result> { + let rem = s.len() % mem::size_of::(); + if rem != 0 { + return Err(Error::new( + core::StsBadArg, + format!( + "Unexpected number of bytes: {} the indicated type, expected multiple of {}", + s.len(), + T::opencv_channels() + ), + )); + } + let len = s.len() / mem::size_of::(); + Self::new_rows_cols_with_bytes::(1, i32::try_from(len)?, s) + } + + /// Create a new `Mat` from a mutable single-dimensional byte slice + #[inline] + pub fn from_bytes_mut(s: &mut [u8]) -> Result> { + let rem = s.len() % mem::size_of::(); + if rem != 0 { + return Err(Error::new( + core::StsBadArg, + format!( + "Unexpected number of bytes: {} the indicated type, expected multiple of {}", + s.len(), + T::opencv_channels() + ), + )); + } + let len = s.len() / mem::size_of::(); + Self::new_rows_cols_with_bytes_mut::(1, i32::try_from(len)?, s) + } + /// Create a new `Mat` from a mutable single-dimensional slice #[inline] pub fn from_slice_mut(s: &mut [T]) -> Result> { @@ -211,17 +253,36 @@ impl Mat { /// Create a new `Mat` that references a single-dimensional slice with custom shape #[inline] pub fn new_rows_cols_with_data(rows: i32, cols: i32, data: &[T]) -> Result> { - match_length(&[rows, cols], data.len())?; + match_length(&[rows, cols], data.len(), 1)?; let m = unsafe { Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_ptr().cast::().cast_mut()) }?; Ok(>::from(m)) } - /// Create a new `Mat` that references a single-dimensional slice with custom shape + /// Create a new `Mat` that references a single-dimensional byte slice with custom shape + #[inline] + pub fn new_rows_cols_with_bytes(rows: i32, cols: i32, data: &[u8]) -> Result> { + match_length(&[rows, cols], data.len(), mem::size_of::())?; + let m = unsafe { + Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_ptr().cast::().cast_mut()) + }?; + Ok(>::from(m)) + } + + /// Create a new mutable `Mat` that references a single-dimensional slice with custom shape #[inline] pub fn new_rows_cols_with_data_mut(rows: i32, cols: i32, data: &mut [T]) -> Result> { - match_length(&[rows, cols], data.len())?; + match_length(&[rows, cols], data.len(), 1)?; + let m = + unsafe { Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_mut_ptr().cast::()) }?; + Ok(>::from(m)) + } + + /// Create a new mutable `Mat` that references a single-dimensional byte slice with custom shape + #[inline] + pub fn new_rows_cols_with_bytes_mut(rows: i32, cols: i32, data: &mut [u8]) -> Result> { + match_length(&[rows, cols], data.len(), mem::size_of::())?; let m = unsafe { Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_mut_ptr().cast::()) }?; Ok(>::from(m)) @@ -230,15 +291,15 @@ impl Mat { /// Create a new `Mat` that references a single-dimensional slice with custom shape #[inline] pub fn new_size_with_data(size: Size, data: &[T]) -> Result> { - match_length(&[size.width, size.height], data.len())?; + match_length(&[size.width, size.height], data.len(), 1)?; let m = unsafe { Self::new_size_with_data_unsafe_def(size, T::opencv_type(), data.as_ptr().cast::().cast_mut()) }?; Ok(>::from(m)) } - /// Create a new `Mat` that references a single-dimensional slice with custom shape + /// Create a new mutable `Mat` that references a single-dimensional slice with custom shape #[inline] pub fn new_size_with_data_mut(size: Size, data: &mut [T]) -> Result> { - match_length(&[size.width, size.height], data.len())?; + match_length(&[size.width, size.height], data.len(), 1)?; let m = unsafe { Self::new_size_with_data_unsafe_def(size, T::opencv_type(), data.as_mut_ptr().cast::()) }?; Ok(>::from(m)) } @@ -246,7 +307,7 @@ impl Mat { /// Create a new `Mat` that references a single-dimensional slice with custom shape #[inline] pub fn new_nd_with_data<'data, T: DataType>(sizes: &[i32], data: &'data [T]) -> Result> { - match_length(sizes, data.len())?; + match_length(sizes, data.len(), 1)?; let m = unsafe { Self::new_nd_with_data_unsafe_def(sizes, T::opencv_type(), data.as_ptr().cast::().cast_mut()) }?; Ok(>::from(m)) } @@ -254,7 +315,7 @@ impl Mat { /// Create a new `Mat` that references a single-dimensional slice with custom shape #[inline] pub fn new_nd_with_data_mut<'data, T: DataType>(sizes: &[i32], data: &'data mut [T]) -> Result> { - match_length(sizes, data.len())?; + match_length(sizes, data.len(), 1)?; let m = unsafe { Self::new_nd_with_data_unsafe_def(sizes, T::opencv_type(), data.as_mut_ptr().cast::()) }?; Ok(>::from(m)) } diff --git a/tests/mat.rs b/tests/mat.rs index 7d846225..63e6af46 100644 --- a/tests/mat.rs +++ b/tests/mat.rs @@ -3,7 +3,9 @@ use std::mem; use matches::assert_matches; -use opencv::core::{MatConstIterator, MatIter, Point, Point2d, Rect, Scalar, Size, Vec2b, Vec2s, Vec3d, Vec3f, Vec4w, Vector}; +use opencv::core::{ + MatConstIterator, MatIter, Point, Point2d, Point2f, Rect, Scalar, Size, Vec2b, Vec2s, Vec3d, Vec3f, Vec3s, Vec4w, Vector, +}; use opencv::prelude::*; use opencv::{core, imgproc, Error, Result}; const PIXEL: &[u8] = include_bytes!("pixel.png"); @@ -1097,6 +1099,53 @@ fn mat_from_slice() -> Result<()> { Ok(()) } +#[test] +fn mat_from_bytes() -> Result<()> { + { + let data = vec![0; 3 * 3 * 3 * 2]; + let mat = Mat::new_rows_cols_with_bytes::(3, 3, &data)?; + + assert_eq!(Size::new(3, 3), mat.size()?); + assert_eq!(54, mat.data_bytes()?.len()); + assert_eq!(9, mat.data_typed::()?.len()); + } + + { + let mut data = vec![0; 2 * 3 * 2 * 4]; + let mut mat = Mat::new_rows_cols_with_bytes_mut::(3, 2, &mut data)?; + + assert_eq!(Size::new(2, 3), mat.size()?); + assert_eq!(48, mat.data_bytes()?.len()); + assert_eq!(6, mat.data_typed::()?.len()); + + mat.at_2d_mut::(1, 1)?.y = 15; + assert!(data[7 * 4..7 * 4 + 4].contains(&15)); + } + + { + let data = vec![0; 6 * 2 * 4]; + let mat = Mat::from_bytes::(&data)?; + + assert_eq!(Size::new(6, 1), mat.size()?); + assert_eq!(48, mat.data_bytes()?.len()); + assert_eq!(6, mat.data_typed::()?.len()); + } + + { + let mut data = vec![0; 4 * 4 * 2]; + let mut mat = Mat::from_bytes_mut::(&mut data)?; + + assert_eq!(Size::new(4, 1), mat.size()?); + assert_eq!(32, mat.data_bytes()?.len()); + assert_eq!(4, mat.data_typed::()?.len()); + + mat.at_mut::(2)?[1] = 123; + assert!(data[18..20].contains(&123)); + } + + Ok(()) +} + #[test] fn mat_from_slice_2d() -> Result<()> { {