Skip to content

Commit

Permalink
Introduce Mat constructors from bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
twistedfall committed Jul 16, 2024
1 parent c7b4b53 commit 6ed87f0
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 17 deletions.
93 changes: 77 additions & 16 deletions src/manual/core/mat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_::*;

Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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<T: DataType>(s: &[u8]) -> Result<BoxedRef<Self>> {
let rem = s.len() % mem::size_of::<T>();
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::<T>();
Self::new_rows_cols_with_bytes::<T>(1, i32::try_from(len)?, s)
}

/// Create a new `Mat` from a mutable single-dimensional byte slice
#[inline]
pub fn from_bytes_mut<T: DataType>(s: &mut [u8]) -> Result<BoxedRefMut<Self>> {
let rem = s.len() % mem::size_of::<T>();
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::<T>();
Self::new_rows_cols_with_bytes_mut::<T>(1, i32::try_from(len)?, s)
}

/// Create a new `Mat` from a mutable single-dimensional slice
#[inline]
pub fn from_slice_mut<T: DataType>(s: &mut [T]) -> Result<BoxedRefMut<Self>> {
Expand Down Expand Up @@ -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<T: DataType>(rows: i32, cols: i32, data: &[T]) -> Result<BoxedRef<Self>> {
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::<c_void>().cast_mut())
}?;
Ok(<BoxedRef<Mat>>::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<T: DataType>(rows: i32, cols: i32, data: &[u8]) -> Result<BoxedRef<Self>> {
match_length(&[rows, cols], data.len(), mem::size_of::<T>())?;
let m = unsafe {
Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_ptr().cast::<c_void>().cast_mut())
}?;
Ok(<BoxedRef<Mat>>::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<T: DataType>(rows: i32, cols: i32, data: &mut [T]) -> Result<BoxedRefMut<Self>> {
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::<c_void>()) }?;
Ok(<BoxedRefMut<Mat>>::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<T: DataType>(rows: i32, cols: i32, data: &mut [u8]) -> Result<BoxedRefMut<Self>> {
match_length(&[rows, cols], data.len(), mem::size_of::<T>())?;
let m =
unsafe { Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_mut_ptr().cast::<c_void>()) }?;
Ok(<BoxedRefMut<Mat>>::from(m))
Expand All @@ -230,31 +291,31 @@ impl Mat {
/// Create a new `Mat` that references a single-dimensional slice with custom shape
#[inline]
pub fn new_size_with_data<T: DataType>(size: Size, data: &[T]) -> Result<BoxedRef<Self>> {
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::<c_void>().cast_mut()) }?;
Ok(<BoxedRef<Mat>>::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<T: DataType>(size: Size, data: &mut [T]) -> Result<BoxedRefMut<Self>> {
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::<c_void>()) }?;
Ok(<BoxedRefMut<Mat>>::from(m))
}

/// 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<BoxedRef<'data, Self>> {
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::<c_void>().cast_mut()) }?;
Ok(<BoxedRef<Mat>>::from(m))
}

/// 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<BoxedRefMut<'data, Self>> {
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::<c_void>()) }?;
Ok(<BoxedRefMut<Mat>>::from(m))
}
Expand Down
51 changes: 50 additions & 1 deletion tests/mat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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::<Vec3s>(3, 3, &data)?;

assert_eq!(Size::new(3, 3), mat.size()?);
assert_eq!(54, mat.data_bytes()?.len());
assert_eq!(9, mat.data_typed::<Vec3s>()?.len());
}

{
let mut data = vec![0; 2 * 3 * 2 * 4];
let mut mat = Mat::new_rows_cols_with_bytes_mut::<Point>(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::<Point>()?.len());

mat.at_2d_mut::<Point>(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::<Point2f>(&data)?;

assert_eq!(Size::new(6, 1), mat.size()?);
assert_eq!(48, mat.data_bytes()?.len());
assert_eq!(6, mat.data_typed::<Point2f>()?.len());
}

{
let mut data = vec![0; 4 * 4 * 2];
let mut mat = Mat::from_bytes_mut::<Vec4w>(&mut data)?;

assert_eq!(Size::new(4, 1), mat.size()?);
assert_eq!(32, mat.data_bytes()?.len());
assert_eq!(4, mat.data_typed::<Vec4w>()?.len());

mat.at_mut::<Vec4w>(2)?[1] = 123;
assert!(data[18..20].contains(&123));
}

Ok(())
}

#[test]
fn mat_from_slice_2d() -> Result<()> {
{
Expand Down

0 comments on commit 6ed87f0

Please sign in to comment.