mod affected;
use std::{marker::PhantomData, ops::Mul};
use image::{Rgba, RgbaImage};
use nalgebra::{Affine2, DMatrix, Point2, RealField};
use num_dual::{DualNum, DualNumFloat};
use num_traits::FromPrimitive;
pub struct Image<F, T: DualNum<F, Inner = F>>(pub DMatrix<T>, PhantomData<F>);
impl<F: DualNumFloat, T: DualNum<F, Inner = F> + RealField> Image<F, T>
where
T::Inner: DualNumFloat + FromPrimitive,
for<'a> &'a T: Mul<&'a T, Output = T>,
{
pub fn new(image: DMatrix<T>) -> Self {
Self(image, PhantomData)
}
pub fn from_points(
point_weights: &[(Point2<T>, T)],
pix_to_point: &Affine2<T>,
background: &DMatrix<F>,
) -> Self {
let mut img = background.map(|f| T::from_inner(f));
for (point, weight) in point_weights.iter() {
let p = pix_to_point.transform_point(point);
let affected = affected::affected(&p.coords);
for affected in affected {
let square = affected.square();
if square.x >= 0
&& (square.x as usize) < background.shape().1
&& square.y >= 0
&& (square.y as usize) < background.shape().0
{
img[(square.y as usize, square.x as usize)] -= affected.val().clone() * weight;
}
}
}
Self(img, PhantomData)
}
pub fn to_image(&self) -> RgbaImage {
let (rows, cols) = self.0.shape();
let mut img = RgbaImage::new(cols as u32, rows as u32);
for (x, y, pixel) in img.enumerate_pixels_mut() {
let val = self.0[(y as usize, x as usize)].re();
let g = (val * F::from(u8::MAX as f64).unwrap())
.round()
.to_i16()
.unwrap()
.clamp(u8::MIN as i16, u8::MAX as i16) as u8;
*pixel = Rgba([g, g, g, u8::MAX]);
}
img
}
}
#[cfg(test)]
mod tests {
use nalgebra::DVector;
use num_dual::{Derivative, DualVec};
use super::*;
#[test]
fn test_image() {
let input = Image::new(DMatrix::from_column_slice(
2,
2,
&[
DualVec::new(
0.0,
Derivative::new(Some(DVector::from_column_slice(&[0.0]))),
),
DualVec::new(
0.1,
Derivative::new(Some(DVector::from_column_slice(&[1.0]))),
),
DualVec::new(
0.9,
Derivative::new(Some(DVector::from_column_slice(&[2.0]))),
),
DualVec::new(
1.0,
Derivative::new(Some(DVector::from_column_slice(&[3.0]))),
),
],
));
let output = input.to_image();
assert_eq!(output.get_pixel(0, 0), &image::Rgba([0, 0, 0, 255]));
assert_eq!(output.get_pixel(1, 0), &image::Rgba([230, 230, 230, 255]));
assert_eq!(output.get_pixel(0, 1), &image::Rgba([26, 26, 26, 255]));
assert_eq!(output.get_pixel(1, 1), &image::Rgba([255, 255, 255, 255]));
}
#[test]
fn from_point() {
let background = DMatrix::from_element(3, 3, 1.0);
let point = [Point2::new(1.75, 1.25)];
let pix_to_point = Affine2::identity();
let weight = 0.5;
let point_weights = point.iter().map(|p| (*p, weight)).collect::<Vec<_>>();
let img = Image::from_points(&point_weights, &pix_to_point, &background);
let output = img.to_image();
assert_eq!(output.get_pixel(0, 0), &image::Rgba([255, 255, 255, 255]));
assert_eq!(output.get_pixel(0, 1), &image::Rgba([255, 255, 255, 255]));
assert_eq!(output.get_pixel(0, 2), &image::Rgba([255, 255, 255, 255]));
let tl = (255.0_f64 - (0.25 * 0.75 * weight * 255.0)).round() as u8;
let bl = (255.0_f64 - (0.75 * 0.75 * weight * 255.0)).round() as u8;
let tr = (255.0_f64 - (0.25 * 0.25 * weight * 255.0)).round() as u8;
let br = (255.0_f64 - (0.75 * 0.25 * weight * 255.0)).round() as u8;
assert_eq!(output.get_pixel(1, 0), &image::Rgba([tl, tl, tl, 255])); assert_eq!(output.get_pixel(1, 1), &image::Rgba([bl, bl, bl, 255])); assert_eq!(output.get_pixel(1, 2), &image::Rgba([255, 255, 255, 255]));
assert_eq!(output.get_pixel(2, 0), &image::Rgba([tr, tr, tr, 255])); assert_eq!(output.get_pixel(2, 1), &image::Rgba([br, br, br, 255])); assert_eq!(output.get_pixel(2, 2), &image::Rgba([255, 255, 255, 255]));
}
#[test]
fn from_points_2_identical() {
let background = DMatrix::from_element(3, 3, 1.0);
let point = [Point2::new(1.75, 1.25); 2];
let pix_to_point = Affine2::identity();
let weight = 0.5;
let point_weights = point.iter().map(|p| (*p, weight)).collect::<Vec<_>>();
let img = Image::from_points(&point_weights, &pix_to_point, &background);
let output = img.to_image();
assert_eq!(output.get_pixel(0, 0), &image::Rgba([255, 255, 255, 255]));
assert_eq!(output.get_pixel(0, 1), &image::Rgba([255, 255, 255, 255]));
assert_eq!(output.get_pixel(0, 2), &image::Rgba([255, 255, 255, 255]));
let tl = (255.0_f64 - 2.0 * (0.25 * 0.75 * weight * 255.0)).round() as u8;
let bl = (255.0_f64 - 2.0 * (0.75 * 0.75 * weight * 255.0)).round() as u8;
let tr = (255.0_f64 - 2.0 * (0.25 * 0.25 * weight * 255.0)).round() as u8;
let br = (255.0_f64 - 2.0 * (0.75 * 0.25 * weight * 255.0)).round() as u8;
assert_eq!(output.get_pixel(1, 0), &image::Rgba([tl, tl, tl, 255])); assert_eq!(output.get_pixel(1, 1), &image::Rgba([bl, bl, bl, 255])); assert_eq!(output.get_pixel(1, 2), &image::Rgba([255, 255, 255, 255]));
assert_eq!(output.get_pixel(2, 0), &image::Rgba([tr, tr, tr, 255])); assert_eq!(output.get_pixel(2, 1), &image::Rgba([br, br, br, 255])); assert_eq!(output.get_pixel(2, 2), &image::Rgba([255, 255, 255, 255]));
}
}