use crate::utils::containers::Array2D; use anyhow::{Result, anyhow}; use half::f16; use rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSliceMut}; use shared::Float; use shared::Ptr; use shared::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR}; use shared::core::geometry::{Bounds2f, Point2f, Point2i}; use shared::core::image::{Image, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D}; use shared::utils::math::square; use smallvec::{SmallVec, smallvec}; use std::ops::{Deref, DerefMut}; use std::sync::Arc; pub mod io; pub mod metadata; pub mod ops; pub mod pixel; pub use io::ImageIO; pub use metadata::*; #[derive(Clone, Debug, Default)] pub struct ImageChannelValues(pub SmallVec<[Float; 4]>); impl ImageChannelValues { pub fn average(&self) -> Float { if self.0.is_empty() { return 0.0; } let sum: Float = self.0.iter().sum(); sum / (self.0.len() as Float) } pub fn max_value(&self) -> Float { self.0.iter().fold(Float::MIN, |a, &b| a.max(b)) } } impl From<&[Float]> for ImageChannelValues { fn from(slice: &[Float]) -> Self { Self(SmallVec::from_slice(slice)) } } impl From> for ImageChannelValues { fn from(vec: Vec) -> Self { Self(SmallVec::from_vec(vec)) } } impl From<[Float; N]> for ImageChannelValues { fn from(arr: [Float; N]) -> Self { Self(SmallVec::from_slice(&arr)) } } impl Deref for ImageChannelValues { type Target = SmallVec<[Float; 4]>; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for ImageChannelValues { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[derive(Debug, Clone)] pub struct HostImage { pub inner: Image, pub channel_names: Vec, } #[derive(Debug, Clone)] pub struct ImageAndMetadata { pub image: HostImage, pub metadata: ImageMetadata, } impl HostImage { pub fn from_u8( data: &[u8], resolution: Point2i, channel_names: &[impl AsRef], encoding: ColorEncoding, ) -> Self { let n_channels = channel_names.len() as i32; Self { inner: Image::from_u8(data, resolution, n_channels, encoding), channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(), } } pub fn from_f32( data: &[f32], resolution: Point2i, channel_names: &[impl AsRef], ) -> Self { let n_channels = channel_names.len() as i32; Self { inner: Image::from_f32(data, resolution, n_channels), channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(), } } pub fn from_f16( data: &[u16], resolution: Point2i, channel_names: &[impl AsRef], ) -> Self { let n_channels = channel_names.len() as i32; Self { inner: Image::from_f16(data, resolution, n_channels), channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(), } } pub fn new( format: PixelFormat, resolution: Point2i, channel_names: &[impl AsRef], encoding: ColorEncoding, ) -> Self { let n_channels = channel_names.len() as i32; Self { inner: Image::new(format, resolution, n_channels, encoding), channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(), } } pub fn new_constant( resolution: Point2i, channel_names: &[impl AsRef], values: &[f32], ) -> Self { let n_channels = channel_names.len(); assert_eq!(values.len(), n_channels, "values length must match channel count"); let n_pixels = (resolution.x() * resolution.y()) as usize; let mut data = Vec::with_capacity(n_pixels * n_channels); for _ in 0..n_pixels { data.extend_from_slice(values); } Self::from_f32(&data, resolution, channel_names) } pub fn channel_names(&self) -> Vec<&str> { self.channel_names.iter().map(|s| s.as_str()).collect() } pub fn get_channel_desc( &self, requested: &[impl AsRef + std::fmt::Display], ) -> Result { let mut offset = Vec::with_capacity(requested.len()); for req in requested { match self.channel_names.iter().position(|n| n == req.as_ref()) { Some(idx) => offset.push(idx), None => { return Err(anyhow!( "Missing channel '{}'. Available: {:?}", req, self.channel_names )); } } } Ok(ImageChannelDesc { offset }) } pub fn all_channels_desc(&self) -> ImageChannelDesc { ImageChannelDesc { offset: (0..self.inner.n_channels() as usize).collect(), } } pub fn channel_names_from_desc(&self, desc: &ImageChannelDesc) -> Vec<&str> { desc.offset.iter().map(|&i| self.channel_names[i].as_str()).collect() } pub fn get_channels(&self, p: Point2i) -> ImageChannelValues { self.get_channels_with_wrap(p, WrapMode::Clamp.into()) } pub fn get_channels_with_wrap(&self, mut p: Point2i, wrap_mode: WrapMode2D) -> ImageChannelValues { if !self.inner.remap_pixel_coords(&mut p, wrap_mode) { return ImageChannelValues(SmallVec::from_elem(0.0, self.inner.n_channels() as usize)); } let offset = self.inner.pixel_offset(p); let nc = self.inner.n_channels() as usize; let mut values = SmallVec::with_capacity(nc); for i in 0..nc { values.push(unsafe { self.inner.pixels.read(offset + i, &self.inner.encoding) }); } ImageChannelValues(values) } pub fn get_channels_with_desc( &self, p: Point2i, desc: &ImageChannelDesc, wrap_mode: WrapMode2D, ) -> ImageChannelValues { let mut pp = p; if !self.inner.remap_pixel_coords(&mut pp, wrap_mode) { return ImageChannelValues(SmallVec::from_elem(0.0, desc.offset.len())); } let pixel_offset = self.inner.pixel_offset(pp); let mut values = SmallVec::with_capacity(desc.offset.len()); for &c in &desc.offset { values.push(unsafe { self.inner.pixels.read(pixel_offset + c, &self.inner.encoding) }); } ImageChannelValues(values) } pub fn set_channels(&mut self, p: Point2i, values: &ImageChannelValues) { for i in 0..values.len() { self.inner.set_channel(p, i as i32, values[i]); } } pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self { let new_names: Vec = desc.offset.iter().map(|&i| self.channel_names[i].clone()).collect(); let res = self.inner.resolution(); let pixel_count = (res.x() * res.y()) as usize; let src_nc = self.inner.n_channels() as usize; let dst_nc = desc.offset.len(); // Always produce f32 output for simplicity let mut dst = vec![0.0f32; pixel_count * dst_nc]; for i in 0..pixel_count { let src_offset = i * src_nc; for (out_idx, &in_c) in desc.offset.iter().enumerate() { dst[i * dst_nc + out_idx] = unsafe { self.inner.pixels.read(src_offset + in_c, &self.inner.encoding) }; } } Self::from_f32(&dst, res, &new_names) } pub fn get_sampling_distribution(&self, dxd_a: F, domain: Bounds2f) -> Array2D where F: Fn(Point2f) -> Float + Sync + Send, { let width = self.inner.resolution().x(); let height = self.inner.resolution().y(); let mut dist = Array2D::new_dims(width, height); dist.as_mut_slice() .par_chunks_mut(width as usize) .enumerate() .for_each(|(y, row)| { let y = y as i32; for (x, out_val) in row.iter_mut().enumerate() { let x = x as i32; let value = self.get_channels(Point2i::new(x, y)).average(); let u = (x as Float + 0.5) / width as Float; let v = (y as Float + 0.5) / height as Float; let p = domain.lerp(Point2f::new(u, v)); *out_val = value * dxd_a(p); } }); dist } pub fn get_sampling_distribution_uniform(&self) -> Array2D { let domain = Bounds2f::from_points(Point2f::new(0.0, 0.0), Point2f::new(1.0, 1.0)); self.get_sampling_distribution(|_| 1.0, domain) } pub fn has_any_infinite_pixels(&self) -> bool { self.inner.has_any_infinite_pixels() } pub fn has_any_nan_pixels(&self) -> bool { self.inner.has_any_nan_pixels() } pub fn mse( &self, desc: &ImageChannelDesc, ref_img: &HostImage, generate_mse_image: bool, ) -> (ImageChannelValues, Option) { let res = self.inner.resolution(); assert_eq!(res, ref_img.inner.resolution()); let ref_desc = ref_img .get_channel_desc(&self.channel_names_from_desc(desc)) .expect("Channels not found in reference image"); let width = res.x() as usize; let height = res.y() as usize; let n_channels = desc.size(); let mut sum_se: Vec = vec![0.0; n_channels]; let mut mse_pixels = if generate_mse_image { vec![0.0f32; width * height * n_channels] } else { Vec::new() }; for y in 0..res.y() { for x in 0..res.x() { let v = self.get_channels_with_desc( Point2i::new(x, y), desc, WrapMode::Clamp.into(), ); let v_ref = ref_img.get_channels_with_desc( Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into(), ); for c in 0..n_channels { let se = square(v[c] as f64 - v_ref[c] as f64); if se.is_infinite() { continue; } sum_se[c] += se; if generate_mse_image { let idx = (y as usize * width + x as usize) * n_channels + c; mse_pixels[idx] = se as f32; } } } } let pixel_count = (res.x() * res.y()) as f64; let mse_values: SmallVec<[Float; 4]> = sum_se.iter().map(|&s| (s / pixel_count) as Float).collect(); let mse_image = if generate_mse_image { let names = self.channel_names_from_desc(desc); Some(HostImage::from_f32(&mse_pixels, res, &names)) } else { None }; (ImageChannelValues(mse_values), mse_image) } pub fn image(&self) -> &Image { &self.inner } pub fn into_image(self) -> Image { self.inner } }