diff --git a/Cargo.toml b/Cargo.toml index 69cd0ba..25eeb42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,8 @@ criterion = "0.5.1" [[bench]] name = "multithreading" +harness = false + +[[bench]] +name = "indexing" harness = false \ No newline at end of file diff --git a/benches/indexing.rs b/benches/indexing.rs new file mode 100644 index 0000000..e5097f1 --- /dev/null +++ b/benches/indexing.rs @@ -0,0 +1,36 @@ +use std::path::Path; + +use criterion::Criterion; +use criterion::{black_box, criterion_group, criterion_main}; +use imsearch::search_index::{Database, FeatureGenerator}; + +pub fn bench_images(c: &mut Criterion) { + c.bench_function("indexing images", |b| { + b.iter(|| { + let files: Vec = std::fs::read_dir("res/benchmark/") + .unwrap() + .map(|f| f.unwrap().path()) + .collect(); + + let feats: Vec = vec![ + imsearch::feature::luminance_distribution, + imsearch::feature::color_distribution, + imsearch::feature::average_luminance, + imsearch::feature::aspect_ratio, + ]; + + let db = Database::new(&files, feats).unwrap(); + + black_box( + db.search( + Path::new("res/benchmark/bird.png"), + imsearch::feature::luminance_distribution, + ) + .unwrap(), + ); + }) + }); +} + +criterion_group!(benches, bench_images); +criterion_main!(benches); diff --git a/res/benchmark/backlit_flower.png b/res/benchmark/backlit_flower.png new file mode 100644 index 0000000..6fe65c2 Binary files /dev/null and b/res/benchmark/backlit_flower.png differ diff --git a/res/benchmark/ball_flower.png b/res/benchmark/ball_flower.png new file mode 100644 index 0000000..d159813 Binary files /dev/null and b/res/benchmark/ball_flower.png differ diff --git a/res/benchmark/bird.png b/res/benchmark/bird.png new file mode 100644 index 0000000..339b6de Binary files /dev/null and b/res/benchmark/bird.png differ diff --git a/res/benchmark/blue_sky_hessen.png b/res/benchmark/blue_sky_hessen.png new file mode 100644 index 0000000..bc3a860 Binary files /dev/null and b/res/benchmark/blue_sky_hessen.png differ diff --git a/res/benchmark/daisy_gray.png b/res/benchmark/daisy_gray.png new file mode 100644 index 0000000..7c41837 Binary files /dev/null and b/res/benchmark/daisy_gray.png differ diff --git a/res/benchmark/daisy_reddish_1.png b/res/benchmark/daisy_reddish_1.png new file mode 100644 index 0000000..04462fb Binary files /dev/null and b/res/benchmark/daisy_reddish_1.png differ diff --git a/res/benchmark/daisy_reddish_2.png b/res/benchmark/daisy_reddish_2.png new file mode 100644 index 0000000..da04dc2 Binary files /dev/null and b/res/benchmark/daisy_reddish_2.png differ diff --git a/res/benchmark/flower_hover.png b/res/benchmark/flower_hover.png new file mode 100644 index 0000000..bc8c976 Binary files /dev/null and b/res/benchmark/flower_hover.png differ diff --git a/res/benchmark/flower_pink.png b/res/benchmark/flower_pink.png new file mode 100644 index 0000000..89e8829 Binary files /dev/null and b/res/benchmark/flower_pink.png differ diff --git a/res/benchmark/flower_purple_1.png b/res/benchmark/flower_purple_1.png new file mode 100644 index 0000000..a802daa Binary files /dev/null and b/res/benchmark/flower_purple_1.png differ diff --git a/res/benchmark/flower_purple_2.png b/res/benchmark/flower_purple_2.png new file mode 100644 index 0000000..5e1cac3 Binary files /dev/null and b/res/benchmark/flower_purple_2.png differ diff --git a/res/benchmark/flower_yellow.png b/res/benchmark/flower_yellow.png new file mode 100644 index 0000000..0ae0aef Binary files /dev/null and b/res/benchmark/flower_yellow.png differ diff --git a/res/benchmark/flowers_gray.png b/res/benchmark/flowers_gray.png new file mode 100644 index 0000000..5d4fa6b Binary files /dev/null and b/res/benchmark/flowers_gray.png differ diff --git a/res/benchmark/hut.png b/res/benchmark/hut.png new file mode 100644 index 0000000..61ce192 Binary files /dev/null and b/res/benchmark/hut.png differ diff --git a/res/benchmark/ice_flower.png b/res/benchmark/ice_flower.png new file mode 100644 index 0000000..452803d Binary files /dev/null and b/res/benchmark/ice_flower.png differ diff --git a/res/benchmark/lago_como_1.png b/res/benchmark/lago_como_1.png new file mode 100644 index 0000000..44c57ee Binary files /dev/null and b/res/benchmark/lago_como_1.png differ diff --git a/res/benchmark/lago_como_2.png b/res/benchmark/lago_como_2.png new file mode 100644 index 0000000..53739c2 Binary files /dev/null and b/res/benchmark/lago_como_2.png differ diff --git a/res/benchmark/mushroom.png b/res/benchmark/mushroom.png new file mode 100644 index 0000000..6029075 Binary files /dev/null and b/res/benchmark/mushroom.png differ diff --git a/res/benchmark/odenwald.png b/res/benchmark/odenwald.png new file mode 100644 index 0000000..fdfb13b Binary files /dev/null and b/res/benchmark/odenwald.png differ diff --git a/res/benchmark/plant_green.png b/res/benchmark/plant_green.png new file mode 100644 index 0000000..d2e2825 Binary files /dev/null and b/res/benchmark/plant_green.png differ diff --git a/res/benchmark/sheep.png b/res/benchmark/sheep.png new file mode 100644 index 0000000..c0e1981 Binary files /dev/null and b/res/benchmark/sheep.png differ diff --git a/res/benchmark/sunset_beach.png b/res/benchmark/sunset_beach.png new file mode 100644 index 0000000..c44c95a Binary files /dev/null and b/res/benchmark/sunset_beach.png differ diff --git a/res/benchmark/sunset_hessen.png b/res/benchmark/sunset_hessen.png new file mode 100644 index 0000000..7313353 Binary files /dev/null and b/res/benchmark/sunset_hessen.png differ diff --git a/res/benchmark/sunset_tree.png b/res/benchmark/sunset_tree.png new file mode 100644 index 0000000..fccfbfa Binary files /dev/null and b/res/benchmark/sunset_tree.png differ diff --git a/res/benchmark/town_blue.png b/res/benchmark/town_blue.png new file mode 100644 index 0000000..a0a903d Binary files /dev/null and b/res/benchmark/town_blue.png differ diff --git a/src/Feature/mod.rs b/src/Feature/mod.rs deleted file mode 100644 index 9507bce..0000000 --- a/src/Feature/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::sync::Arc; - -#[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) -} - -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: 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, - } - } -} - -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]) - } - } - - 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); - } -} - -fn average_luminance(image: Arc>) -> (String, FeatureResult) { - let num_pixels = image.pixels.len() as u32; - let total_brightness: f32 = image.pixels - .iter() - .map(|(r, g, b, _)| 0.299 * r + 0.587 * g + 0.114 * b) // Calculate Y for each pixel - .sum(); - let average_brightness = total_brightness / num_pixels as f32; - - let feature_name = String::from("average-brightness"); - let feature_result = FeatureResult::F32(average_brightness); - - (feature_name, feature_result) -} -fn compare_Dim(image0: Arc>, image1: Arc>) -> (String, FeatureResult) { - let a = image0.width as f32 / image0.height as f32; - let b = image1.width as f32 / image1.height as f32; - let equal = a == b; - - let feature_name = String::from("Dimension-comparison"); - let feature_result = FeatureResult::Bool(equal); - - (feature_name, feature_result) -} diff --git a/src/feature/mod.rs b/src/feature/mod.rs index b5bd2c9..4906ee1 100644 --- a/src/feature/mod.rs +++ b/src/feature/mod.rs @@ -8,34 +8,37 @@ //! - aspect ratio of images computed a width/height //! All features are designed to used with sRGB color channels only. -use std::sync::Arc; use crate::{image::Image, search_index::FeatureResult}; +use std::sync::Arc; #[allow(unused)] // from https://github.com/programmieren-mit-rust/pr-ferrisgroup/issues/8 by @SirTalksalot75 /// Compute a basic distribution of values from all color channels and count their apprearances in buckets. /// This function will use 5 buckets per channel. -fn color_distribution(image: Arc>) -> (String, FeatureResult) { +pub fn color_distribution(image: Arc>) -> (String, FeatureResult) { const N: usize = 5; let mut histogram = vec![0u64; N * 3 + 1]; - const INV_255: f32 = 1./255. * N as f32; + const INV_255: f32 = 1. / 255. * N as f32; for (r, g, b, _) in image.iter() { // map linear channel value to bin index - histogram[ (r * INV_255) as usize] += 1; - histogram[ (g * INV_255) as usize * 2 ] += 1; - histogram[ (b * INV_255) as usize * 3 ] += 1; + histogram[(r * INV_255) as usize] += 1; + histogram[(g * INV_255) as usize * 2] += 1; + histogram[(b * INV_255) as usize * 3] += 1; } - (String::from("luminance-distribution"), FeatureResult::Indices(histogram)) + ( + String::from("luminance-distribution"), + FeatureResult::Indices(histogram), + ) } #[allow(unused)] // from https://github.com/programmieren-mit-rust/pr-ferrisgroup/issues/8 by @SirTalksalot75 /// Compute a basic distribution of luminance values and count their apprearances in buckets. /// Luminance is calculated via Digital ITU BT.601 and NOT the more common Photometric ITU BT.709 -fn luminance_distribution(image: Arc>) -> (String, FeatureResult) { +pub fn luminance_distribution(image: Arc>) -> (String, FeatureResult) { let mut histogram = vec![0u64; 256]; // Assuming 256 bins for the histogram for (r, g, b, _) in image.iter() { @@ -44,15 +47,18 @@ fn luminance_distribution(image: Arc>) -> (String, FeatureResult) { let luminance = (0.299 * r + 0.587 * g + 0.114 * b) as usize; histogram[luminance] += 1; } - - (String::from("luminance-distribution"), FeatureResult::Indices(histogram)) + + ( + String::from("luminance-distribution"), + FeatureResult::Indices(histogram), + ) } #[allow(unused)] // from https://github.com/programmieren-mit-rust/pr-ferrisgroup/issues/8 by @SirTalksalot75 /// Compute the average luminance of all pixels in a given image. /// Luminance is calculated via Digital ITU BT.601 and NOT the more common Photometric ITU BT.709 -fn average_luminance(image: Arc>) -> (String, FeatureResult) { +pub fn average_luminance(image: Arc>) -> (String, FeatureResult) { let num_pixels = image.pixels().len() as u32; let total_brightness: f32 = image .iter() @@ -68,7 +74,7 @@ fn average_luminance(image: Arc>) -> (String, FeatureResult) { #[allow(unused)] // from https://github.com/programmieren-mit-rust/pr-ferrisgroup/issues/8 by @SirTalksalot75 -fn aspect_ratio(image: Arc>) -> (String, FeatureResult) { +pub fn aspect_ratio(image: Arc>) -> (String, FeatureResult) { let a = image.width() as f32 / image.height() as f32; (String::from("aspect-ratio"), FeatureResult::Percent(a)) @@ -93,7 +99,13 @@ mod test { let db = Database::new(&files, feats).unwrap(); - for (path, sim) in db.search(Path::new("res/integration/gray_image.png"), color_distribution).unwrap() { + for (path, sim) in db + .search( + Path::new("res/integration/gray_image.png"), + color_distribution, + ) + .unwrap() + { let file_name = path.file_name().unwrap().to_str().unwrap(); if file_name.eq("gray_image.png") { assert_eq!(sim, 1.); @@ -113,7 +125,13 @@ mod test { let db = Database::new(&files, feats).unwrap(); - for (path, sim) in db.search(Path::new("res/integration/gray_image.png"), average_luminance).unwrap() { + for (path, sim) in db + .search( + Path::new("res/integration/gray_image.png"), + average_luminance, + ) + .unwrap() + { let file_name = path.file_name().unwrap().to_str().unwrap(); if file_name.eq("gray_image.png") { assert_eq!(sim, 1.); @@ -133,7 +151,10 @@ mod test { let db = Database::new(&files, feats).unwrap(); - for (path, sim) in db.search(Path::new("res/integration/gray_image.png"), aspect_ratio).unwrap() { + for (path, sim) in db + .search(Path::new("res/integration/gray_image.png"), aspect_ratio) + .unwrap() + { let file_name = path.file_name().unwrap().to_str().unwrap(); if file_name.eq("gray_image.png") { assert_eq!(sim, 1.); @@ -141,4 +162,4 @@ mod test { println!("{} {}", file_name, sim); } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 2532a15..d086197 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,19 +7,18 @@ //! .unwrap() //! .map(|f| f.unwrap().path()) //! .collect(); -//! +//! //! let feats: Vec = vec![average_rgb_value]; -//! +//! //! let db = Database::new(&files, feats).unwrap(); -//! +//! //! db.write_to_file(json); -//! ``` +//! ``` extern crate core; +pub mod feature; pub mod image; pub mod image_loader; pub mod multithreading; pub mod search_index; -pub mod feature; -