Skip to content

Commit

Permalink
image: implement image getter and setter for dev purpose (#230)
Browse files Browse the repository at this point in the history
* image: implement image getter and setter for dev purpose

* image: implement image getter and setter for dev purpose

* Update image.rs
  • Loading branch information
edgarriba authored Jan 26, 2025
1 parent 2a3d1af commit 03a0b2c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 23 deletions.
107 changes: 88 additions & 19 deletions crates/kornia-image/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,8 @@ impl<T, const C: usize> Image<T, C> {

/// 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 iterators over the data slice.
///
/// # Arguments
///
Expand All @@ -429,11 +430,7 @@ impl<T, const C: usize> Image<T, C> {
/// # 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<T, ImageError>
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,
Expand All @@ -452,7 +449,42 @@ impl<T, const C: usize> Image<T, C> {
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(())
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -510,7 +542,7 @@ mod tests {
}

#[test]
fn image_smoke() -> Result<(), ImageError> {
fn test_image_smoke() -> Result<(), ImageError> {
let image = Image::<u8, 3>::new(
ImageSize {
width: 10,
Expand All @@ -526,7 +558,7 @@ mod tests {
}

#[test]
fn image_from_vec() -> Result<(), ImageError> {
fn test_image_from_vec() -> Result<(), ImageError> {
let image: Image<f32, 3> = Image::new(
ImageSize {
height: 3,
Expand All @@ -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 {
Expand All @@ -560,7 +592,7 @@ mod tests {
}

#[test]
fn image_rgbd() -> Result<(), ImageError> {
fn test_image_rgbd() -> Result<(), ImageError> {
let image = Image::<f32, 4>::new(
ImageSize {
height: 2,
Expand All @@ -576,7 +608,7 @@ mod tests {
}

#[test]
fn image_channel() -> Result<(), ImageError> {
fn test_image_channel() -> Result<(), ImageError> {
let image = Image::<f32, 3>::new(
ImageSize {
height: 2,
Expand All @@ -592,7 +624,7 @@ mod tests {
}

#[test]
fn image_split_channels() -> Result<(), ImageError> {
fn test_image_split_channels() -> Result<(), ImageError> {
let image = Image::<f32, 3>::new(
ImageSize {
height: 2,
Expand All @@ -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::<u8, 3>::new(
ImageSize {
Expand All @@ -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::<u8, 3>::new(
ImageSize {
Expand All @@ -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::<u8, 2, _>::from_shape_vec([2, 3], data, CpuAllocator)?;

Expand All @@ -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::<u8, 3, CpuAllocator>::from_shape_vec(
[2, 3, 4],
vec![0u8; 2 * 3 * 4],
Expand All @@ -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())? };
Expand All @@ -692,4 +724,41 @@ mod tests {
assert_eq!(image.num_channels(), 1);
Ok(())
}

#[test]
fn test_get_pixel() -> Result<(), ImageError> {
let image = Image::<u8, 3>::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::<u8, 3>::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(())
}
}
4 changes: 2 additions & 2 deletions crates/kornia-image/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, U, const C: usize>(
src: &Image<T, C>,
Expand Down
4 changes: 2 additions & 2 deletions crates/kornia-imgproc/src/threshold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, const C: usize>(
src: &Image<T, C>,
Expand Down

0 comments on commit 03a0b2c

Please sign in to comment.