add support for accessing n-dimensional Mat's, unify validity checking logic
MODIFY
gen_rust.py
gen_rust.py
@@ -162,6 +162,8 @@ func_rename = { # todo check if any "new" is required
"cv_Mat_at_const_int_int": "at_2d",
"cv_Mat_at_int_int_int": "at_3d_mut",
"cv_Mat_at_const_int_int_int": "at_3d",
+ "cv_Mat_at_const_int_X": "at_nd_mut",
+ "cv_Mat_at_const_const_int_X": "at_nd",
"cv_Mat_resize_size_t_Scalar": "resize_with_default",
"cv_Mat_rowRange_const_int_int": "rowbounds",
"cv_Mat_type_const": "typ",
@@ -529,16 +531,22 @@ func_manual = {
"cv_Mat_at_const_int": {
"rust_safe": _forward_const_rust_safe,
},
+ "cv_Mat_at_int_int": {
+ "rust_safe": _forward_mut_rust_safe,
+ },
+ "cv_Mat_at_const_int_int": {
+ "rust_safe": _forward_const_rust_safe,
+ },
"cv_Mat_at_int_int_int": {
"rust_safe": _forward_mut_rust_safe,
},
"cv_Mat_at_const_int_int_int": {
"rust_safe": _forward_const_rust_safe,
},
- "cv_Mat_at_int_int": {
+ "cv_Mat_at_const_int_X": {
"rust_safe": _forward_mut_rust_safe,
},
- "cv_Mat_at_const_int_int": {
+ "cv_Mat_at_const_const_int_X": {
"rust_safe": _forward_const_rust_safe,
},
}
@@ -635,6 +643,12 @@ def decl_patch(module, decl):
pts_arg = decl[3][1]
if pts_arg[0] == "InputArray" and pts_arg[1] == "points":
decl[3][1][0] = "const std::vector<Point>&"
+ elif module == "core":
+ # replace pointers with slices for nd Mat access methods
+ if (decl[0] == "cv.Mat.at" or decl[0] == "cv.Mat.ptr") and len(decl[3]) == 1:
+ idx_arg = decl[3][0]
+ if idx_arg[0] == "const int*" and idx_arg[1] == "idx":
+ decl[3][0][0] = "const int[]"
return decl

MODIFY
src/hub/manual/core/mat.rs
src/hub/manual/core/mat.rs
@@ -84,6 +84,29 @@ impl Mat {
}
}
+ fn match_dims(&self, dims: usize) -> Result<()> {
+ let mat_dims = self.dims()? as usize;
+ if mat_dims == dims {
+ Ok(())
+ } else {
+ Err(Error::new(core::StsUnmatchedSizes, format!("Mat dims is: {}, but requested dims is: {}", mat_dims, dims)))
+ }
+ }
+
+ #[inline(always)]
+ fn match_indices(&self, idx: &[i32]) -> Result<()> {
+ let size = self.mat_size()?;
+ self.match_dims(idx.len())?;
+ let mut out_of_bounds = size.iter()
+ .enumerate()
+ .filter(|&(i, &x)| idx[i] < 0 || idx[i] >= x);
+ if let Some((out_dim, out_size)) = out_of_bounds.next() {
+ Err(Error::new(core::StsOutOfRange, format!("Index: {} along dimension: {} out of bounds 0..{}", idx[out_dim], out_dim, out_size)))
+ } else {
+ Ok(())
+ }
+ }
+
#[inline(always)]
fn convert_ptr<T>(r: &u8) -> &T {
unsafe { &*(r as *const _ as *const T) }
@@ -138,15 +161,9 @@ impl Mat {
#[inline(always)]
pub(crate) fn _at_2d<T: DataType>(&self, row: i32, col: i32) -> Result<&T> {
- self.match_format::<T>()?;
- let size = self.size()?;
- if row >= size.height || col >= size.width {
- return Err(Error::new(
- core::StsOutOfRange,
- format!("Given coordinate is out of bounds. \
- Mat has size {:?} but was given row: {}, col: {}", size, row, col)));
- }
- unsafe { self.at_2d_unchecked(row, col) }
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(&[row, col]))
+ .and_then(|_| unsafe { self.at_2d_unchecked(row, col) })
}
/// Like `Mat::at_2d()` but performs no bounds or type checks
@@ -157,14 +174,8 @@ impl Mat {
#[inline(always)]
pub(crate) fn _at_2d_mut<T: DataType>(&mut self, row: i32, col: i32) -> Result<&mut T> {
- self.match_format::<T>()?;
- let size = self.size()?;
- if row >= size.height || col >= size.width {
- return Err(Error::new(
- core::StsOutOfRange,
- format!("Given coordinate is out of bounds. \
- Mat has size {:?} but was given row: {}, col: {}", size, row, col)));
- }
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(&[row, col]))?;
unsafe { self.at_2d_mut_unchecked(row, col) }
}
@@ -174,31 +185,59 @@ impl Mat {
.map(Self::convert_ptr_mut)
}
- /// Note that since the bindings are set up to wrap all Mat sizes in our custom `Size` struct
- /// instead of a `MatSize` smart pointer, we don't have a good way to check bounds on Mats with
- /// more than 2 dimensions, so we can't perform a real bounds check here.
#[inline(always)]
pub(crate) fn _at_3d<T: DataType>(&self, i0: i32, i1: i32, i2: i32) -> Result<&T> {
- self.match_format::<T>().and_then(|_| unsafe { self.ptr_3d(i0, i1, i2) }.map(Self::convert_ptr))
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(&[i0, i1, i2]))
+ .and_then(|_| unsafe { self.at_3d_unchecked(i0, i1, i2) })
+ }
+
+ pub unsafe fn at_3d_unchecked<T: DataType>(&self, i0: i32, i1: i32, i2: i32) -> Result<&T> {
+ self.ptr_3d(i0, i1, i2)
+ .map(Self::convert_ptr)
}
- /// See safety caveats of `Mat::_at_3d()`
#[inline(always)]
pub(crate) fn _at_3d_mut<T: DataType>(&mut self, i0: i32, i1: i32, i2: i32) -> Result<&mut T> {
- self.match_format::<T>().and_then(|_| unsafe { self.ptr_3d_mut(i0, i1, i2) }.map(Self::convert_ptr_mut))
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(&[i0, i1, i2]))?;
+ unsafe { self.at_3d_mut_unchecked(i0, i1, i2) }
+ }
+
+ pub unsafe fn at_3d_mut_unchecked<T: DataType>(&mut self, i0: i32, i1: i32, i2: i32) -> Result<&mut T> {
+ self.ptr_3d_mut(i0, i1, i2)
+ .map(Self::convert_ptr_mut)
+ }
+
+ #[inline(always)]
+ pub(crate) fn _at_nd<T: core::DataType>(&self, idx: &[i32]) -> Result<&T> {
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(idx))
+ .and_then(|_| unsafe { self.at_nd_unchecked(idx) })
+ }
+
+ pub unsafe fn at_nd_unchecked<T: core::DataType>(&self, idx: &[i32]) -> Result<&T> {
+ self.ptr_nd(idx)
+ .map(Self::convert_ptr)
+ }
+
+ #[inline(always)]
+ pub(crate) fn _at_nd_mut<T: core::DataType>(&mut self, idx: &[i32]) -> Result<&mut T> {
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(idx))?;
+ unsafe { self.at_nd_mut_unchecked(idx) }
+ }
+
+ pub unsafe fn at_nd_mut_unchecked<T: core::DataType>(&mut self, idx: &[i32]) -> Result<&mut T> {
+ self.ptr_nd_mut(idx)
+ .map(Self::convert_ptr_mut)
}
/// Return a complete read-only row
pub fn at_row<T: DataType>(&self, row: i32) -> Result<&[T]> {
- self.match_format::<T>()?;
- let size = self.size()?;
- if row >= size.height {
- return Err(Error::new(
- core::StsOutOfRange,
- format!("Given coordinate is out of bounds. \
- Mat has size {:?} but was given row: {}", size, row)));
- }
- unsafe { self.at_row_unchecked(row) }
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(&[row, 0]))
+ .and_then(|_| unsafe { self.at_row_unchecked(row) })
}
/// Like `Mat::at_row()` but performs no bounds or type checks
@@ -209,14 +248,8 @@ impl Mat {
/// Return a complete writeable row
pub fn at_row_mut<T: DataType>(&mut self, row: i32) -> Result<&mut [T]> {
- self.match_format::<T>()?;
- let size = self.size()?;
- if row >= size.height {
- return Err(Error::new(
- core::StsOutOfRange,
- format!("Given coordinate is out of bounds. \
- Mat has size {:?} but was given row: {}", size, row)));
- }
+ self.match_format::<T>()
+ .and_then(|_| self.match_indices(&[row, 0]))?;
unsafe { self.at_row_mut_unchecked(row) }
}
@@ -292,11 +325,7 @@ impl Mat {
}
pub fn to_vec_2d<T: DataType>(&self) -> Result<Vec<Vec<T>>> {
- self.match_format::<T>().and_then(|_| {
- let dims = self.dims()?;
- if dims > 2 {
- return Err(Error::new(core::StsUnmatchedSizes, format!("Cannot convert Mat with dims: {} to 2D Vec", dims)));
- }
+ self.match_format::<T>().and_then(|_| self.match_dims(2)).and_then(|_| {
let size = self.size()?;
let width = size.width as usize;
if self.is_continuous()? {
@@ -388,7 +417,6 @@ impl fmt::Debug for MatStep {
}
}
-
mod private {
pub trait Sealed {}
}

MODIFY
tests/mat.rs
tests/mat.rs
@@ -67,6 +67,37 @@ fn mat_for_rows_and_cols() -> Result<()> {
Ok(())
}
+#[test]
+fn mat_nd() -> Result<()> {
+ {
+ let dims = VectorOfint::from_iter(vec![3, 3, 3]);
+ let mut mat = Mat::new_nd_with_default(&dims, Vec4w::typ(), Scalar::default())?;
+ assert_eq!(0, mat.at_3d::<Vec4w>(1, 1, 1)?[0]);
+ *mat.at_3d_mut::<Vec4w>(1, 1, 1)? = Vec4w::all(10);
+ assert_eq!(10, mat.at_3d::<Vec4w>(1, 1, 1)?[0]);
+ assert_eq!(0, mat.at_3d::<Vec4w>(1, 1, 2)?[2]);
+ assert_eq!(3, mat.dims()?);
+ assert_eq!([3, 3, 3], *mat.mat_size()?);
+ }
+
+ {
+ let dims = VectorOfint::from_iter(vec![2, 3, 4, 5, 6, 7]);
+ let mut mat = Mat::new_nd_with_default(&dims, Vec4w::typ(), Scalar::default())?;
+ assert_eq!(-1, mat.rows()?);
+ assert_eq!(-1, mat.cols()?);
+ assert_matches!(mat.at_nd::<Vec4w>(&[1, 1, 1]), Err(Error { code: core::StsUnmatchedSizes, ..}));
+ assert_matches!(mat.at_nd::<Vec4w>(&[1, 1, 1, 1, 1, 1, 1]), Err(Error { code: core::StsUnmatchedSizes, ..}));
+ assert_matches!(mat.at_nd::<Vec4w>(&[10, 10, 10, 10, 10, 10]), Err(Error { code: core::StsOutOfRange, ..}));
+ assert_matches!(mat.at_nd::<Vec4w>(&[-1, 10, 10, 10, 10, 10]), Err(Error { code: core::StsOutOfRange, ..}));
+ assert_matches!(mat.at_nd::<Vec4w>(&[2, 3, 4, 5, 10, 10]), Err(Error { code: core::StsOutOfRange, ..}));
+ assert_matches!(mat.at_nd::<Vec4w>(&[2, 3, 4, 5, 6, 10]), Err(Error { code: core::StsOutOfRange, ..}));
+ assert_eq!(Vec4w::default(), *mat.at_nd::<Vec4w>(&[1, 2, 3, 4, 5, 6])?);
+ *mat.at_nd_mut::<Vec4w>(&[1, 2, 3, 4, 5, 6])? = Vec4w::from([5, 6, 7, 8]);
+ assert_eq!(Vec4w::from([5, 6, 7, 8]), *mat.at_nd::<Vec4w>(&[1, 2, 3, 4, 5, 6])?);
+ }
+ Ok(())
+}
+
#[test]
fn mat_at_1d_refers_to_rows() -> Result<()> {
let mut mat =
@@ -267,7 +298,6 @@ fn mat_operations() -> Result<()> {
Ok(())
}
-
#[test]
fn mat_from_data() -> Result<()> {
let mut bytes = PIXEL.to_vec();

add support for accessing n-dimensional Mat's, unify validity checking logic