Skip to content

Commit

Permalink
Add sample/interpolate nearest
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianKnodt committed Jul 18, 2023
1 parent 2a5c5bf commit a84ae8e
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/imageops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ pub use self::affine::{

/// Image sampling
pub use self::sample::{
blur, filter3x3, interpolate_bilinear, resize, sample_bilinear, thumbnail, unsharpen,
blur, filter3x3, interpolate_bilinear, interpolate_nearest, resize, sample_bilinear,
sample_nearest, thumbnail, unsharpen,
};

/// Color operations
Expand Down
89 changes: 87 additions & 2 deletions src/imageops/sample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ where
out
}

/// Linearly bisample from an image using coordinates in [0,1].
/// Linearly sample from an image using coordinates in [0,1].
pub fn sample_bilinear<P: Pixel>(
img: &impl GenericImageView<Pixel = P>,
u: f32,
Expand All @@ -328,6 +328,50 @@ pub fn sample_bilinear<P: Pixel>(
)
}

/// Sample from an image using coordinates in [0,1], taking the nearest coordinate.
pub fn sample_nearest<P: Pixel>(
img: &impl GenericImageView<Pixel = P>,
u: f32,
v: f32,
) -> Option<P> {
if ![u, v].iter().all(|c| (0.0..=1.0).contains(c)) {
return None;
}

let (w, h) = img.dimensions();
if w == 0 || h == 0 {
return None;
}

let ui = w as f32 * u - 0.5;
let vi = h as f32 * v - 0.5;
interpolate_nearest(
img,
ui.max(0.).min((w - 1) as f32),
vi.max(0.).min((h - 1) as f32),
)
}

/// Linearly bisample from an image using coordinates in [0,w-1] and [0,h-1].
pub fn interpolate_nearest<P: Pixel>(
img: &impl GenericImageView<Pixel = P>,
x: f32,
y: f32,
) -> Option<P> {
let (w, h) = img.dimensions();
if w == 0 || h == 0 {
return None;
}
if !(0.0..=((w - 1) as f32)).contains(&x) {
return None;
}
if !(0.0..=((h - 1) as f32)).contains(&y) {
return None;
}

Some(img.get_pixel(x.round() as u32, y.round() as u32))
}

/// Linearly bisample from an image using coordinates in [0,w-1] and [0,h-1].
pub fn interpolate_bilinear<P: Pixel>(
img: &impl GenericImageView<Pixel = P>,
Expand Down Expand Up @@ -961,7 +1005,7 @@ where

#[cfg(test)]
mod tests {
use super::{resize, sample_bilinear, FilterType};
use super::{resize, sample_bilinear, sample_nearest, FilterType};
use crate::{GenericImageView, ImageBuffer, RgbImage};
#[cfg(feature = "benchmarks")]
use test;
Expand Down Expand Up @@ -1006,6 +1050,25 @@ mod tests {
assert!(sample_bilinear(&img, -0.1, -0.1).is_none());
}
#[test]
#[cfg(feature = "png")]
fn test_sample_nearest() {
use std::path::Path;
let img = crate::open(&Path::new("./examples/fractal.png")).unwrap();
assert!(sample_nearest(&img, 0., 0.).is_some());
assert!(sample_nearest(&img, 1., 0.).is_some());
assert!(sample_nearest(&img, 0., 1.).is_some());
assert!(sample_nearest(&img, 1., 1.).is_some());
assert!(sample_nearest(&img, 0.5, 0.5).is_some());

assert!(sample_nearest(&img, 1.2, 0.5).is_none());
assert!(sample_nearest(&img, 0.5, 1.2).is_none());
assert!(sample_nearest(&img, 1.2, 1.2).is_none());

assert!(sample_nearest(&img, -0.1, 0.2).is_none());
assert!(sample_nearest(&img, 0.2, -0.1).is_none());
assert!(sample_nearest(&img, -0.1, -0.1).is_none());
}
#[test]
fn test_sample_bilinear_correctness() {
use crate::Rgba;
let img = ImageBuffer::from_fn(2, 2, |x, y| match (x, y) {
Expand Down Expand Up @@ -1038,6 +1101,28 @@ mod tests {
Some(Rgba([0, 0, 128, 128]))
);
}
#[test]
fn test_sample_nearest_correctness() {
use crate::Rgba;
let img = ImageBuffer::from_fn(2, 2, |x, y| match (x, y) {
(0, 0) => Rgba([255, 0, 0, 0]),
(0, 1) => Rgba([0, 255, 0, 0]),
(1, 0) => Rgba([0, 0, 255, 0]),
(1, 1) => Rgba([0, 0, 0, 255]),
_ => panic!(),
});

assert_eq!(sample_nearest(&img, 0.0, 0.0), Some(Rgba([255, 0, 0, 0])));
assert_eq!(sample_nearest(&img, 0.0, 1.0), Some(Rgba([0, 255, 0, 0])));
assert_eq!(sample_nearest(&img, 1.0, 0.0), Some(Rgba([0, 0, 255, 0])));
assert_eq!(sample_nearest(&img, 1.0, 1.0), Some(Rgba([0, 0, 0, 255])));

assert_eq!(sample_nearest(&img, 0.5, 0.5), Some(Rgba([0, 0, 0, 255])));
assert_eq!(sample_nearest(&img, 0.5, 0.0), Some(Rgba([0, 0, 255, 0])));
assert_eq!(sample_nearest(&img, 0.0, 0.5), Some(Rgba([0, 255, 0, 0])));
assert_eq!(sample_nearest(&img, 0.5, 1.0), Some(Rgba([0, 0, 0, 255])));
assert_eq!(sample_nearest(&img, 1.0, 0.5), Some(Rgba([0, 0, 0, 255])));
}

#[bench]
#[cfg(all(feature = "benchmarks", feature = "tiff"))]
Expand Down

0 comments on commit a84ae8e

Please sign in to comment.