use crate::exif::{get_exif_data, get_exposures, get_gains};
use crate::extensions::NDArrayBuffer;
use crate::io::read_image;
use crate::Error;
use image::DynamicImage;
use ndarray::Array3;
use rayon::prelude::*;
use std::path::Path;
use std::time::Duration;
#[derive(Clone)]
pub struct HDRInput {
buffer: Array3<f32>,
exposure: f32,
gain: f32,
}
impl HDRInput {
pub fn new(path: &Path) -> Result<Self, crate::Error> {
let new_input = Self::try_from(path)?;
Ok(new_input)
}
pub fn with_exposure_and_gain(
path: &Path,
exposure: Duration,
gain: f32,
) -> Result<Self, Error> {
let data = std::fs::read(path)?;
let format = image::ImageFormat::from_path(path).ok();
let image = read_image(&data, format)?;
Self::with_image(&image, exposure, gain)
}
pub fn with_image(image: &DynamicImage, exposure: Duration, gain: f32) -> Result<Self, Error> {
if gain.is_infinite() || gain.is_nan() || gain <= 0. {
return Err(Error::InputError {
parameter_name: "gain".to_string(),
message: "Gain must be a valid positive and non-zero floating point number"
.to_string(),
});
}
if exposure.is_zero() {
return Err(Error::InputError {
parameter_name: "exposure".to_string(),
message: "Exposure must be a positive non-zero duration".to_string(),
});
}
let buffer = image.to_nd_array_buffer();
Ok(Self {
buffer,
exposure: exposure.as_secs_f32(),
gain,
})
}
#[must_use]
pub fn get_exposure(&self) -> f32 {
self.exposure
}
#[must_use]
pub fn get_gain(&self) -> f32 {
self.gain
}
#[must_use]
pub fn get_buffer(&self) -> &Array3<f32> {
&self.buffer
}
#[must_use]
pub fn get_buffer_mut(&mut self) -> &mut Array3<f32> {
&mut self.buffer
}
}
impl TryFrom<&Path> for HDRInput {
type Error = Error;
fn try_from(value: &Path) -> Result<Self, Self::Error> {
let data = std::fs::read(value)?;
let format = image::ImageFormat::from_path(value).ok();
let image = read_image(&data, format)?;
let exif = get_exif_data(&data)?;
let exposure = get_exposures(&exif)?;
let gain = get_gains(&exif)?;
Self::with_image(&image, Duration::from_secs_f32(exposure), gain)
}
}
pub struct HDRInputList(Vec<HDRInput>);
impl HDRInputList {
#[must_use]
pub fn into_vec(self) -> Vec<HDRInput> {
self.0
}
#[must_use]
pub fn as_slice(&self) -> &[HDRInput] {
&self.0
}
#[must_use]
pub fn as_slice_mut(&mut self) -> &mut [HDRInput] {
&mut self.0
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl From<Vec<HDRInput>> for HDRInputList {
fn from(value: Vec<HDRInput>) -> Self {
Self(value)
}
}
impl<P: AsRef<Path> + Sync> TryFrom<&[P]> for HDRInputList {
type Error = Error;
fn try_from(value: &[P]) -> Result<Self, Self::Error> {
Ok(HDRInputList(
value
.par_iter()
.map(|value| -> Result<HDRInput, Self::Error> {
HDRInput::try_from(value.as_ref())
})
.collect::<Result<Vec<HDRInput>, Self::Error>>()?,
))
}
}