use crate::image::{Image, ImageDtype, ImageSize};
use crate::interpolation::{interpolate_pixel, meshgrid, InterpolationMode};
use anyhow::Result;
use fast_image_resize as fr;
use ndarray::stack;
use std::num::NonZeroU32;
pub fn resize_native<T: ImageDtype, const CHANNELS: usize>(
image: &Image<T, CHANNELS>,
new_size: ImageSize,
interpolation: InterpolationMode,
) -> Result<Image<T, CHANNELS>> {
let mut output = Image::from_size_val(new_size, T::default())?;
let x = ndarray::Array::linspace(0., (image.width() - 1) as f32, new_size.width)
.insert_axis(ndarray::Axis(0));
let y = ndarray::Array::linspace(0., (image.height() - 1) as f32, new_size.height)
.insert_axis(ndarray::Axis(0));
let (xx, yy) = meshgrid(&x, &y);
let xy = stack![ndarray::Axis(2), xx, yy];
ndarray::Zip::from(xy.rows())
.and(output.data.rows_mut())
.par_for_each(|uv, mut out| {
assert_eq!(uv.len(), 2);
let (u, v) = (uv[0], uv[1]);
let pixels = (0..image.num_channels())
.map(|k| interpolate_pixel(&image.data, u, v, k, interpolation));
for (k, pixel) in pixels.enumerate() {
out[k] = pixel;
}
});
Ok(output)
}
pub fn resize_fast(
image: &Image<u8, 3>,
new_size: ImageSize,
interpolation: InterpolationMode,
) -> Result<Image<u8, 3>> {
let src_width = NonZeroU32::new(image.width() as u32).ok_or(anyhow::anyhow!(
"The width of the input image must be greater than zero."
))?;
let src_height = NonZeroU32::new(image.height() as u32).ok_or(anyhow::anyhow!(
"The height of the input image must be greater than zero."
))?;
let image_data = image.data.as_slice().ok_or(anyhow::anyhow!(
"The image data must be contiguous and not empty."
))?;
let src_image = fr::Image::from_vec_u8(
src_width,
src_height,
image_data.to_vec(),
fr::PixelType::U8x3,
)?;
let dst_width = NonZeroU32::new(new_size.width as u32).ok_or(anyhow::anyhow!(
"The width of the output image must be greater than zero."
))?;
let dst_height = NonZeroU32::new(new_size.height as u32).ok_or(anyhow::anyhow!(
"The height of the output image must be greater than zero."
))?;
let mut dst_image = fr::Image::new(dst_width, dst_height, src_image.pixel_type());
let mut dst_view = dst_image.view_mut();
let mut resizer = {
match interpolation {
InterpolationMode::Bilinear => {
fr::Resizer::new(fr::ResizeAlg::Convolution(fr::FilterType::Bilinear))
}
InterpolationMode::Nearest => fr::Resizer::new(fr::ResizeAlg::Nearest),
}
};
resizer.resize(&src_image.view(), &mut dst_view)?;
Image::new(new_size, dst_image.buffer().to_vec())
}
#[cfg(test)]
mod tests {
use anyhow::Result;
#[test]
fn resize_smoke_ch3() -> Result<()> {
use crate::image::{Image, ImageSize};
let image = Image::<_, 3>::new(
ImageSize {
width: 4,
height: 5,
},
vec![0f32; 4 * 5 * 3],
)?;
let image_resized = super::resize_native(
&image,
ImageSize {
width: 2,
height: 3,
},
super::InterpolationMode::Bilinear,
)?;
assert_eq!(image_resized.num_channels(), 3);
assert_eq!(image_resized.size().width, 2);
assert_eq!(image_resized.size().height, 3);
Ok(())
}
#[test]
fn resize_smoke_ch1() -> Result<()> {
use crate::image::{Image, ImageSize};
let image = Image::<_, 1>::new(
ImageSize {
width: 4,
height: 5,
},
vec![0; 4 * 5],
)?;
let image_resized = super::resize_native(
&image,
ImageSize {
width: 2,
height: 3,
},
super::InterpolationMode::Nearest,
)?;
assert_eq!(image_resized.num_channels(), 1);
assert_eq!(image_resized.size().width, 2);
assert_eq!(image_resized.size().height, 3);
Ok(())
}
#[test]
fn meshgrid() {
let x = ndarray::Array::linspace(0., 4., 5).insert_axis(ndarray::Axis(0));
let y = ndarray::Array::linspace(0., 3., 4).insert_axis(ndarray::Axis(0));
let (xx, yy) = super::meshgrid(&x, &y);
assert_eq!(xx.shape(), &[4, 5]);
assert_eq!(yy.shape(), &[4, 5]);
assert_eq!(xx[[0, 0]], 0.);
assert_eq!(xx[[0, 4]], 4.);
assert_eq!(yy[[0, 0]], 0.);
assert_eq!(yy[[3, 0]], 3.);
}
#[test]
fn resize_fast() -> Result<()> {
use crate::image::{Image, ImageSize};
let image = Image::<_, 3>::new(
ImageSize {
width: 4,
height: 5,
},
vec![0u8; 4 * 5 * 3],
)?;
let image_resized = super::resize_fast(
&image,
ImageSize {
width: 2,
height: 3,
},
super::InterpolationMode::Nearest,
)?;
assert_eq!(image_resized.num_channels(), 3);
assert_eq!(image_resized.size().width, 2);
assert_eq!(image_resized.size().height, 3);
Ok(())
}
}