diff --git a/crates/kornia-imgproc/Cargo.toml b/crates/kornia-imgproc/Cargo.toml index 4a3e749b..b590d2c0 100644 --- a/crates/kornia-imgproc/Cargo.toml +++ b/crates/kornia-imgproc/Cargo.toml @@ -49,3 +49,7 @@ harness = false [[bench]] name = "bench_flip" harness = false + +[[bench]] +name = "bench_crop" +harness = false diff --git a/crates/kornia-imgproc/benches/bench_crop.rs b/crates/kornia-imgproc/benches/bench_crop.rs new file mode 100644 index 00000000..4df0b783 --- /dev/null +++ b/crates/kornia-imgproc/benches/bench_crop.rs @@ -0,0 +1,70 @@ +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; + +use kornia_image::{Image, ImageSize}; + +fn bench_resize(c: &mut Criterion) { + let mut group = c.benchmark_group("Crop"); + + for (width, height) in [(256, 224), (512, 448), (1024, 896)].iter() { + group.throughput(criterion::Throughput::Elements((*width * *height) as u64)); + + let parameter_string = format!("{}x{}", width, height); + + // input image + let image_size = [*width, *height].into(); + let data = vec![0u8; width * height * 3]; + let image = Image::::new(image_size, data).unwrap(); + let (x, y) = (13, 21); + + // output image + let new_size = ImageSize { + width: width / 2, + height: height / 2, + }; + + let out_u8 = Image::::from_size_val(new_size, 0).unwrap(); + + group.bench_with_input( + BenchmarkId::new("image_rs", ¶meter_string), + &image, + |b, i| { + let mut image = image::DynamicImage::ImageRgb8( + image::RgbImage::from_raw( + i.size().width as u32, + i.size().height as u32, + i.as_slice().to_vec(), + ) + .unwrap(), + ); + b.iter(|| { + let _image_cropped = image.crop( + x as u32, + y as u32, + new_size.width as u32, + new_size.height as u32, + ); + }) + }, + ); + + group.bench_with_input( + BenchmarkId::new("kornia_par", ¶meter_string), + &(&image, &out_u8), + |b, i| { + let (src, mut dst) = (i.0.clone(), i.1.clone()); + b.iter(|| { + kornia_imgproc::crop::crop_image( + black_box(&src), + black_box(&mut dst), + black_box(0), + black_box(0), + ) + }) + }, + ); + } + group.finish(); +} + +criterion_group!(benches, bench_resize); +criterion_main!(benches); diff --git a/crates/kornia-imgproc/src/crop.rs b/crates/kornia-imgproc/src/crop.rs new file mode 100644 index 00000000..24a9f272 --- /dev/null +++ b/crates/kornia-imgproc/src/crop.rs @@ -0,0 +1,98 @@ +use kornia_core::SafeTensorType; +use kornia_image::{Image, ImageError}; +use rayon::{ + iter::{IndexedParallelIterator, ParallelIterator}, + slice::ParallelSliceMut, +}; + +/// Crop an image to a specified region. +/// +/// # Arguments +/// +/// * `src` - The source image to crop. +/// * `dst` - The destination image to store the cropped image. +/// * `x` - The x-coordinate of the top-left corner of the region to crop. +/// * `y` - The y-coordinate of the top-left corner of the region to crop. +/// +/// # Examples +/// +/// ```rust +/// use kornia_image::{Image, ImageSize}; +/// use kornia_imgproc::crop::crop_image; +/// +/// let image = Image::<_, 1>::new(ImageSize { width: 4, height: 4 }, vec![ +/// 0u8, 1, 2, 3, +/// 4u8, 5, 6, 7, +/// 8u8, 9, 10, 11, +/// 12u8, 13, 14, 15 +/// ]).unwrap(); +/// +/// let mut cropped = Image::<_, 1>::from_size_val(ImageSize { width: 2, height: 2 }, 0u8).unwrap(); +/// +/// crop_image(&image, &mut cropped, 1, 1).unwrap(); +/// +/// assert_eq!(cropped.as_slice(), &[5u8, 6, 9, 10]); +/// ``` +pub fn crop_image( + src: &Image, + dst: &mut Image, + x: usize, + y: usize, +) -> Result<(), ImageError> +where + T: SafeTensorType, +{ + let dst_cols = dst.cols(); + + dst.as_slice_mut() + .par_chunks_exact_mut(dst_cols * C) + .enumerate() + .for_each(|(i, dst_row)| { + // get the slice at the top left corner + let offset = (y + i) * src.cols() * C + x * C; + let src_slice = &src.as_slice()[offset..offset + dst_cols * C]; + + // copy the slice to the destination + dst_row.copy_from_slice(src_slice); + }); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use kornia_image::{Image, ImageError, ImageSize}; + + #[test] + fn test_crop() -> Result<(), ImageError> { + let image_size = ImageSize { + width: 2, + height: 3, + }; + + #[rustfmt::skip] + let image = Image::<_, 3>::new( + image_size, + vec![ + 0u8, 1, 2, 3, 4, 5, + 6u8, 7, 8, 9, 10, 11, + 12u8, 13, 14, 15, 16, 17, + ], + )?; + + let data_expected = vec![9u8, 10, 11, 15, 16, 17]; + + let crop_size = ImageSize { + width: 1, + height: 2, + }; + + let mut cropped = Image::<_, 3>::from_size_val(crop_size, 0u8)?; + + super::crop_image(&image, &mut cropped, 1, 1)?; + + assert_eq!(cropped.as_slice(), &data_expected); + + Ok(()) + } +} diff --git a/crates/kornia-imgproc/src/lib.rs b/crates/kornia-imgproc/src/lib.rs index 19802381..58f6bef7 100644 --- a/crates/kornia-imgproc/src/lib.rs +++ b/crates/kornia-imgproc/src/lib.rs @@ -9,6 +9,10 @@ pub mod color; /// image basic operations module. pub mod core; + +/// image cropping module. +pub mod crop; + // NOTE: not ready yet // pub mod distance_transform;