Skip to content

Commit f4f3b6a

Browse files
committed
Introduce Mat constructors from bytes
1 parent 2bde888 commit f4f3b6a

File tree

2 files changed

+127
-17
lines changed

2 files changed

+127
-17
lines changed

src/manual/core/mat.rs

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::convert::TryInto;
22
use std::ffi::c_void;
33
use std::marker::PhantomData;
44
use std::ops::Deref;
5-
use std::{fmt, ptr, slice};
5+
use std::{fmt, mem, ptr, slice};
66

77
pub use mat_::*;
88

@@ -97,23 +97,29 @@ fn match_is_continuous(mat: &(impl MatTraitConst + ?Sized)) -> Result<()> {
9797
}
9898
}
9999

100-
fn match_length(sizes: &[i32], len: usize) -> Result<()> {
100+
fn match_length(sizes: &[i32], slice_len: usize, size_mul: usize) -> Result<()> {
101101
if sizes.is_empty() {
102102
return Err(Error::new(core::StsUnmatchedSizes, "Dimensions must not be empty"));
103103
}
104-
let mut volume: u64 = 1;
104+
let mut expected_len: u64 = 1;
105105
for (i, size) in sizes.iter().enumerate() {
106106
let size =
107107
u64::try_from(*size).map_err(|_| Error::new(core::StsOutOfRange, format!("Dimension {i} must not be negative")))?;
108-
volume = volume.saturating_mul(size);
108+
expected_len = expected_len.saturating_mul(size);
109109
}
110-
let data_len = u64::try_from(len).map_err(|_| Error::new(core::StsOutOfRange, "Length must fit in u64"))?;
111-
if volume != data_len {
110+
if size_mul > 1 {
111+
// cast is safe because of the `> 1` check above
112+
expected_len = expected_len.saturating_mul(size_mul as u64);
113+
}
114+
let slice_len = u64::try_from(slice_len).map_err(|_| Error::new(core::StsOutOfRange, "Length must fit in u64"))?;
115+
if expected_len != slice_len {
112116
let msg = match sizes {
113117
[rows, cols] => {
114-
format!("The length of the slice: {data_len} must match the passed row: {rows} and column: {cols} counts exactly")
118+
format!("The length of the slice: {slice_len} must be: {expected_len} to match the passed row: {rows} and column: {cols} counts")
119+
}
120+
_ => {
121+
format!("The length of the slice: {slice_len} must be: {expected_len} to match the passed dimensions: {sizes:?}")
115122
}
116-
_ => format!("The length of the slice: {data_len} must match the passed dimensions: {sizes:?} exactly"),
117123
};
118124
return Err(Error::new(core::StsUnmatchedSizes, msg));
119125
}
@@ -165,6 +171,42 @@ impl Mat {
165171
Self::new_rows_cols_with_data(1, i32::try_from(s.len())?, s)
166172
}
167173

174+
/// Create a new `Mat` from a single-dimensional byte slice
175+
#[inline]
176+
pub fn from_bytes<T: DataType>(s: &[u8]) -> Result<BoxedRef<Self>> {
177+
let rem = s.len() % mem::size_of::<T>();
178+
if rem != 0 {
179+
return Err(Error::new(
180+
core::StsBadArg,
181+
format!(
182+
"Unexpected number of bytes: {} the indicated type, expected multiple of {}",
183+
s.len(),
184+
T::opencv_channels()
185+
),
186+
));
187+
}
188+
let len = s.len() / mem::size_of::<T>();
189+
Self::new_rows_cols_with_bytes::<T>(1, i32::try_from(len)?, s)
190+
}
191+
192+
/// Create a new `Mat` from a mutable single-dimensional byte slice
193+
#[inline]
194+
pub fn from_bytes_mut<T: DataType>(s: &mut [u8]) -> Result<BoxedRefMut<Self>> {
195+
let rem = s.len() % mem::size_of::<T>();
196+
if rem != 0 {
197+
return Err(Error::new(
198+
core::StsBadArg,
199+
format!(
200+
"Unexpected number of bytes: {} the indicated type, expected multiple of {}",
201+
s.len(),
202+
T::opencv_channels()
203+
),
204+
));
205+
}
206+
let len = s.len() / mem::size_of::<T>();
207+
Self::new_rows_cols_with_bytes_mut::<T>(1, i32::try_from(len)?, s)
208+
}
209+
168210
/// Create a new `Mat` from a mutable single-dimensional slice
169211
#[inline]
170212
pub fn from_slice_mut<T: DataType>(s: &mut [T]) -> Result<BoxedRefMut<Self>> {
@@ -211,17 +253,36 @@ impl Mat {
211253
/// Create a new `Mat` that references a single-dimensional slice with custom shape
212254
#[inline]
213255
pub fn new_rows_cols_with_data<T: DataType>(rows: i32, cols: i32, data: &[T]) -> Result<BoxedRef<Self>> {
214-
match_length(&[rows, cols], data.len())?;
256+
match_length(&[rows, cols], data.len(), 1)?;
215257
let m = unsafe {
216258
Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_ptr().cast::<c_void>().cast_mut())
217259
}?;
218260
Ok(<BoxedRef<Mat>>::from(m))
219261
}
220262

221-
/// Create a new `Mat` that references a single-dimensional slice with custom shape
263+
/// Create a new `Mat` that references a single-dimensional byte slice with custom shape
264+
#[inline]
265+
pub fn new_rows_cols_with_bytes<T: DataType>(rows: i32, cols: i32, data: &[u8]) -> Result<BoxedRef<Self>> {
266+
match_length(&[rows, cols], data.len(), mem::size_of::<T>())?;
267+
let m = unsafe {
268+
Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_ptr().cast::<c_void>().cast_mut())
269+
}?;
270+
Ok(<BoxedRef<Mat>>::from(m))
271+
}
272+
273+
/// Create a new mutable `Mat` that references a single-dimensional slice with custom shape
222274
#[inline]
223275
pub fn new_rows_cols_with_data_mut<T: DataType>(rows: i32, cols: i32, data: &mut [T]) -> Result<BoxedRefMut<Self>> {
224-
match_length(&[rows, cols], data.len())?;
276+
match_length(&[rows, cols], data.len(), 1)?;
277+
let m =
278+
unsafe { Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_mut_ptr().cast::<c_void>()) }?;
279+
Ok(<BoxedRefMut<Mat>>::from(m))
280+
}
281+
282+
/// Create a new mutable `Mat` that references a single-dimensional byte slice with custom shape
283+
#[inline]
284+
pub fn new_rows_cols_with_bytes_mut<T: DataType>(rows: i32, cols: i32, data: &mut [u8]) -> Result<BoxedRefMut<Self>> {
285+
match_length(&[rows, cols], data.len(), mem::size_of::<T>())?;
225286
let m =
226287
unsafe { Self::new_rows_cols_with_data_unsafe_def(rows, cols, T::opencv_type(), data.as_mut_ptr().cast::<c_void>()) }?;
227288
Ok(<BoxedRefMut<Mat>>::from(m))
@@ -230,31 +291,31 @@ impl Mat {
230291
/// Create a new `Mat` that references a single-dimensional slice with custom shape
231292
#[inline]
232293
pub fn new_size_with_data<T: DataType>(size: Size, data: &[T]) -> Result<BoxedRef<Self>> {
233-
match_length(&[size.width, size.height], data.len())?;
294+
match_length(&[size.width, size.height], data.len(), 1)?;
234295
let m = unsafe { Self::new_size_with_data_unsafe_def(size, T::opencv_type(), data.as_ptr().cast::<c_void>().cast_mut()) }?;
235296
Ok(<BoxedRef<Mat>>::from(m))
236297
}
237298

238-
/// Create a new `Mat` that references a single-dimensional slice with custom shape
299+
/// Create a new mutable `Mat` that references a single-dimensional slice with custom shape
239300
#[inline]
240301
pub fn new_size_with_data_mut<T: DataType>(size: Size, data: &mut [T]) -> Result<BoxedRefMut<Self>> {
241-
match_length(&[size.width, size.height], data.len())?;
302+
match_length(&[size.width, size.height], data.len(), 1)?;
242303
let m = unsafe { Self::new_size_with_data_unsafe_def(size, T::opencv_type(), data.as_mut_ptr().cast::<c_void>()) }?;
243304
Ok(<BoxedRefMut<Mat>>::from(m))
244305
}
245306

246307
/// Create a new `Mat` that references a single-dimensional slice with custom shape
247308
#[inline]
248309
pub fn new_nd_with_data<'data, T: DataType>(sizes: &[i32], data: &'data [T]) -> Result<BoxedRef<'data, Self>> {
249-
match_length(sizes, data.len())?;
310+
match_length(sizes, data.len(), 1)?;
250311
let m = unsafe { Self::new_nd_with_data_unsafe_def(sizes, T::opencv_type(), data.as_ptr().cast::<c_void>().cast_mut()) }?;
251312
Ok(<BoxedRef<Mat>>::from(m))
252313
}
253314

254315
/// Create a new `Mat` that references a single-dimensional slice with custom shape
255316
#[inline]
256317
pub fn new_nd_with_data_mut<'data, T: DataType>(sizes: &[i32], data: &'data mut [T]) -> Result<BoxedRefMut<'data, Self>> {
257-
match_length(sizes, data.len())?;
318+
match_length(sizes, data.len(), 1)?;
258319
let m = unsafe { Self::new_nd_with_data_unsafe_def(sizes, T::opencv_type(), data.as_mut_ptr().cast::<c_void>()) }?;
259320
Ok(<BoxedRefMut<Mat>>::from(m))
260321
}

tests/mat.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::mem;
33

44
use matches::assert_matches;
55

6-
use opencv::core::{MatConstIterator, MatIter, Point, Point2d, Rect, Scalar, Size, Vec2b, Vec2s, Vec3d, Vec3f, Vec4w, Vector};
6+
use opencv::core::{
7+
MatConstIterator, MatIter, Point, Point2d, Point2f, Rect, Scalar, Size, Vec2b, Vec2s, Vec3d, Vec3f, Vec3s, Vec4w, Vector,
8+
};
79
use opencv::prelude::*;
810
use opencv::{core, imgproc, Error, Result};
911
const PIXEL: &[u8] = include_bytes!("pixel.png");
@@ -1097,6 +1099,53 @@ fn mat_from_slice() -> Result<()> {
10971099
Ok(())
10981100
}
10991101

1102+
#[test]
1103+
fn mat_from_bytes() -> Result<()> {
1104+
{
1105+
let data = vec![0; 3 * 3 * 3 * 2];
1106+
let mat = Mat::new_rows_cols_with_bytes::<Vec3s>(3, 3, &data)?;
1107+
1108+
assert_eq!(Size::new(3, 3), mat.size()?);
1109+
assert_eq!(54, mat.data_bytes()?.len());
1110+
assert_eq!(9, mat.data_typed::<Vec3s>()?.len());
1111+
}
1112+
1113+
{
1114+
let mut data = vec![0; 2 * 3 * 2 * 4];
1115+
let mut mat = Mat::new_rows_cols_with_bytes_mut::<Point>(3, 2, &mut data)?;
1116+
1117+
assert_eq!(Size::new(2, 3), mat.size()?);
1118+
assert_eq!(48, mat.data_bytes()?.len());
1119+
assert_eq!(6, mat.data_typed::<Point>()?.len());
1120+
1121+
mat.at_2d_mut::<Point>(1, 1)?.y = 15;
1122+
assert!(data[7 * 4..7 * 4 + 4].contains(&15));
1123+
}
1124+
1125+
{
1126+
let data = vec![0; 6 * 2 * 4];
1127+
let mat = Mat::from_bytes::<Point2f>(&data)?;
1128+
1129+
assert_eq!(Size::new(6, 1), mat.size()?);
1130+
assert_eq!(48, mat.data_bytes()?.len());
1131+
assert_eq!(6, mat.data_typed::<Point2f>()?.len());
1132+
}
1133+
1134+
{
1135+
let mut data = vec![0; 4 * 4 * 2];
1136+
let mut mat = Mat::from_bytes_mut::<Vec4w>(&mut data)?;
1137+
1138+
assert_eq!(Size::new(4, 1), mat.size()?);
1139+
assert_eq!(32, mat.data_bytes()?.len());
1140+
assert_eq!(4, mat.data_typed::<Vec4w>()?.len());
1141+
1142+
mat.at_mut::<Vec4w>(2)?[1] = 123;
1143+
assert!(data[18..20].contains(&123));
1144+
}
1145+
1146+
Ok(())
1147+
}
1148+
11001149
#[test]
11011150
fn mat_from_slice_2d() -> Result<()> {
11021151
{

0 commit comments

Comments
 (0)