Skip to content

Commit 8feb094

Browse files
Error Handling policy violation (#726)
* Error Handling policy violation fix
1 parent 8300107 commit 8feb094

File tree

36 files changed

+231
-216
lines changed

36 files changed

+231
-216
lines changed

crates/kornia-3d/src/registration/ops.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub(crate) fn find_correspondences(
9898
}
9999

100100
let mid_dist = distances.len() / 2;
101-
distances.select_nth_unstable_by(mid_dist, |a, b| a.partial_cmp(b).unwrap());
101+
distances.select_nth_unstable_by(mid_dist, |a, b| a.total_cmp(b));
102102
let median_dist = distances[mid_dist];
103103

104104
let mut dmed = nn_results
@@ -107,7 +107,7 @@ pub(crate) fn find_correspondences(
107107
.collect::<Vec<_>>();
108108

109109
let mid_mad = dmed.len() / 2;
110-
dmed.select_nth_unstable_by(mid_mad, |a, b| a.partial_cmp(b).unwrap());
110+
dmed.select_nth_unstable_by(mid_mad, |a, b| a.total_cmp(b));
111111
let mad = dmed[mid_mad];
112112

113113
let sigma_d = 1.4826 * mad;

crates/kornia-image/src/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub enum ImageError {
5757
#[error("Unsupported channel count {0}")]
5858
UnsupportedChannelCount(usize),
5959

60-
/// Error when an interpolation mode is not implemented.
61-
#[error("Unsupported interpolation mode: {0}")]
62-
UnsupportedInterpolation(String),
60+
/// Error when interpolation mode is unsupported.
61+
#[error("Unsupported interpolation mode: {0:?}")]
62+
UnsupportedInterpolation(crate::image::InterpolationMode),
6363
}

crates/kornia-image/src/image.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ pub enum PixelFormat {
3838
F32,
3939
}
4040

41+
/// Interpolation mode for the image operations
42+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43+
pub enum InterpolationMode {
44+
/// Bilinear interpolation
45+
Bilinear,
46+
/// Nearest neighbor interpolation
47+
Nearest,
48+
/// Lanczos interpolation
49+
Lanczos,
50+
/// Bicubic interpolation
51+
Bicubic,
52+
}
53+
4154
/// Image layout metadata (size, channels, pixel format).
4255
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4356
pub struct ImageLayout {

crates/kornia-image/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub mod ops;
1717
pub mod color_spaces;
1818

1919
pub use crate::error::ImageError;
20-
pub use crate::image::{Image, ImageLayout, ImageSize, PixelFormat};
20+
pub use crate::image::{Image, ImageLayout, ImageSize, InterpolationMode, PixelFormat};
2121

2222
/// Arrow integration for converting images to Arrow format
2323
#[cfg(feature = "arrow")]

crates/kornia-imgproc/benches/bench_resize.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,12 @@ fn resize_ndarray_zip(src: &Image<f32, 3, CpuAllocator>, dst: &mut Image<f32, 3,
7171
k,
7272
InterpolationMode::Nearest,
7373
)
74+
.unwrap_or(0.0)
7475
});
7576

7677
// write the pixel values to the output image
7778
for (k, pixel) in pixels.enumerate() {
78-
out[k] = pixel.unwrap_or(0.0);
79+
out[k] = pixel;
7980
}
8081
});
8182
}

crates/kornia-imgproc/src/distance_transform.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ pub(crate) fn euclidean_distance(x1: Vec<f32>, x2: Vec<f32>) -> f32 {
99
}
1010

1111
/// NOTE: only for testing, extremely slow
12-
pub fn distance_transform_vanilla<A>(image: &Image<f32, 1, A>) -> Image<f32, 1, CpuAllocator>
12+
pub fn distance_transform_vanilla<A>(
13+
image: &Image<f32, 1, A>,
14+
) -> Result<Image<f32, 1, CpuAllocator>, ImageError>
1315
where
1416
A: ImageAllocator,
1517
{
@@ -34,7 +36,7 @@ where
3436
}
3537
}
3638

37-
Image::new(image.size(), output, CpuAllocator).unwrap()
39+
Image::new(image.size(), output, CpuAllocator)
3840
}
3941

4042
/// Executor for computing the Euclidean Distance Transform.
@@ -184,29 +186,29 @@ mod tests {
184186
use kornia_image::{Image, ImageSize};
185187

186188
#[test]
187-
fn test_accuracy_vs_vanilla() {
189+
fn test_accuracy_vs_vanilla() -> Result<(), ImageError> {
188190
let width = 20;
189191
let height = 20;
190192
let mut data = vec![0.0; width * height];
191193
data[45] = 1.0;
192194
data[102] = 1.0;
193195
data[300] = 1.0;
194196

195-
let image =
196-
Image::<f32, 1, _>::new(ImageSize { width, height }, data, CpuAllocator).unwrap();
197-
let expected = distance_transform_vanilla(&image);
197+
let image = Image::<f32, 1, _>::new(ImageSize { width, height }, data, CpuAllocator)?;
198+
let expected = distance_transform_vanilla(&image)?;
198199

199200
let mut executor = DistanceTransformExecutor::new();
200-
let actual = executor.execute(&image).unwrap();
201+
let actual = executor.execute(&image)?;
201202

202203
for i in 0..actual.as_slice().len() {
203204
let diff = (expected.as_slice()[i] - actual.as_slice()[i]).abs();
204205
assert!(diff < 1e-4);
205206
}
207+
Ok(())
206208
}
207209

208210
#[test]
209-
fn distance_transform_smoke() {
211+
fn distance_transform_smoke() -> Result<(), ImageError> {
210212
let image = Image::<f32, 1, _>::new(
211213
ImageSize {
212214
width: 3,
@@ -216,14 +218,15 @@ mod tests {
216218
0.0f32, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0,
217219
],
218220
CpuAllocator,
219-
)
220-
.unwrap();
221+
)?;
221222

222223
let mut executor = DistanceTransformExecutor::new();
223-
let output = executor.execute(&image).unwrap();
224+
let output = executor.execute(&image)?;
224225

225226
assert_eq!(output.size().width, 3);
226227
assert_eq!(output.size().height, 4);
227228
assert_eq!(output.as_slice()[2], 0.0);
229+
230+
Ok(())
228231
}
229232
}

crates/kornia-imgproc/src/draw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ pub fn draw_filled_polygon<const C: usize, A: ImageAllocator>(
183183
}
184184

185185
// sort the intersections by x coordinate
186-
x_intersections.sort_by(|a, b| a.partial_cmp(b).unwrap());
186+
x_intersections.sort_by(|a, b| a.total_cmp(b));
187187

188188
// filling the region between intersections with color
189189
for pair in x_intersections.chunks(2) {

crates/kornia-imgproc/src/features/responses.rs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ impl HarrisResponse {
129129
Self { k, ..self }
130130
}
131131

132+
#[inline]
133+
fn row_bounds_err(cols: usize, rows: usize) -> ImageError {
134+
ImageError::PixelIndexOutOfBounds(0, rows.saturating_sub(1), cols, rows)
135+
}
136+
137+
#[inline]
138+
fn col_bounds_err(cols: usize, rows: usize, row_idx: usize) -> ImageError {
139+
ImageError::PixelIndexOutOfBounds(cols.saturating_sub(1), row_idx + 1, cols, rows)
140+
}
141+
132142
/// Computes the harris response of an image.
133143
///
134144
/// The Harris response is computed by the determinant minus the trace squared.
@@ -158,49 +168,58 @@ impl HarrisResponse {
158168
));
159169
}
160170

171+
if src.cols() < 2 || src.rows() < 2 {
172+
return Err(ImageError::InvalidImageSize(
173+
src.size().width,
174+
src.size().height,
175+
2,
176+
2,
177+
));
178+
}
179+
161180
let src_data = src.as_slice();
162181
let col_slice = src.cols()..src_data.len() - src.cols();
163182
let row_slice = 1..src.cols() - 1;
164183

165184
self.dx2_data
166185
.as_mut_slice()
167186
.get_mut(col_slice.clone())
168-
// SAFETY: we ranges is valid
169-
.unwrap()
187+
.ok_or_else(|| Self::row_bounds_err(src.cols(), src.rows()))?
170188
.par_chunks_exact_mut(src.cols())
171189
.zip(
172190
self.dy2_data
173191
.as_mut_slice()
174192
.get_mut(col_slice.clone())
175-
// SAFETY: we ranges is valid
176-
.unwrap()
193+
.ok_or_else(|| Self::row_bounds_err(src.cols(), src.rows()))?
177194
.par_chunks_exact_mut(src.cols()),
178195
)
179196
.zip(
180197
self.dxy_data
181198
.as_mut_slice()
182199
.get_mut(col_slice.clone())
183-
// SAFETY: we ranges is valid
184-
.unwrap()
200+
.ok_or_else(|| Self::row_bounds_err(src.cols(), src.rows()))?
185201
.par_chunks_exact_mut(src.cols()),
186202
)
187203
.enumerate()
188-
.for_each(|(row_idx, ((dx2_chunk, dy2_chunk), dxy_chunk))| {
204+
.try_for_each(|(row_idx, ((dx2_chunk, dy2_chunk), dxy_chunk))| {
189205
let row_offset = (row_idx + 1) * src.cols();
190206

191207
dx2_chunk
192208
.get_mut(row_slice.clone())
193-
// SAFETY: we ranges is valid
194-
.unwrap()
209+
.ok_or_else(|| Self::col_bounds_err(src.cols(), src.rows(), row_idx))?
195210
.iter_mut()
196211
.zip(
197212
dy2_chunk
198213
.get_mut(row_slice.clone())
199-
// SAFETY: we ranges is valid
200-
.unwrap()
214+
.ok_or_else(|| Self::col_bounds_err(src.cols(), src.rows(), row_idx))?
215+
.iter_mut(),
216+
)
217+
.zip(
218+
dxy_chunk
219+
.get_mut(row_slice.clone())
220+
.ok_or_else(|| Self::col_bounds_err(src.cols(), src.rows(), row_idx))?
201221
.iter_mut(),
202222
)
203-
.zip(dxy_chunk.get_mut(row_slice.clone()).unwrap().iter_mut())
204223
.enumerate()
205224
.for_each(|(col_idx, ((dx2_pixel, dy2_pixel), dxy_pixel))| {
206225
let current_idx = row_offset + col_idx + 1;
@@ -230,21 +249,20 @@ impl HarrisResponse {
230249
*dy2_pixel = dy * dy;
231250
*dxy_pixel = dx * dy;
232251
});
233-
});
252+
Ok::<(), ImageError>(())
253+
})?;
234254

235255
dst.as_slice_mut()
236256
.get_mut(col_slice.clone())
237-
// SAFETY: we ranges is valid
238-
.unwrap()
257+
.ok_or_else(|| Self::row_bounds_err(src.cols(), src.rows()))?
239258
.par_chunks_exact_mut(src.cols())
240259
.enumerate()
241-
.for_each(|(row_idx, dst_chunk)| {
260+
.try_for_each(|(row_idx, dst_chunk)| {
242261
let row_offset = (row_idx + 1) * src.cols();
243262

244263
dst_chunk
245264
.get_mut(row_slice.clone())
246-
// SAFETY: we ranges is valid
247-
.unwrap()
265+
.ok_or_else(|| Self::col_bounds_err(src.cols(), src.rows(), row_idx))?
248266
.iter_mut()
249267
.enumerate()
250268
.for_each(|(col_idx, dst_pixel)| {
@@ -282,7 +300,8 @@ impl HarrisResponse {
282300

283301
*dst_pixel = f32::max(0.0, response);
284302
});
285-
});
303+
Ok::<(), ImageError>(())
304+
})?;
286305

287306
Ok(())
288307
}

crates/kornia-imgproc/src/filter/kernels.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use kornia_image::ImageError;
2-
31
/// Create a box blur kernel.
42
///
53
/// # Arguments
@@ -49,18 +47,28 @@ pub fn gaussian_kernel_1d(kernel_size: usize, sigma: f32) -> Vec<f32> {
4947
/// * `kernel_size` - The size of the kernel (supports 3 and 5).
5048
///
5149
/// # Returns
50+
/// A tuple `(kernel_x, kernel_y)` containing the derivative and smoothing kernels.
51+
///
52+
/// # Errors
5253
///
53-
/// A tuple containing `(derivative_kernel, smoothing_kernel)`.
54-
pub fn sobel_kernel_1d(kernel_size: usize) -> Result<(Vec<f32>, Vec<f32>), ImageError> {
55-
let (kernel_deriv, kernel_smooth) = match kernel_size {
54+
/// Returns `ImageError::InvalidKernelLength` when `kernel_size` is not 3 or 5.
55+
pub fn sobel_kernel_1d(
56+
kernel_size: usize,
57+
) -> Result<(Vec<f32>, Vec<f32>), kornia_image::ImageError> {
58+
let (kernel_x, kernel_y) = match kernel_size {
5659
3 => (vec![-1.0, 0.0, 1.0], vec![1.0, 2.0, 1.0]),
5760
5 => (
5861
vec![-1.0, -2.0, 0.0, 2.0, 1.0],
5962
vec![1.0, 4.0, 6.0, 4.0, 1.0],
6063
),
61-
_ => return Err(ImageError::InvalidKernelLength(1, kernel_size)),
64+
_ => {
65+
return Err(kornia_image::ImageError::InvalidKernelLength(
66+
kernel_size,
67+
kernel_size,
68+
))
69+
}
6270
};
63-
Ok((kernel_deriv, kernel_smooth))
71+
Ok((kernel_x, kernel_y))
6472
}
6573

6674
/// Create a normalized 2d sobel kernel.
@@ -121,6 +129,7 @@ pub fn box_blur_fast_kernels_1d(sigma: f32, kernels: u8) -> Vec<usize> {
121129
#[cfg(test)]
122130
mod tests {
123131
use super::*;
132+
use kornia_image::ImageError;
124133

125134
#[test]
126135
fn test_sobel_kernel_1d() -> Result<(), ImageError> {

crates/kornia-imgproc/src/interpolation/interpolate.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,7 @@ use super::nearest::nearest_neighbor_interpolation;
33
use kornia_image::allocator::ImageAllocator;
44
use kornia_image::{Image, ImageError};
55

6-
/// Interpolation mode for the resize operation
7-
#[derive(Debug, Clone, Copy, PartialEq)]
8-
pub enum InterpolationMode {
9-
/// Bilinear interpolation
10-
Bilinear,
11-
/// Nearest neighbor interpolation
12-
Nearest,
13-
/// Lanczos interpolation
14-
Lanczos,
15-
/// Bicubic interpolation
16-
Bicubic,
17-
}
6+
pub use kornia_image::InterpolationMode;
187

198
/// Validate that the given interpolation mode is supported by `interpolate_pixel`.
209
///
@@ -23,7 +12,7 @@ pub enum InterpolationMode {
2312
pub fn validate_interpolation(interpolation: InterpolationMode) -> Result<(), ImageError> {
2413
match interpolation {
2514
InterpolationMode::Bilinear | InterpolationMode::Nearest => Ok(()),
26-
mode => Err(ImageError::UnsupportedInterpolation(format!("{mode:?}"))),
15+
mode => Err(ImageError::UnsupportedInterpolation(mode)),
2716
}
2817
}
2918

@@ -38,18 +27,35 @@ pub fn validate_interpolation(interpolation: InterpolationMode) -> Result<(), Im
3827
/// * `interpolation` - The interpolation mode to use.
3928
///
4029
/// # Returns
41-
///
42-
/// The interpolated pixel value, or an error if the interpolation mode is not supported.
30+
/// The interpolated pixel value, or an error if the interpolation mode is unsupported.
4331
pub fn interpolate_pixel<const C: usize, A: ImageAllocator>(
4432
image: &Image<f32, C, A>,
4533
u: f32,
4634
v: f32,
4735
c: usize,
4836
interpolation: InterpolationMode,
4937
) -> Result<f32, ImageError> {
38+
validate_interpolation(interpolation)?;
39+
Ok(interpolate_pixel_fast(image, u, v, c, interpolation))
40+
}
41+
42+
/// Fallible-free internal kernel for fast pixel interpolation (must be validated first)
43+
pub(crate) fn interpolate_pixel_fast<const C: usize, A: ImageAllocator>(
44+
image: &Image<f32, C, A>,
45+
u: f32,
46+
v: f32,
47+
c: usize,
48+
interpolation: InterpolationMode,
49+
) -> f32 {
5050
match interpolation {
51-
InterpolationMode::Bilinear => Ok(bilinear_interpolation(image, u, v, c)),
52-
InterpolationMode::Nearest => Ok(nearest_neighbor_interpolation(image, u, v, c)),
53-
mode => Err(ImageError::UnsupportedInterpolation(format!("{mode:?}"))),
51+
InterpolationMode::Bilinear => bilinear_interpolation(image, u, v, c),
52+
InterpolationMode::Nearest => nearest_neighbor_interpolation(image, u, v, c),
53+
InterpolationMode::Lanczos | InterpolationMode::Bicubic => {
54+
debug_assert!(
55+
false,
56+
"unsupported mode should have been caught by validate_interpolation"
57+
);
58+
0.0
59+
}
5460
}
5561
}

0 commit comments

Comments
 (0)