use anyhow::Result;
use num_traits::Float;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ImageSize {
pub width: usize,
pub height: usize,
}
impl std::fmt::Display for ImageSize {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"ImageSize {{ width: {}, height: {} }}",
self.width, self.height
)
}
}
#[derive(Clone)]
pub struct Image<T, const CHANNELS: usize> {
pub data: ndarray::Array<T, ndarray::Dim<[ndarray::Ix; 3]>>,
}
impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
pub fn new(size: ImageSize, data: Vec<T>) -> Result<Self> {
if data.len() != size.width * size.height * CHANNELS {
return Err(anyhow::anyhow!(
"Data length ({}) does not match the image size ({})",
data.len(),
size.width * size.height * CHANNELS
));
}
let data =
ndarray::Array::<T, _>::from_shape_vec((size.height, size.width, CHANNELS), data)?;
Ok(Image { data })
}
pub fn from_size_val(size: ImageSize, val: T) -> Result<Self>
where
T: Clone + Default,
{
let data = vec![val; size.width * size.height * CHANNELS];
let image = Image::new(size, data)?;
Ok(image)
}
pub fn cast<U>(self) -> Result<Image<U, CHANNELS>>
where
U: Clone + Default + num_traits::NumCast + std::fmt::Debug,
T: Copy + num_traits::NumCast + std::fmt::Debug,
{
let casted_data = self
.data
.map(|&x| U::from(x).expect("Failed to cast image data"));
Ok(Image { data: casted_data })
}
pub fn cast_and_scale<U>(self, scale: U) -> Result<Image<U, CHANNELS>>
where
U: Copy
+ Clone
+ Default
+ num_traits::NumCast
+ std::fmt::Debug
+ std::ops::Mul<Output = U>,
T: Copy + num_traits::NumCast + std::fmt::Debug,
{
let casted_data = self.data.map(|&x| {
let xu = U::from(x).expect("Failed to cast image data");
xu * scale
});
Ok(Image { data: casted_data })
}
pub fn mul(&self, scale: T) -> Self
where
T: Copy + std::ops::Mul<Output = T>,
{
let scaled_data = self.data.map(|&x| x * scale);
Image { data: scaled_data }
}
pub fn div(&self, scale: T) -> Self
where
T: Copy + std::ops::Div<Output = T>,
{
let scaled_data = self.data.map(|&x| x / scale);
Image { data: scaled_data }
}
pub fn sub(&self, other: &Self) -> Self
where
T: Copy + std::ops::Sub<Output = T>,
{
let diff = &self.data - &other.data;
Image { data: diff }
}
pub fn powi(&self, n: i32) -> Self
where
T: Copy + Float,
{
let powered_data = self.data.map(|&x| x.powi(n));
Image { data: powered_data }
}
pub fn mean(&self) -> T
where
T: Copy + Float,
{
self.data.fold(T::zero(), |acc, &x| acc + x) / T::from(self.data.len()).unwrap()
}
pub fn abs(&self) -> Self
where
T: Copy + Float,
{
let abs_data = self.data.map(|&x| x.abs());
Image { data: abs_data }
}
#[deprecated(since = "0.1.2", note = "Use `image.size()` instead")]
pub fn image_size(&self) -> ImageSize {
ImageSize {
width: self.width(),
height: self.height(),
}
}
pub fn size(&self) -> ImageSize {
ImageSize {
width: self.width(),
height: self.height(),
}
}
pub fn width(&self) -> usize {
self.data.shape()[1]
}
pub fn height(&self) -> usize {
self.data.shape()[0]
}
pub fn num_channels(&self) -> usize {
CHANNELS
}
}
#[cfg(test)]
mod tests {
use crate::image::ImageSize;
#[test]
fn image_size() {
use crate::image::ImageSize;
let image_size = ImageSize {
width: 10,
height: 20,
};
assert_eq!(image_size.width, 10);
assert_eq!(image_size.height, 20);
}
#[test]
fn image_smoke() {
use crate::image::{Image, ImageSize};
let image = Image::<u8, 3>::new(
ImageSize {
width: 10,
height: 20,
},
vec![0u8; 10 * 20 * 3],
)
.unwrap();
assert_eq!(image.size().width, 10);
assert_eq!(image.size().height, 20);
assert_eq!(image.num_channels(), 3);
}
#[test]
fn image_from_vec() {
use crate::image::Image;
let image: Image<f32, 3> = Image::new(
ImageSize {
height: 3,
width: 2,
},
vec![0.0; 3 * 2 * 3],
)
.unwrap();
assert_eq!(image.size().width, 2);
assert_eq!(image.size().height, 3);
assert_eq!(image.num_channels(), 3);
}
#[test]
fn image_cast() {
use crate::image::Image;
let data = vec![0., 1., 2., 3., 4., 5.];
let image_f64 = Image::<f64, 3>::new(
ImageSize {
height: 2,
width: 1,
},
data,
)
.unwrap();
assert_eq!(image_f64.data.get((1, 0, 2)).unwrap(), &5.0f64);
let image_u8 = image_f64.cast::<u8>().unwrap();
assert_eq!(image_u8.data.get((1, 0, 2)).unwrap(), &5u8);
let image_i32: Image<i32, 3> = image_u8.cast().unwrap();
assert_eq!(image_i32.data.get((1, 0, 2)).unwrap(), &5i32);
}
#[test]
fn image_rgbd() {
use crate::image::Image;
let image = Image::<f32, 4>::new(
ImageSize {
height: 2,
width: 3,
},
vec![0f32; 2 * 3 * 4],
)
.unwrap();
assert_eq!(image.size().width, 3);
assert_eq!(image.size().height, 2);
assert_eq!(image.num_channels(), 4);
}
}