use std::{
collections::HashMap,
time::{Duration, SystemTime},
};
use serde::{Deserialize, Serialize};
use crate::{
genericimageref::GenericImageRef,
metadata::{name_check, InsertValue},
BayerError, CalcOptExp, Debayer, DemosaicMethod, DynamicImageOwned, GenericLineItem,
ImageProps, OptimumExposure, EXPOSURE_KEY, TIMESTAMP_KEY,
};
#[allow(unused_imports)]
use crate::{ColorSpace, DynamicImageRef};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GenericImageOwned {
pub(crate) metadata: HashMap<String, GenericLineItem>,
pub(crate) image: DynamicImageOwned,
}
impl GenericImageOwned {
pub fn new(tstamp: SystemTime, image: DynamicImageOwned) -> Self {
let mut metadata = HashMap::new();
metadata.insert(
TIMESTAMP_KEY.to_string(),
GenericLineItem {
value: tstamp.into(),
comment: Some("Timestamp of the image".to_owned()),
},
);
Self { metadata, image }
}
pub fn get_timestamp(&self) -> SystemTime {
self.metadata
.get(TIMESTAMP_KEY)
.and_then(|x| x.get_value().clone().try_into().ok())
.unwrap() }
pub fn get_exposure(&self) -> Option<Duration> {
self.metadata
.get(EXPOSURE_KEY)
.and_then(|x| x.get_value().clone().try_into().ok())
}
pub fn insert_key<T: InsertValue>(&mut self, name: &str, value: T) -> Result<(), &'static str> {
if name.to_uppercase() == TIMESTAMP_KEY {
return Err("Cannot re-insert timestamp key");
}
T::insert_key_go(self, name, value)
}
pub fn remove_key(&mut self, name: &str) -> Result<(), &'static str> {
if name.to_uppercase() == TIMESTAMP_KEY {
return Err("Cannot remove timestamp key");
}
name_check(name)?;
self.metadata.remove(name).ok_or("Key not found")?;
Ok(())
}
pub fn replace_key<T: InsertValue>(
&mut self,
name: &str,
value: T,
) -> Result<(), &'static str> {
T::replace_go(self, name, value)
}
pub fn get_image(&self) -> &DynamicImageOwned {
&self.image
}
pub fn get_image_mut(&mut self) -> &mut DynamicImageOwned {
&mut self.image
}
pub fn get_metadata(&self) -> &HashMap<String, GenericLineItem> {
&self.metadata
}
pub fn get_key(&self, name: &str) -> Option<&GenericLineItem> {
name_check(name).ok()?;
self.metadata.get(name)
}
pub fn into_u8(self) -> GenericImageOwned {
let img = self.image.into_u8();
GenericImageOwned {
metadata: self.metadata,
image: img,
}
}
}
impl Debayer for GenericImageOwned {
type Output = GenericImageOwned;
fn debayer(&self, method: DemosaicMethod) -> Result<Self::Output, BayerError> {
let img = self.image.debayer(method)?;
let meta = self.metadata.clone();
Ok(Self::Output {
metadata: meta,
image: img,
})
}
}
impl ImageProps for GenericImageOwned {
type OutputU8 = GenericImageOwned;
fn width(&self) -> usize {
self.image.width()
}
fn height(&self) -> usize {
self.image.height()
}
fn channels(&self) -> u8 {
self.image.channels()
}
fn color_space(&self) -> crate::ColorSpace {
self.image.color_space()
}
fn pixel_type(&self) -> crate::PixelType {
self.image.pixel_type()
}
fn len(&self) -> usize {
self.image.len()
}
fn is_empty(&self) -> bool {
self.image.is_empty()
}
fn cast_u8(&self) -> Self::OutputU8 {
Self::OutputU8 {
metadata: self.metadata.clone(),
image: self.image.clone().into_u8(),
}
}
}
impl GenericImageOwned {
pub fn operate<F>(&self, f: F) -> Result<Self, &'static str>
where
F: FnOnce(&DynamicImageOwned) -> Result<DynamicImageOwned, &'static str>,
{
let img = f(&(self.image))?;
Ok(GenericImageOwned {
metadata: self.metadata.clone(),
image: img,
})
}
}
impl<'a> From<GenericImageRef<'a>> for GenericImageOwned {
fn from(img: GenericImageRef<'a>) -> Self {
Self {
metadata: img.metadata,
image: (&img.image).into(),
}
}
}
impl CalcOptExp for GenericImageOwned {
fn calc_opt_exp(
mut self,
eval: &OptimumExposure,
exposure: Duration,
bin: u8,
) -> Result<(Duration, u16), &'static str> {
match &mut self.image {
DynamicImageOwned::U8(img) => {let len = img.len(); eval.calculate(img.as_mut_slice(), len, exposure, bin)},
DynamicImageOwned::U16(img) => {let len = img.len(); eval.calculate(img.as_mut_slice(), len, exposure, bin)},
DynamicImageOwned::F32(_) => Err("Floating point images are not supported for this operation, since Ord is not implemented for floating point types."),
}
}
}
impl GenericImageOwned {
pub fn as_raw_u8(&self) -> &[u8] {
self.image.as_raw_u8()
}
pub fn as_raw_u8_checked(&self) -> Option<&[u8]> {
self.image.as_raw_u8_checked()
}
pub fn as_slice_u8(&self) -> Option<&[u8]> {
self.image.as_slice_u8()
}
pub fn as_mut_slice_u8(&mut self) -> Option<&mut [u8]> {
self.image.as_mut_slice_u8()
}
pub fn as_slice_u16(&self) -> Option<&[u16]> {
self.image.as_slice_u16()
}
pub fn as_mut_slice_u16(&mut self) -> Option<&mut [u16]> {
self.image.as_mut_slice_u16()
}
pub fn as_slice_f32(&self) -> Option<&[f32]> {
self.image.as_slice_f32()
}
pub fn as_mut_slice_f32(&mut self) -> Option<&mut [f32]> {
self.image.as_mut_slice_f32()
}
}
mod test {
#[test]
fn test_optimum_exposure() {
use crate::CalcOptExp;
let opt_exp = crate::OptimumExposureBuilder::default()
.pixel_exclusion(1)
.build()
.unwrap();
let img = vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let img = crate::ImageOwned::from_owned(img, 5, 2, crate::ColorSpace::Gray)
.expect("Failed to create ImageOwned");
let img = crate::DynamicImageOwned::from(img);
let img = crate::GenericImageOwned::new(std::time::SystemTime::now(), img);
let exp = std::time::Duration::from_secs(10); let bin = 1; let res = img.calc_opt_exp(&opt_exp, exp, bin).unwrap();
assert_eq!(res, (exp, bin as u16));
}
}