changed the search function

deleted test feature
This commit is contained in:
Felix Müller 2023-06-17 11:00:55 +02:00
parent 99d0b5a670
commit 71b70444e0
1 changed files with 85 additions and 46 deletions

View File

@ -1,15 +1,13 @@
use crate::image::Image;
use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::default::Default; use std::default::Default;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::image::Image;
trait WeightedCmp { trait WeightedCmp {
fn weighted(&self, other: &Self) -> f32; fn weighted(&self, other: &Self) -> f32;
} }
/// Every feature returns a known and sized type /// Every feature returns a known and sized type
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
enum FeatureResult { enum FeatureResult {
@ -49,10 +47,12 @@ impl PartialEq for FeatureResult {
(Self::I32(l0), Self::I32(r0)) => l0 == r0, (Self::I32(l0), Self::I32(r0)) => l0 == r0,
(Self::F32(l0), Self::F32(r0)) => l0 == r0, (Self::F32(l0), Self::F32(r0)) => l0 == r0,
(Self::Vec(l0), Self::Vec(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::Indices(l), Self::Indices(r)) => l == r,
(Self::Char(l0), Self::Char(r0)) => l0 == r0, (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, (Self::Percent(l0), Self::Percent(r0)) => l0 == r0,
_ => false, _ => false,
} }
@ -62,29 +62,78 @@ impl PartialEq for FeatureResult {
impl WeightedCmp for FeatureResult { impl WeightedCmp for FeatureResult {
fn weighted(&self, other: &Self) -> f32 { fn weighted(&self, other: &Self) -> f32 {
match (self, other) { match (self, other) {
(Self::Bool(l0), Self::Bool(r0)) => if l0 == r0 { 1. } else { 0. }, (Self::Bool(l0), Self::Bool(r0)) => {
(Self::I32(l0), Self::I32(r0)) => if l0 == r0 { 1. } else { 0. }, if l0 == r0 {
(Self::F32(l0), Self::F32(r0)) => if (l0 - r0).abs() > 0.5 { 1. } else { 0. }, 1.
(Self::Vec(r), Self::Vec(l)) => if l == r { 1. } else { 0. }, } 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)) => { (Self::RGBA(l0, l1, l2, l3), Self::RGBA(r0, r1, r2, r3)) => {
let mut a = 0.; let mut a = 0.;
if l0 == r0 { a += 0.25;} if l0 == r0 {
if l1 == r1 { a += 0.25;} a += 0.25;
if l2 == r2 { a += 0.25;} }
if l3 == r3 { a += 0.25;} if l1 == r1 {
a}, a += 0.25;
(Self::Indices(l), Self::Indices(r)) => l.iter().zip(r.iter()).map(|(a, b)| a * b).sum::<u64>() as f32 / (l.iter().map(|a| a * a).sum::<u64>() as f32 * r.iter().map(|b| b * b).sum::<u64>() as f32).sqrt(), //cosines similarity }
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::<u64>() as f32
/ (l.iter().map(|a| a * a).sum::<u64>() as f32
* r.iter().map(|b| b * b).sum::<u64>() as f32)
.sqrt()
} //cosines similarity
(Self::Char(l0), Self::Char(r0)) => if l0 == r0 { 1. } else { 0. }, (Self::Char(l0), Self::Char(r0)) => {
(Self::String(l0), Self::String(r0)) => if l0 == r0 { 1. } else { 0. },//todo!("change it a bit") if l0 == r0 {
(Self::Percent(l0), Self::Percent(r0)) => if l0 == r0 { 1. } else { 0. }, 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., _ => 0.,
} }
} }
} }
type FeatureGenerator = Box<dyn Fn(&Image<f32>) -> (String, FeatureResult)>; type FeatureGenerator = Box<dyn Fn(&Image<f32>) -> (String, FeatureResult)>;
#[derive(Serialize, Deserialize, Default)] #[derive(Serialize, Deserialize, Default)]
@ -94,37 +143,33 @@ struct Database {
/// keep feature generator for the case when we add a new image /// 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 /// this field is not serialized and needs to be wrapped in an option
#[serde(skip)] #[serde(skip)]
generators: Option<Vec<FeatureGenerator>> generators: Option<Vec<FeatureGenerator>>,
} }
impl Database { impl Database {
pub fn search(&self, imagepath: &Path, feature: FeatureGenerator) -> Vec<(PathBuf, f32)> {
pub fn search (&self,image: &Path, feature: FeatureGenerator) -> Vec<(PathBuf,f32)>{
let image: Image<f32> = Image::default(); //todo!("Image reader function") let image: Image<f32> = Image::default(); //todo!("Image reader function")
let search_feat = feature(&image); 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 image in &self.images {
for feat in image.1{ for feat in image.1 {
if search_feat.0 == *feat.0 { 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 result
} }
///the new function generates a new Database out of a vector of the Paths of the Images and a Vector of features ///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<PathBuf>, features: Option<Vec<FeatureGenerator>>)-> Self{ pub fn new(images: &Vec<PathBuf>, features: Option<Vec<FeatureGenerator>>) -> Self {
let mut images_with_feats = HashMap::new(); let mut images_with_feats = HashMap::new();
for path in images { for path in images {
let image: Image<f32> = Image::default(); //todo!("Image reader function") let image: Image<f32> = Image::default(); //todo!("Image reader function")
let mut feats = HashMap::new(); let mut feats = HashMap::new();
if let Some(gen) = &features{ if let Some(gen) = &features {
for generator in gen { for generator in gen {
let (name, result) = generator(&image); let (name, result) = generator(&image);
feats.insert(name, result); feats.insert(name, result);
@ -132,36 +177,30 @@ impl Database {
images_with_feats.insert(image.path().clone(), feats); images_with_feats.insert(image.path().clone(), feats);
} }
} }
Self{ Self {
images: images_with_feats, images: images_with_feats,
generators: features, generators: features,
} }
} }
/// with add_image you can add images in a existing database. /// with add_image you can add images in a existing database.
/// databases from a file are read only /// databases from a file are read only
pub fn add_image(&mut self, path: &Path) { pub fn add_image(&mut self, path: &Path) {
let image: Image<f32> = Image::default(); //todo!("Image reader function") let image: Image<f32> = Image::default(); //todo!("Image reader function")
let mut features = HashMap::new(); let mut features = HashMap::new();
if let Some(gen) = &self.generators{ if let Some(gen) = &self.generators {
for generator in gen { for generator in gen {
let (name, result) = generator(&image); let (name, result) = generator(&image);
features.insert(name, result); features.insert(name, result);
} }
self.images.insert(image.path().clone(), features); 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<f32>) -> (String, FeatureResult) {
(String::from("average-brightness"), FeatureResult::F32(0.0))
}
#[test] #[test]
fn test() { fn test() {