From 71b70444e0ebb7a07f85562a754fd4047048d90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Sat, 17 Jun 2023 11:00:55 +0200 Subject: [PATCH] changed the search function deleted test feature --- src/search_index/mod.rs | 131 ++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 46 deletions(-) diff --git a/src/search_index/mod.rs b/src/search_index/mod.rs index 5b5e0a7..931fb47 100644 --- a/src/search_index/mod.rs +++ b/src/search_index/mod.rs @@ -1,15 +1,13 @@ +use crate::image::Image; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::default::Default; use std::path::{Path, PathBuf}; -use serde::{Deserialize, Serialize}; -use crate::image::Image; - trait WeightedCmp { fn weighted(&self, other: &Self) -> f32; } - /// Every feature returns a known and sized type #[derive(Debug, Clone, Serialize, Deserialize)] enum FeatureResult { @@ -49,10 +47,12 @@ impl PartialEq for FeatureResult { (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::RGBA(l0, l1, l2, l3), Self::RGBA(r0, r1, r2, r3)) => { + l0 == r0 && l1 == r1 && l2 == r2 && l3 == r3 + } (Self::Indices(l), Self::Indices(r)) => l == r, (Self::Char(l0), Self::Char(r0)) => l0 == r0, - (Self::String(l0), Self::String(r0)) => l0 == r0, + (Self::String(l), Self::String(r)) => l == r, (Self::Percent(l0), Self::Percent(r0)) => l0 == r0, _ => false, } @@ -62,29 +62,78 @@ impl PartialEq for FeatureResult { impl WeightedCmp for FeatureResult { fn weighted(&self, other: &Self) -> f32 { match (self, other) { - (Self::Bool(l0), Self::Bool(r0)) => if l0 == r0 { 1. } else { 0. }, - (Self::I32(l0), Self::I32(r0)) => if l0 == r0 { 1. } else { 0. }, - (Self::F32(l0), Self::F32(r0)) => if (l0 - r0).abs() > 0.5 { 1. } else { 0. }, - (Self::Vec(r), Self::Vec(l)) => if l == r { 1. } else { 0. }, - (Self::RGBA(l0, l1, l2, l3), Self::RGBA(r0, r1, r2, r3)) => { - let mut a = 0.; - if l0 == r0 { a += 0.25;} - if l1 == r1 { a += 0.25;} - if l2 == r2 { a += 0.25;} - if l3 == r3 { a += 0.25;} - a}, - (Self::Indices(l), Self::Indices(r)) => l.iter().zip(r.iter()).map(|(a, b)| a * b).sum::() as f32 / (l.iter().map(|a| a * a).sum::() as f32 * r.iter().map(|b| b * b).sum::() as f32).sqrt(), //cosines similarity + (Self::Bool(l0), Self::Bool(r0)) => { + if l0 == r0 { + 1. + } else { + 0. + } + } + (Self::I32(l0), Self::I32(r0)) => { + if l0 == r0 { + 1. + } else { + 0. + } + } + (Self::F32(l0), Self::F32(r0)) => { + if (l0 - r0).abs() > 0.5 { + 1. + } else { + 0. + } + } + (Self::Vec(r), Self::Vec(l)) => { + if l == r { + 1. + } else { + 0. + } + } + (Self::RGBA(l0, l1, l2, l3), Self::RGBA(r0, r1, r2, r3)) => { + let mut a = 0.; + if l0 == r0 { + a += 0.25; + } + if l1 == r1 { + a += 0.25; + } + if l2 == r2 { + a += 0.25; + } + if l3 == r3 { + a += 0.25; + } + a + } + (Self::Indices(l), Self::Indices(r)) => { + l.iter().zip(r.iter()).map(|(a, b)| a * b).sum::() as f32 + / (l.iter().map(|a| a * a).sum::() as f32 + * r.iter().map(|b| b * b).sum::() as f32) + .sqrt() + } //cosines similarity - (Self::Char(l0), Self::Char(r0)) => if l0 == r0 { 1. } else { 0. }, - (Self::String(l0), Self::String(r0)) => if l0 == r0 { 1. } else { 0. },//todo!("change it a bit") - (Self::Percent(l0), Self::Percent(r0)) => if l0 == r0 { 1. } else { 0. }, + (Self::Char(l0), Self::Char(r0)) => { + if l0 == r0 { + 1. + } else { + 0. + } + } + (Self::String(l0), Self::String(r0)) => { + if l0 == r0 { + 1. + } else { + 0. + } + } + (Self::Percent(l0), Self::Percent(r0)) => 1. - (l0 - r0).abs(), _ => 0., } } } - type FeatureGenerator = Box) -> (String, FeatureResult)>; #[derive(Serialize, Deserialize, Default)] @@ -94,37 +143,33 @@ struct Database { /// 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> + generators: Option>, } impl Database { - - pub fn search (&self,image: &Path, feature: FeatureGenerator) -> Vec<(PathBuf,f32)>{ - let image: Image = Image::default(); //todo!("Image reader function") + pub fn search(&self, imagepath: &Path, feature: FeatureGenerator) -> Vec<(PathBuf, f32)> { + let image: Image = Image::default(); //todo!("Image reader function") let search_feat = feature(&image); - let mut result: Vec<(PathBuf,f32)> = Vec::new(); + let mut result: Vec<(PathBuf, f32)> = Vec::new(); for image in &self.images { - for feat in image.1{ + for feat in image.1 { if search_feat.0 == *feat.0 { - result.push((image.0.clone(), search_feat.1.weighted(feat.1))); + result.push((image.0.clone(), search_feat.1.weighted(feat.1))); } } } result - } - ///the new function generates a new Database out of a vector of the Paths of the Images and a Vector of features - pub fn new(images: &Vec, features: Option>)-> Self{ - + pub fn new(images: &Vec, features: Option>) -> Self { let mut images_with_feats = HashMap::new(); for path in images { - let image: Image = Image::default(); //todo!("Image reader function") + let image: Image = Image::default(); //todo!("Image reader function") let mut feats = HashMap::new(); - if let Some(gen) = &features{ + if let Some(gen) = &features { for generator in gen { let (name, result) = generator(&image); feats.insert(name, result); @@ -132,36 +177,30 @@ impl Database { images_with_feats.insert(image.path().clone(), feats); } } - Self{ + Self { images: images_with_feats, generators: features, } - - } - /// with add_image you can add images in a existing database. /// databases from a file are read only pub fn add_image(&mut self, path: &Path) { let image: Image = Image::default(); //todo!("Image reader function") let mut features = HashMap::new(); - if let Some(gen) = &self.generators{ + if let Some(gen) = &self.generators { for generator in gen { let (name, result) = generator(&image); features.insert(name, result); } self.images.insert(image.path().clone(), features); - + } else { + panic!("database without generator functions is immutable") } - else { panic!("database without generator functions is immutable") } } } -/// example feature implementation -fn average_luminance(image: &Image) -> (String, FeatureResult) { - (String::from("average-brightness"), FeatureResult::F32(0.0)) -} + #[test] fn test() {