Skip to content

Commit

Permalink
implement a crop function (#155)
Browse files Browse the repository at this point in the history
* implement a crop function

* add benchmark
  • Loading branch information
edgarriba authored Sep 30, 2024
1 parent da12c16 commit 3d1acde
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
4 changes: 4 additions & 0 deletions crates/kornia-imgproc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ harness = false
[[bench]]
name = "bench_flip"
harness = false

[[bench]]
name = "bench_crop"
harness = false
70 changes: 70 additions & 0 deletions crates/kornia-imgproc/benches/bench_crop.rs
Original file line number Diff line number Diff line change
@@ -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::<u8, 3>::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::<u8, 3>::from_size_val(new_size, 0).unwrap();

group.bench_with_input(
BenchmarkId::new("image_rs", &parameter_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", &parameter_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);
98 changes: 98 additions & 0 deletions crates/kornia-imgproc/src/crop.rs
Original file line number Diff line number Diff line change
@@ -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<T, const C: usize>(
src: &Image<T, C>,
dst: &mut Image<T, C>,
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(())
}
}
4 changes: 4 additions & 0 deletions crates/kornia-imgproc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 3d1acde

Please sign in to comment.