From 88eac91581b3c1eda279f433ccef9fa1869eb496 Mon Sep 17 00:00:00 2001 From: edgar Date: Sat, 25 Jan 2025 21:07:55 +0100 Subject: [PATCH 1/3] image: implement image getter and setter for dev purpose --- crates/kornia-image/src/image.rs | 110 ++++++++++++++++++++----- crates/kornia-image/src/ops.rs | 4 +- crates/kornia-imgproc/src/threshold.rs | 4 +- 3 files changed, 95 insertions(+), 23 deletions(-) diff --git a/crates/kornia-image/src/image.rs b/crates/kornia-image/src/image.rs index a4c8b856..cffc092b 100644 --- a/crates/kornia-image/src/image.rs +++ b/crates/kornia-image/src/image.rs @@ -418,7 +418,8 @@ impl Image { /// Get the pixel data of the image. /// - /// NOTE: experimental api + /// NOTE: this is method is for convenience and not optimized for performance. + /// We recommend using the `get` method instead. /// /// # Arguments /// @@ -429,11 +430,7 @@ impl Image { /// # Returns /// /// The pixel value at the given coordinates. - #[deprecated(since = "0.1.6", note = "Please use the `get` method instead.")] - pub fn get_pixel(&self, x: usize, y: usize, ch: usize) -> Result - where - T: Copy, - { + pub fn get_pixel(&self, x: usize, y: usize, ch: usize) -> Result<&T, ImageError> { if x >= self.width() || y >= self.height() { return Err(ImageError::PixelIndexOutOfBounds( x, @@ -452,7 +449,45 @@ impl Image { None => return Err(ImageError::ImageDataNotContiguous), }; - Ok(*val) + Ok(val) + } + + /// Set the pixel value at the given coordinates. + /// + /// NOTE: this is method is for convenience and not optimized for performance. + /// We recommend creating a mutable slice and operating on it directly. + /// + /// # Arguments + /// + /// * `x` - The x-coordinate of the pixel. + /// * `y` - The y-coordinate of the pixel. + /// * `ch` - The channel index of the pixel. + /// * `val` - The value to set the pixel to. + /// + /// # Returns + /// + /// The pixel value at the given coordinates. + pub fn set_pixel(&mut self, x: usize, y: usize, ch: usize, val: T) -> Result<(), ImageError> + where + T: Copy, + { + if x >= self.width() || y >= self.height() { + return Err(ImageError::PixelIndexOutOfBounds( + x, + y, + self.width(), + self.height(), + )); + } + + if ch >= C { + return Err(ImageError::ChannelIndexOutOfBounds(ch, C)); + } + + let idx = y * self.width() * C + x * C + ch; + self.as_slice_mut()[idx] = val; + + Ok(()) } } @@ -500,7 +535,7 @@ mod tests { use kornia_tensor::{CpuAllocator, Tensor}; #[test] - fn image_size() { + fn test_image_size() { let image_size = ImageSize { width: 10, height: 20, @@ -510,7 +545,7 @@ mod tests { } #[test] - fn image_smoke() -> Result<(), ImageError> { + fn test_image_smoke() -> Result<(), ImageError> { let image = Image::::new( ImageSize { width: 10, @@ -526,7 +561,7 @@ mod tests { } #[test] - fn image_from_vec() -> Result<(), ImageError> { + fn test_image_from_vec() -> Result<(), ImageError> { let image: Image = Image::new( ImageSize { height: 3, @@ -542,7 +577,7 @@ mod tests { } #[test] - fn image_cast() -> Result<(), ImageError> { + fn test_image_cast() -> Result<(), ImageError> { let data = vec![0, 1, 2, 3, 4, 5]; let image_u8 = Image::<_, 3>::new( ImageSize { @@ -560,7 +595,7 @@ mod tests { } #[test] - fn image_rgbd() -> Result<(), ImageError> { + fn test_image_rgbd() -> Result<(), ImageError> { let image = Image::::new( ImageSize { height: 2, @@ -576,7 +611,7 @@ mod tests { } #[test] - fn image_channel() -> Result<(), ImageError> { + fn test_image_channel() -> Result<(), ImageError> { let image = Image::::new( ImageSize { height: 2, @@ -592,7 +627,7 @@ mod tests { } #[test] - fn image_split_channels() -> Result<(), ImageError> { + fn test_image_split_channels() -> Result<(), ImageError> { let image = Image::::new( ImageSize { height: 2, @@ -611,7 +646,7 @@ mod tests { } #[test] - fn scale_and_cast() -> Result<(), ImageError> { + fn test_scale_and_cast() -> Result<(), ImageError> { let data = vec![0u8, 0, 255, 0, 0, 255]; let image_u8 = Image::::new( ImageSize { @@ -627,7 +662,7 @@ mod tests { } #[test] - fn cast_and_scale() -> Result<(), ImageError> { + fn test_cast_and_scale() -> Result<(), ImageError> { let data = vec![0u8, 0, 255, 0, 0, 255]; let image_u8 = Image::::new( ImageSize { @@ -643,7 +678,7 @@ mod tests { } #[test] - fn image_from_tensor() -> Result<(), ImageError> { + fn test_image_from_tensor() -> Result<(), ImageError> { let data = vec![0u8, 1, 2, 3, 4, 5]; let tensor = Tensor::::from_shape_vec([2, 3], data, CpuAllocator)?; @@ -661,7 +696,7 @@ mod tests { } #[test] - fn image_from_tensor_3d() -> Result<(), ImageError> { + fn test_image_from_tensor_3d() -> Result<(), ImageError> { let tensor = Tensor::::from_shape_vec( [2, 3, 4], vec![0u8; 2 * 3 * 4], @@ -682,7 +717,7 @@ mod tests { } #[test] - fn image_from_raw_parts() -> Result<(), ImageError> { + fn test_image_from_raw_parts() -> Result<(), ImageError> { let data = vec![0u8, 1, 2, 3, 4, 5]; let image = unsafe { Image::<_, 1>::from_raw_parts([2, 3].into(), data.as_ptr(), data.len())? }; @@ -692,4 +727,41 @@ mod tests { assert_eq!(image.num_channels(), 1); Ok(()) } + + #[test] + fn test_get_pixel() -> Result<(), ImageError> { + let image = Image::::new( + ImageSize { + height: 2, + width: 1, + }, + vec![1, 2, 5, 19, 255, 128], + )?; + assert_eq!(image.get_pixel(0, 0, 0)?, &1); + assert_eq!(image.get_pixel(0, 0, 1)?, &2); + assert_eq!(image.get_pixel(0, 0, 2)?, &5); + assert_eq!(image.get_pixel(0, 1, 0)?, &19); + assert_eq!(image.get_pixel(0, 1, 1)?, &255); + assert_eq!(image.get_pixel(0, 1, 2)?, &128); + Ok(()) + } + + #[test] + fn test_set_pixel() -> Result<(), ImageError> { + let mut image = Image::::new( + ImageSize { + height: 2, + width: 1, + }, + vec![1, 2, 5, 19, 255, 128], + )?; + + image.set_pixel(0, 0, 0, 128)?; + image.set_pixel(0, 1, 1, 25)?; + + assert_eq!(image.get_pixel(0, 0, 0)?, &128); + assert_eq!(image.get_pixel(0, 1, 1)?, &25); + + Ok(()) + } } diff --git a/crates/kornia-image/src/ops.rs b/crates/kornia-image/src/ops.rs index ce196716..f1c8e92a 100644 --- a/crates/kornia-image/src/ops.rs +++ b/crates/kornia-image/src/ops.rs @@ -26,8 +26,8 @@ use crate::{Image, ImageError}; /// /// cast_and_scale(&image, &mut image_f32, 1. / 255.0).unwrap(); /// -/// assert_eq!(image_f32.get_pixel(0, 0, 0).unwrap(), 0.0f32); -/// assert_eq!(image_f32.get_pixel(1, 0, 0).unwrap(), 1.0f32); +/// assert_eq!(image_f32.get_pixel(0, 0, 0).unwrap(), &0.0f32); +/// assert_eq!(image_f32.get_pixel(1, 0, 0).unwrap(), &1.0f32); /// ``` pub fn cast_and_scale( src: &Image, diff --git a/crates/kornia-imgproc/src/threshold.rs b/crates/kornia-imgproc/src/threshold.rs index 3ba287ca..fe4bd907 100644 --- a/crates/kornia-imgproc/src/threshold.rs +++ b/crates/kornia-imgproc/src/threshold.rs @@ -329,8 +329,8 @@ where /// assert_eq!(thresholded.num_channels(), 1); /// assert_eq!(thresholded.size().width, 2); /// -/// assert_eq!(thresholded.get_pixel(0, 0, 0).unwrap(), 255); -/// assert_eq!(thresholded.get_pixel(1, 0, 0).unwrap(), 0); +/// assert_eq!(thresholded.get_pixel(0, 0, 0).unwrap(), &255); +/// assert_eq!(thresholded.get_pixel(1, 0, 0).unwrap(), &0); /// ``` pub fn in_range( src: &Image, From ee7c0c52f9e27c5f7c1acfc389d60c1701aee166 Mon Sep 17 00:00:00 2001 From: edgar Date: Sat, 25 Jan 2025 21:07:55 +0100 Subject: [PATCH 2/3] image: implement image getter and setter for dev purpose --- crates/kornia-image/src/image.rs | 107 ++++++++++++++++++++----- crates/kornia-image/src/ops.rs | 4 +- crates/kornia-imgproc/src/threshold.rs | 4 +- 3 files changed, 92 insertions(+), 23 deletions(-) diff --git a/crates/kornia-image/src/image.rs b/crates/kornia-image/src/image.rs index a4c8b856..a0e8010d 100644 --- a/crates/kornia-image/src/image.rs +++ b/crates/kornia-image/src/image.rs @@ -418,7 +418,8 @@ impl Image { /// Get the pixel data of the image. /// - /// NOTE: experimental api + /// NOTE: this is method is for convenience and not optimized for performance. + /// We recommend using the `get` method instead. /// /// # Arguments /// @@ -429,11 +430,7 @@ impl Image { /// # Returns /// /// The pixel value at the given coordinates. - #[deprecated(since = "0.1.6", note = "Please use the `get` method instead.")] - pub fn get_pixel(&self, x: usize, y: usize, ch: usize) -> Result - where - T: Copy, - { + pub fn get_pixel(&self, x: usize, y: usize, ch: usize) -> Result<&T, ImageError> { if x >= self.width() || y >= self.height() { return Err(ImageError::PixelIndexOutOfBounds( x, @@ -452,7 +449,42 @@ impl Image { None => return Err(ImageError::ImageDataNotContiguous), }; - Ok(*val) + Ok(val) + } + + /// Set the pixel value at the given coordinates. + /// + /// NOTE: this is method is for convenience and not optimized for performance. + /// We recommend creating a mutable slice and operating on it directly. + /// + /// # Arguments + /// + /// * `x` - The x-coordinate of the pixel. + /// * `y` - The y-coordinate of the pixel. + /// * `ch` - The channel index of the pixel. + /// * `val` - The value to set the pixel to. + /// + /// # Returns + /// + /// The pixel value at the given coordinates. + pub fn set_pixel(&mut self, x: usize, y: usize, ch: usize, val: T) -> Result<(), ImageError> { + if x >= self.width() || y >= self.height() { + return Err(ImageError::PixelIndexOutOfBounds( + x, + y, + self.width(), + self.height(), + )); + } + + if ch >= C { + return Err(ImageError::ChannelIndexOutOfBounds(ch, C)); + } + + let idx = y * self.width() * C + x * C + ch; + self.as_slice_mut()[idx] = val; + + Ok(()) } } @@ -500,7 +532,7 @@ mod tests { use kornia_tensor::{CpuAllocator, Tensor}; #[test] - fn image_size() { + fn test_image_size() { let image_size = ImageSize { width: 10, height: 20, @@ -510,7 +542,7 @@ mod tests { } #[test] - fn image_smoke() -> Result<(), ImageError> { + fn test_image_smoke() -> Result<(), ImageError> { let image = Image::::new( ImageSize { width: 10, @@ -526,7 +558,7 @@ mod tests { } #[test] - fn image_from_vec() -> Result<(), ImageError> { + fn test_image_from_vec() -> Result<(), ImageError> { let image: Image = Image::new( ImageSize { height: 3, @@ -542,7 +574,7 @@ mod tests { } #[test] - fn image_cast() -> Result<(), ImageError> { + fn test_image_cast() -> Result<(), ImageError> { let data = vec![0, 1, 2, 3, 4, 5]; let image_u8 = Image::<_, 3>::new( ImageSize { @@ -560,7 +592,7 @@ mod tests { } #[test] - fn image_rgbd() -> Result<(), ImageError> { + fn test_image_rgbd() -> Result<(), ImageError> { let image = Image::::new( ImageSize { height: 2, @@ -576,7 +608,7 @@ mod tests { } #[test] - fn image_channel() -> Result<(), ImageError> { + fn test_image_channel() -> Result<(), ImageError> { let image = Image::::new( ImageSize { height: 2, @@ -592,7 +624,7 @@ mod tests { } #[test] - fn image_split_channels() -> Result<(), ImageError> { + fn test_image_split_channels() -> Result<(), ImageError> { let image = Image::::new( ImageSize { height: 2, @@ -611,7 +643,7 @@ mod tests { } #[test] - fn scale_and_cast() -> Result<(), ImageError> { + fn test_scale_and_cast() -> Result<(), ImageError> { let data = vec![0u8, 0, 255, 0, 0, 255]; let image_u8 = Image::::new( ImageSize { @@ -627,7 +659,7 @@ mod tests { } #[test] - fn cast_and_scale() -> Result<(), ImageError> { + fn test_cast_and_scale() -> Result<(), ImageError> { let data = vec![0u8, 0, 255, 0, 0, 255]; let image_u8 = Image::::new( ImageSize { @@ -643,7 +675,7 @@ mod tests { } #[test] - fn image_from_tensor() -> Result<(), ImageError> { + fn test_image_from_tensor() -> Result<(), ImageError> { let data = vec![0u8, 1, 2, 3, 4, 5]; let tensor = Tensor::::from_shape_vec([2, 3], data, CpuAllocator)?; @@ -661,7 +693,7 @@ mod tests { } #[test] - fn image_from_tensor_3d() -> Result<(), ImageError> { + fn test_image_from_tensor_3d() -> Result<(), ImageError> { let tensor = Tensor::::from_shape_vec( [2, 3, 4], vec![0u8; 2 * 3 * 4], @@ -682,7 +714,7 @@ mod tests { } #[test] - fn image_from_raw_parts() -> Result<(), ImageError> { + fn test_image_from_raw_parts() -> Result<(), ImageError> { let data = vec![0u8, 1, 2, 3, 4, 5]; let image = unsafe { Image::<_, 1>::from_raw_parts([2, 3].into(), data.as_ptr(), data.len())? }; @@ -692,4 +724,41 @@ mod tests { assert_eq!(image.num_channels(), 1); Ok(()) } + + #[test] + fn test_get_pixel() -> Result<(), ImageError> { + let image = Image::::new( + ImageSize { + height: 2, + width: 1, + }, + vec![1, 2, 5, 19, 255, 128], + )?; + assert_eq!(image.get_pixel(0, 0, 0)?, &1); + assert_eq!(image.get_pixel(0, 0, 1)?, &2); + assert_eq!(image.get_pixel(0, 0, 2)?, &5); + assert_eq!(image.get_pixel(0, 1, 0)?, &19); + assert_eq!(image.get_pixel(0, 1, 1)?, &255); + assert_eq!(image.get_pixel(0, 1, 2)?, &128); + Ok(()) + } + + #[test] + fn test_set_pixel() -> Result<(), ImageError> { + let mut image = Image::::new( + ImageSize { + height: 2, + width: 1, + }, + vec![1, 2, 5, 19, 255, 128], + )?; + + image.set_pixel(0, 0, 0, 128)?; + image.set_pixel(0, 1, 1, 25)?; + + assert_eq!(image.get_pixel(0, 0, 0)?, &128); + assert_eq!(image.get_pixel(0, 1, 1)?, &25); + + Ok(()) + } } diff --git a/crates/kornia-image/src/ops.rs b/crates/kornia-image/src/ops.rs index ce196716..f1c8e92a 100644 --- a/crates/kornia-image/src/ops.rs +++ b/crates/kornia-image/src/ops.rs @@ -26,8 +26,8 @@ use crate::{Image, ImageError}; /// /// cast_and_scale(&image, &mut image_f32, 1. / 255.0).unwrap(); /// -/// assert_eq!(image_f32.get_pixel(0, 0, 0).unwrap(), 0.0f32); -/// assert_eq!(image_f32.get_pixel(1, 0, 0).unwrap(), 1.0f32); +/// assert_eq!(image_f32.get_pixel(0, 0, 0).unwrap(), &0.0f32); +/// assert_eq!(image_f32.get_pixel(1, 0, 0).unwrap(), &1.0f32); /// ``` pub fn cast_and_scale( src: &Image, diff --git a/crates/kornia-imgproc/src/threshold.rs b/crates/kornia-imgproc/src/threshold.rs index 3ba287ca..fe4bd907 100644 --- a/crates/kornia-imgproc/src/threshold.rs +++ b/crates/kornia-imgproc/src/threshold.rs @@ -329,8 +329,8 @@ where /// assert_eq!(thresholded.num_channels(), 1); /// assert_eq!(thresholded.size().width, 2); /// -/// assert_eq!(thresholded.get_pixel(0, 0, 0).unwrap(), 255); -/// assert_eq!(thresholded.get_pixel(1, 0, 0).unwrap(), 0); +/// assert_eq!(thresholded.get_pixel(0, 0, 0).unwrap(), &255); +/// assert_eq!(thresholded.get_pixel(1, 0, 0).unwrap(), &0); /// ``` pub fn in_range( src: &Image, From 02ec12960aaa98fe646ae3b53413d1f78ec2f395 Mon Sep 17 00:00:00 2001 From: Edgar Riba Date: Sun, 26 Jan 2025 09:32:25 +0100 Subject: [PATCH 3/3] Update image.rs --- crates/kornia-image/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kornia-image/src/image.rs b/crates/kornia-image/src/image.rs index a0e8010d..10145a2d 100644 --- a/crates/kornia-image/src/image.rs +++ b/crates/kornia-image/src/image.rs @@ -419,7 +419,7 @@ impl Image { /// Get the pixel data of the image. /// /// NOTE: this is method is for convenience and not optimized for performance. - /// We recommend using the `get` method instead. + /// We recommend using iterators over the data slice. /// /// # Arguments ///