parent
99d0b5a670
commit
71b70444e0
|
@ -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,12 +143,11 @@ 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();
|
||||||
|
@ -112,13 +160,10 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
@ -136,11 +181,8 @@ impl Database {
|
||||||
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) {
|
||||||
|
@ -152,16 +194,13 @@ impl Database {
|
||||||
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() {
|
||||||
|
|
Loading…
Reference in New Issue