diff --git a/src/Feature/mod.rs b/src/Feature/mod.rs index 01074f2..78c4b0c 100644 --- a/src/Feature/mod.rs +++ b/src/Feature/mod.rs @@ -1,69 +1,94 @@ -use std::collections::HashMap; +#[derive(Debug, Clone, Serialize, Deserialize)] +enum FeatureResult { + /// A boolean. Just a boolean + Bool(bool), + /// Signed 32-bit integer + I32(i32), + /// 32-bit single precision floating point + /// can be used for aspect ratio or luminance + F32(f32), + /// Vector for nested multidimensional + Vec(Vec), + /// Standard RGBA color + RGBA(f32, f32, f32, f32), + /// Indices intended for the usage in historgrams + Indices(Vec) +} -fn extract_color_distribution(image_data: &[u8]) -> HashMap { - let mut color_distribution: HashMap = HashMap::new(); - let total_pixels = image_data.len() as f32 / 4.0;//für 4 Werte +impl Default for FeatureResult { + fn default() -> Self { + FeatureResult::Bool(false) + } +} - for pixel in image_data.chunks_exact(4) { - let r = pixel[0] as u32; - let g = pixel[1] as u32; - let b = pixel[2] as u32; - let a = pixel[3] as u32; - let rgba = (r << 24) | (g << 16) | (b << 8) | a; +/// For some feature return type we want to implement a custom compare function +/// for example: historgrams are compared with cosine similarity +impl PartialEq for FeatureResult { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Bool(l0), Self::Bool(r0)) => l0 == r0, + (Self::I32(l0), Self::I32(r0)) => l0 == r0, + (Self::F32(l0), Self::F32(r0)) => l0 == r0, + (Self::Vec(l0), Self::Vec(r0)) => l0 == r0, + (Self::RGBA(l0, l1, l2, l3), Self::RGBA(r0, r1, r2, r3)) => l0 == r0 && l1 == r1 && l2 == r2 && l3 == r3, + (Self::Indices(_), Self::Indices(_)) => todo!("implement cosine similarity"), + _ => false, + } + } +} - *color_distribution.entry(rgba).or_insert(0.0) += 1.0; +type FeatureGenerator = Box) -> (String, FeatureResult)>; + +#[derive(Serialize, Deserialize, Default)] +struct Database { + images: HashMap>, + + /// keep feature generator for the case when we add a new image + /// this field is not serialized and needs to be wrapped in an option + #[serde(skip)] + generators: Option> +} + +impl Database { + + pub fn add_feature(&mut self, feature: FeatureGenerator) { + for (path, features) in self.images.iter_mut() { + // compute feature for every image + todo!("run this as a closure parallel with a thread pool"); + let (name, res) = feature(todo!("load image from disk")); + features.insert(name, res); + } + + if let Some(generators) = self.generators.as_mut() { + generators.push(feature); + } else { + self.generators = Some(vec![feature]) + } } - for (_, count) in &mut color_distribution { - *count /= total_pixels; + pub fn add_image(&mut self, path: String) { + let image = todo!("load image from disk"); + let mut features = HashMap::new(); + if let Some(generators) = self.generators { + for generator in generators.iter() { + let (name, res) = generator(image); + features.insert(name, res); + } + } + self.images.insert(path, features); } - - color_distribution } -fn extract_average_brightness(image: &[u8]) -> u8 { - let mut sum: u32 = 0; - let mut count: u32 = 0; - - for i in (0..image.len()).step_by(4) { - let r = image[i] as u32; - let g = image[i + 1] as u32; - let b = image[i + 2] as u32; - - // (0.299 * R) + (0.587 * G) + (0.114 * B) - let brightness = ((0.299 * r as f32) + (0.587 * g as f32) + (0.114 * b as f32)).round() as u32; - - sum += brightness; - count += 1; - } - let average_brightness = (sum / count) as u8; - average_brightness +/// example feature implementation +fn average_luminance(image: Image) -> (String, FeatureResult) { + (String::from("average-brightness"), FeatureResult::F32(0.0)) } +#[test] +fn test() { + let mut data = Database::default(); -fn main() { - -test2(); + data.add_feature(Box::new(average_luminance)); -} - -fn test2(){ - - let image_data: Vec<(u8, u8, u8, u8)> = vec![ - (255, 0, 0, 255), // Red - (0, 255, 0, 255), // Green - (0, 0, 255, 255), // Blue - ]; - //convert image data to useable &u8 slice - let byte_slice: &[u8] = unsafe { - std::slice::from_raw_parts( - image_data.as_ptr() as *const u8, - image_data.len() * 4, - ) - }; - let color_distribution = extract_color_distribution(&byte_slice); - let color_distribution_vec: Vec = color_distribution.values().cloned().collect(); - let average_brightness = extract_average_brightness(&byte_slice); - println!("{:?}", average_brightness); - println!("{:?}", color_distribution_vec); + let _as_json = serde_json::to_string(&data); }