Skip to content

Commit 83f5fa8

Browse files
committed
Add missing Mat_ methods, speed up at*() methods slightly
1 parent bd395b1 commit 83f5fa8

File tree

3 files changed

+109
-46
lines changed

3 files changed

+109
-46
lines changed

src/manual/core/mat.rs

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub use mat_::*;
88

99
use crate::boxed_ref::{BoxedRef, BoxedRefMut};
1010
use crate::core::{MatConstIterator, MatExpr, MatSize, Point, Rect, Scalar, Size, UMat};
11+
use crate::manual::core::DataType;
1112
use crate::prelude::*;
1213
use crate::{core, input_output_array, input_output_array_vector, Error, Result};
1314

@@ -42,30 +43,28 @@ fn match_format<T: DataType>(mat_type: i32) -> Result<()> {
4243
}
4344
}
4445

45-
#[inline]
46-
fn match_dims(mat: &(impl MatTraitConst + ?Sized), dims: usize) -> Result<()> {
47-
// safe because `Mat::dims()` returns value >= 2
48-
let mat_dims = mat.dims() as usize;
49-
if mat_dims == dims {
50-
Ok(())
51-
} else {
52-
Err(Error::new(
46+
fn match_indices(mat: &(impl MatTraitConst + ?Sized), idx: &[i32]) -> Result<()> {
47+
let mat_size = mat.mat_size();
48+
let size = &*mat_size;
49+
if size.len() != idx.len() {
50+
return Err(Error::new(
5351
core::StsUnmatchedSizes,
54-
format!("Mat dims is: {mat_dims}, but requested dims is: {dims}"),
55-
))
52+
format!(
53+
"Amount of Mat dimensions: {} doesn't match the amount of requested indices: {}",
54+
size.len(),
55+
idx.len()
56+
),
57+
));
5658
}
57-
}
58-
59-
fn match_indices(mat: &(impl MatTraitConst + ?Sized), idx: &[i32]) -> Result<()> {
60-
let size = mat.mat_size();
61-
match_dims(mat, idx.len())?;
62-
if let Some((out_dim, out_size)) = size.iter().enumerate().find(|&(i, &x)| idx[i] < 0 || idx[i] >= x) {
59+
if let Some((out_idx, (out_idx_val, out_size))) = idx
60+
.iter()
61+
.zip(size)
62+
.enumerate()
63+
.find(|(_, (idx_val, &size))| !(0..size).contains(idx_val))
64+
{
6365
Err(Error::new(
6466
core::StsOutOfRange,
65-
format!(
66-
"Index: {} along dimension: {} out of bounds 0..{}",
67-
idx[out_dim], out_dim, out_size
68-
),
67+
format!("Index: {out_idx_val} along dimension: {out_idx} out of bounds 0..{out_size}"),
6968
))
7069
} else {
7170
Ok(())
@@ -98,21 +97,23 @@ fn match_is_continuous(mat: &(impl MatTraitConst + ?Sized)) -> Result<()> {
9897
}
9998
}
10099

101-
#[inline]
102100
fn match_length(sizes: &[i32], len: usize) -> Result<()> {
103101
if sizes.is_empty() {
104102
return Err(Error::new(core::StsUnmatchedSizes, "Dimensions must not be empty"));
105103
}
106-
let data_len = i32::try_from(len)?;
107-
if sizes.iter().product::<i32>() != data_len {
108-
let msg = if sizes.len() == 2 {
109-
format!(
110-
"The length of the slice: {data_len} must match the passed row: {rows} and column: {cols} counts exactly",
111-
rows = sizes[0],
112-
cols = sizes[1],
113-
)
114-
} else {
115-
format!("The length of the slice: {data_len} must match the passed dimensions: {sizes:?} exactly")
104+
let mut volume: u64 = 1;
105+
for (i, size) in sizes.iter().enumerate() {
106+
let size =
107+
u64::try_from(*size).map_err(|_| Error::new(core::StsOutOfRange, format!("Dimension {i} must not be negative")))?;
108+
volume = volume.saturating_mul(size);
109+
}
110+
let data_len = u64::try_from(len).map_err(|_| Error::new(core::StsOutOfRange, "Length must fit in u64"))?;
111+
if volume != data_len {
112+
let msg = match sizes {
113+
[rows, cols] => {
114+
format!("The length of the slice: {data_len} must match the passed row: {rows} and column: {cols} counts exactly")
115+
}
116+
_ => format!("The length of the slice: {data_len} must match the passed dimensions: {sizes:?} exactly"),
116117
};
117118
return Err(Error::new(core::StsUnmatchedSizes, msg));
118119
}
@@ -344,16 +345,13 @@ pub(crate) mod mat_forward {
344345
#[inline]
345346
pub fn at<T: DataType>(mat: &(impl MatTraitConst + ?Sized), i0: i32) -> Result<&T> {
346347
match_format::<T>(mat.typ())
347-
.and_then(|_| match_dims(mat, 2))
348348
.and_then(|_| match_total(mat, i0))
349349
.and_then(|_| unsafe { mat.at_unchecked(i0) })
350350
}
351351

352352
#[inline]
353353
pub fn at_mut<T: DataType>(mat: &mut (impl MatTrait + ?Sized), i0: i32) -> Result<&mut T> {
354-
match_format::<T>(mat.typ())
355-
.and_then(|_| match_dims(mat, 2))
356-
.and_then(|_| match_total(mat, i0))?;
354+
match_format::<T>(mat.typ()).and_then(|_| match_total(mat, i0))?;
357355
unsafe { mat.at_unchecked_mut(i0) }
358356
}
359357

@@ -520,8 +518,19 @@ pub trait MatTraitConstManual: MatTraitConst {
520518
}
521519

522520
fn to_vec_2d<T: DataType>(&self) -> Result<Vec<Vec<T>>> {
523-
match_format::<T>(self.typ()).and_then(|_| match_dims(self, 2)).and_then(|_| {
524-
let size = self.size()?;
521+
match_format::<T>(self.typ()).and_then(|_| {
522+
let size = match *self.mat_size() {
523+
[rows, cols] => Size::new(cols, rows),
524+
ref mat_size => {
525+
return Err(Error::new(
526+
core::StsUnmatchedSizes,
527+
format!(
528+
"Mat must have 2 dimensions for this operation, but it has: {}",
529+
mat_size.len()
530+
),
531+
))
532+
}
533+
};
525534
// safe because Mat size can't be negative
526535
let width = size.width as usize;
527536
if self.is_continuous() {

src/manual/core/mat/mat_.rs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ use std::marker::PhantomData;
55

66
use crate::boxed_ref::{BoxedRef, BoxedRefMut};
77
use crate::core::{
8-
Mat, MatTrait, MatTraitConst, MatTraitConstManual, MatTraitManual, ToInputArray, ToInputOutputArray, ToOutputArray,
8+
Mat, MatTrait, MatTraitConst, MatTraitConstManual, MatTraitManual, Point, ToInputArray, ToInputOutputArray, ToOutputArray,
99
_InputArray, _InputOutputArray, _OutputArray,
1010
};
1111
use crate::traits::Boxed;
1212
use crate::{Error, Result};
1313

14-
use super::{match_dims, match_format, match_is_continuous, match_total, DataType};
14+
use super::{match_format, match_indices, match_is_continuous, match_total, DataType};
1515

1616
/// [docs.opencv.org](https://docs.opencv.org/master/df/dfc/classcv_1_1Mat__.html)
1717
///
1818
/// This struct is freely convertible into and from `Mat` using `into` and `try_from` methods. You might want
1919
/// to convert `Mat` to `Mat_` before calling typed methods (like `at`, `data_typed`) when more performance is
20-
/// required because this way you will skip the data type checks (still WIP, not all methods are covered).
20+
/// required because this way you will skip the data type checks.
2121
pub struct Mat_<T> {
2222
inner: Mat,
2323
_type: PhantomData<T>,
@@ -65,24 +65,77 @@ impl<T: DataType> Mat_<T> {
6565
self.as_raw_mut_Mat()
6666
}
6767

68+
/// See [Mat::at]
6869
#[inline]
6970
pub fn at(&self, i0: i32) -> Result<&T> {
70-
match_dims(self, 2)
71-
.and_then(|_| match_total(self, i0))
72-
.and_then(|_| unsafe { self.at_unchecked(i0) })
71+
match_total(self, i0).and_then(|_| unsafe { self.at_unchecked(i0) })
7372
}
7473

74+
/// See [Mat::at_2d]
75+
#[inline]
76+
pub fn at_2d(&self, row: i32, col: i32) -> Result<&T> {
77+
match_indices(self, &[row, col]).and_then(|_| unsafe { self.at_2d_unchecked(row, col) })
78+
}
79+
80+
/// See [Mat::at_3d]
81+
#[inline]
82+
pub fn at_3d(&self, i0: i32, i1: i32, i2: i32) -> Result<&T> {
83+
match_indices(self, &[i0, i1, i2]).and_then(|_| unsafe { self.at_3d_unchecked(i0, i1, i2) })
84+
}
85+
86+
/// See [Mat::at_nd]
87+
#[inline]
88+
pub fn at_nd(&self, idx: &[i32]) -> Result<&T> {
89+
match_indices(self, idx).and_then(|_| unsafe { self.at_nd_unchecked(idx) })
90+
}
91+
92+
/// See [Mat::at_pt]
93+
#[inline]
94+
pub fn at_pt(&self, pt: Point) -> Result<&T> {
95+
self.at_2d(pt.y, pt.x)
96+
}
97+
98+
/// See [Mat::at_mut]
7599
#[inline]
76100
pub fn at_mut(&mut self, i0: i32) -> Result<&mut T> {
77-
match_dims(self, 2).and_then(|_| match_total(self, i0))?;
101+
match_total(self, i0)?;
78102
unsafe { self.at_unchecked_mut(i0) }
79103
}
80104

105+
/// See [Mat::at_2d_mut]
106+
#[inline]
107+
pub fn at_2d_mut(&mut self, row: i32, col: i32) -> Result<&mut T> {
108+
match_indices(self, &[row, col])?;
109+
unsafe { self.at_2d_unchecked_mut(row, col) }
110+
}
111+
112+
/// See [Mat::at_3d_mut]
113+
#[inline]
114+
pub fn at_3d_mut(&mut self, i0: i32, i1: i32, i2: i32) -> Result<&mut T> {
115+
match_indices(self, &[i0, i1, i2])?;
116+
unsafe { self.at_3d_unchecked_mut(i0, i1, i2) }
117+
}
118+
119+
/// See [Mat::at_nd_mut]
120+
#[inline]
121+
pub fn at_nd_mut(&mut self, idx: &[i32]) -> Result<&mut T> {
122+
match_indices(self, idx)?;
123+
unsafe { self.at_nd_unchecked_mut(idx) }
124+
}
125+
126+
/// See [Mat::at_pt_mut]
127+
#[inline]
128+
pub fn at_pt_mut(&mut self, pt: Point) -> Result<&mut T> {
129+
self.at_2d_mut(pt.y, pt.x)
130+
}
131+
132+
/// See [Mat::data_typed]
81133
#[inline]
82134
pub fn data_typed(&self) -> Result<&[T]> {
83135
match_is_continuous(self).and_then(|_| unsafe { self.data_typed_unchecked() })
84136
}
85137

138+
/// See [Mat::data_typed_mut]
86139
#[inline]
87140
pub fn data_typed_mut(&mut self) -> Result<&mut [T]> {
88141
match_is_continuous(self)?;

tests/mat.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,11 @@ fn mat_for_rows_and_cols() -> Result<()> {
9797
fn mat_nd() -> Result<()> {
9898
{
9999
let mut mat = Mat::new_nd_with_default(&[3, 3, 3], Vec4w::opencv_type(), 0.into())?;
100+
assert_eq!(&Vec4w::new(0, 0, 0, 0), mat.at::<Vec4w>(1)?);
100101
assert_matches!(
101-
mat.at::<Vec4w>(0),
102+
mat.at::<Vec4w>(27),
102103
Err(Error {
103-
code: core::StsUnmatchedSizes,
104+
code: core::StsOutOfRange,
104105
..
105106
})
106107
);

0 commit comments

Comments
 (0)