Implement unchecked variants of `Mat::at*` methods
With the exception of 3d lookups, since we don't have a good way
to check the size of >2d matrices.
MODIFY
src/hub/manual/core/mat.rs
src/hub/manual/core/mat.rs
@@ -83,43 +83,133 @@ impl Mat {
}
#[inline(always)]
- pub(crate) fn _at<T: DataType>(&self, i0: i32) -> Result<&T> {
- self.match_format::<T>().and_then(|_| self.ptr(i0).map(|x| unsafe { &*(x as *const _ as *const T) }))
+ pub(crate) fn _at<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_unchecked(row) }
+ }
+
+ /// Like `Mat::at()` but performs no bounds or type checks
+ ///
+ /// Note that this (and `Mat::at()`) differs from the behavior of OpenCV's
+ /// `_Tp& cv::Mat::at(int i0 = 0)`, in that it only looks up by row, and
+ /// does not look up by column in the case of single-row Mats, as OpenCV's
+ /// `Mat::at()` does.
+ /// (This upstream behavior is documented at https://docs.opencv.org/3.4/d3/d63/classcv_1_1Mat.html#aa5d20fc86d41d59e4d71ae93daee9726)
+ pub unsafe fn at_unchecked<T: DataType>(&self, row: i32) -> Result<&T> {
+ self.ptr(row).map(|x| unsafe { &*(x as *const _ as *const T) })
}
#[inline(always)]
pub(crate) fn _at_mut<T: DataType>(&mut self, i0: i32) -> Result<&mut T> {
- self.match_format::<T>().and_then(|_| self.ptr_mut(i0).map(|x| unsafe { &mut *(x as *mut _ as *mut T) }))
+ self.match_format::<T>()?;
+ let size = self.size()?;
+ if i0 >= size.height {
+ return Err(Error::new(
+ core::StsOutOfRange,
+ format!("Given coordinate is out of bounds. \
+ Mat has size {:?} but was given i0: {}", size, i0)));
+ }
+ unsafe { self.at_mut_unchecked(i0) }
+ }
+
+ /// Like `Mat::at_mut()` but performs no bounds or type checks
+ pub unsafe fn at_mut_unchecked<T: DataType>(&mut self, i0: i32) -> Result<&mut T> {
+ self.ptr_mut(i0).map(|x| unsafe { &mut *(x as *mut _ as *mut T) })
}
#[inline(always)]
pub(crate) fn _at_2d<T: DataType>(&self, row: i32, col: i32) -> Result<&T> {
- self.match_format::<T>().and_then(|_| self.ptr_2d(row, col).map(|x| unsafe { &*(x as *const _ as *const 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) }
+ }
+
+ /// Like `Mat::at_2d()` but performs no bounds or type checks
+ pub unsafe fn at_2d_unchecked<T: DataType>(&self, row: i32, col: i32) -> Result<&T> {
+ self.ptr_2d(row, col).map(|x| unsafe { &*(x as *const _ as *const T) })
}
#[inline(always)]
pub(crate) fn _at_2d_mut<T: DataType>(&mut self, row: i32, col: i32) -> Result<&mut T> {
- self.match_format::<T>().and_then(|_| self.ptr_2d_mut(row, col).map(|x| unsafe { &mut *(x as *mut _ as *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)));
+ }
+ unsafe { self.at_2d_mut_unchecked(row, col) }
+ }
+
+ /// Like `Mat::at_2d_mut()` but performs no bounds or type checks
+ pub unsafe fn at_2d_mut_unchecked<T: DataType>(&mut self, row: i32, col: i32) -> Result<&mut T> {
+ self.ptr_2d_mut(row, col).map(|x| unsafe { &mut *(x as *mut _ as *mut T) })
}
+ /// 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(|_| self.ptr_3d(i0, i1, i2).map(|x| unsafe { &*(x as *const _ as *const T) }))
}
+ /// 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(|_| self.ptr_3d_mut(i0, i1, i2).map(|x| unsafe { &mut *(x as *mut _ as *mut T) }))
}
- pub fn at_row<T: DataType>(&self, i0: i32) -> Result<&[T]> {
+ /// 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) }
+ }
+
+ /// Like `Mat::at_row()` but performs no bounds or type checks
+ pub unsafe fn at_row_unchecked<T: DataType>(&self, row: i32) -> Result<&[T]> {
let width = self.size()?.width as usize;
- self._at(i0).map(|x| unsafe { slice::from_raw_parts(x, width) })
+ self.at_unchecked(row).map(|x| slice::from_raw_parts(x, width))
+ }
+
+ /// 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)));
+ }
+ unsafe { self.at_row_mut_unchecked(row) }
}
- pub fn at_row_mut<T: DataType>(&mut self, i0: i32) -> Result<&mut [T]> {
+ /// Like `Mat::at_row_mut()` but performs no bounds or type checks
+ pub unsafe fn at_row_mut_unchecked<T: DataType>(&mut self, row: i32) -> Result<&mut [T]> {
let width = self.size()?.width as usize;
- self._at_mut(i0).map(|x| unsafe { slice::from_raw_parts_mut(x, width) })
+ self.at_mut_unchecked(row).map(|x| slice::from_raw_parts_mut(x, width))
}
pub fn data_typed<T: DataType>(&self) -> Result<&[T]> {

MODIFY
tests/mat.rs
tests/mat.rs
@@ -26,6 +26,23 @@ fn mat_for_rows_and_cols() {
assert_eq!(120000, mat.total().unwrap());
}
+#[test]
+fn mat_at_1d_refers_to_rows() {
+ let mut mat =
+ Mat::new_rows_cols_with_default(100, 100, core::CV_32FC1, Scalar::all(1.23)).unwrap();
+ *mat.at_mut::<f32>(5).unwrap() = 1.;
+ assert_almost_eq(*mat.at_2d::<f32>(5, 0).unwrap(), 1.);
+}
+
+#[test]
+fn mat_2d_i0_is_rows_i1_is_cols() {
+ // Just a sanity check about which Mat dimension corresponds to which in Size
+ let mut mat =
+ Mat::new_rows_cols_with_default(5, 6, core::CV_32FC1, Scalar::all(1.23)).unwrap();
+ let size = mat.size().unwrap();
+ assert_eq!(size.width, 6);
+ assert_eq!(size.height, 5);
+}
#[test]
fn mat_at_2d_CV_32FC1() {
@@ -35,6 +52,7 @@ fn mat_at_2d_CV_32FC1() {
*mat.at_2d_mut::<f32>(0, 0).unwrap() = 1.;
assert_almost_eq(*mat.at_2d::<f32>(0, 0).unwrap(), 1.);
assert_is_err(mat.at::<i32>(0));
+ assert_is_err(mat.at::<f32>(100));
}
#[test]
@@ -52,6 +70,10 @@ fn mat_at_2d_CV_32FC3() {
assert_almost_eq(pix[0], 1.1);
assert_almost_eq(pix[1], 2.2);
assert_almost_eq(pix[2], 3.3);
+
+ assert_is_err(mat.at_2d::<i32>(0, 0));
+ assert_is_err(mat.at_2d::<core::Vec3f>(100, 1));
+ assert_is_err(mat.at_2d::<core::Vec3f>(1, 100));
}
#[test]
@@ -71,6 +93,11 @@ fn mat_at_row_CV_32FC1() {
assert_almost_eq(data[101], 20.);
assert_almost_eq(data[102], 30.);
assert_almost_eq(data[103], 40.);
+
+ assert_is_err(mat.at_row::<i32>(0));
+ assert_is_err(mat.at_row::<i32>(100));
+ assert_is_err(mat.at_row_mut::<i32>(0));
+ assert_is_err(mat.at_row_mut::<i32>(100));
}
#[test]

Implement unchecked variants of `Mat::at*` methods

With the exception of 3d lookups, since we don't have a good way
to check the size of >2d matrices.