added the search index module
This commit is contained in:
parent
0f86c727cb
commit
99d0b5a670
|
@ -2,6 +2,7 @@ extern crate core;
|
|||
|
||||
pub mod image;
|
||||
pub mod multithreading;
|
||||
pub mod search_index;
|
||||
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
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 {
|
||||
/// 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<FeatureResult>),
|
||||
/// Standard RGBA color
|
||||
RGBA(f32, f32, f32, f32),
|
||||
/// Indices intended for the usage in histograms
|
||||
Indices(Vec<u64>),
|
||||
///A Character :)
|
||||
Char(char),
|
||||
///A String ;)
|
||||
String(String),
|
||||
///a f32 between 0 and 1
|
||||
Percent(f32),
|
||||
}
|
||||
|
||||
impl Default for FeatureResult {
|
||||
fn default() -> Self {
|
||||
FeatureResult::Bool(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// For some feature return type we want to implement a custom compare function
|
||||
/// for example: histograms 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(l), Self::Indices(r)) => l == r,
|
||||
(Self::Char(l0), Self::Char(r0)) => l0 == r0,
|
||||
(Self::String(l0), Self::String(r0)) => l0 == r0,
|
||||
(Self::Percent(l0), Self::Percent(r0)) => l0 == r0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::<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::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. },
|
||||
|
||||
_ => 0.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type FeatureGenerator = Box<dyn Fn(&Image<f32>) -> (String, FeatureResult)>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
struct Database {
|
||||
images: HashMap<PathBuf, HashMap<String, FeatureResult>>,
|
||||
|
||||
/// 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<Vec<FeatureGenerator>>
|
||||
}
|
||||
|
||||
impl Database {
|
||||
|
||||
pub fn search (&self,image: &Path, feature: FeatureGenerator) -> Vec<(PathBuf,f32)>{
|
||||
let image: Image<f32> = Image::default(); //todo!("Image reader function")
|
||||
let search_feat = feature(&image);
|
||||
let mut result: Vec<(PathBuf,f32)> = Vec::new();
|
||||
|
||||
for image in &self.images {
|
||||
for feat in image.1{
|
||||
if search_feat.0 == *feat.0 {
|
||||
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<PathBuf>, features: Option<Vec<FeatureGenerator>>)-> Self{
|
||||
|
||||
let mut images_with_feats = HashMap::new();
|
||||
|
||||
for path in images {
|
||||
let image: Image<f32> = Image::default(); //todo!("Image reader function")
|
||||
let mut feats = HashMap::new();
|
||||
if let Some(gen) = &features{
|
||||
for generator in gen {
|
||||
let (name, result) = generator(&image);
|
||||
feats.insert(name, result);
|
||||
}
|
||||
images_with_feats.insert(image.path().clone(), feats);
|
||||
}
|
||||
}
|
||||
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<f32> = Image::default(); //todo!("Image reader function")
|
||||
let mut features = HashMap::new();
|
||||
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") }
|
||||
}
|
||||
}
|
||||
|
||||
/// example feature implementation
|
||||
fn average_luminance(image: &Image<f32>) -> (String, FeatureResult) {
|
||||
(String::from("average-brightness"), FeatureResult::F32(0.0))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let data = Database::default();
|
||||
|
||||
let _as_json = serde_json::to_string(&data);
|
||||
}
|
Loading…
Reference in New Issue