added feature module

This commit is contained in:
Sven Vogel 2023-06-18 11:24:17 +02:00
parent 805397a6e1
commit 7cd1b7474a
2 changed files with 163 additions and 0 deletions

144
src/feature/mod.rs Normal file
View File

@ -0,0 +1,144 @@
//! # Prebuild features
//! This module provides a set of prebuild features ready to be used with a database
//! to index images.
//! Features include:
//! - distribution of colors (via histogram)
//! - distribution of luminance (via histogram)
//! - average luminance
//! - 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};
#[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<Image<f32>>) -> (String, FeatureResult) {
const N: usize = 5;
let mut histogram = vec![0u64; N * 3 + 1];
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;
}
(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<Image<f32>>) -> (String, FeatureResult) {
let mut histogram = vec![0u64; 256]; // Assuming 256 bins for the histogram
for (r, g, b, _) in image.iter() {
// map luminance to bin index
// luminance is a value between 0 and 255.
let luminance = (0.299 * r + 0.587 * g + 0.114 * b) as usize;
histogram[luminance] += 1;
}
(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<Image<f32>>) -> (String, FeatureResult) {
let num_pixels = image.pixels().len() as u32;
let total_brightness: f32 = image
.iter()
.map(|(r, g, b, _)| (0.299 * r + 0.587 * g + 0.114 * b) / 255.0) // 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::Percent(average_brightness);
(feature_name, feature_result)
}
#[allow(unused)]
// from https://github.com/programmieren-mit-rust/pr-ferrisgroup/issues/8 by @SirTalksalot75
fn aspect_ratio(image: Arc<Image<f32>>) -> (String, FeatureResult) {
let a = image.width() as f32 / image.height() as f32;
(String::from("aspect-ratio"), FeatureResult::Percent(a))
}
#[cfg(test)]
mod test {
use std::path::Path;
use crate::search_index::{Database, FeatureGenerator};
use super::*;
#[test]
fn test_histogram() {
let files: Vec<std::path::PathBuf> = std::fs::read_dir("res/integration/")
.unwrap()
.map(|f| f.unwrap().path())
.collect();
let feats: Vec<FeatureGenerator> = vec![color_distribution];
let db = Database::new(&files, feats).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.);
}
println!("{} {}", file_name, sim);
}
}
#[test]
fn test_average_luminance() {
let files: Vec<std::path::PathBuf> = std::fs::read_dir("res/integration/")
.unwrap()
.map(|f| f.unwrap().path())
.collect();
let feats: Vec<FeatureGenerator> = vec![average_luminance];
let db = Database::new(&files, feats).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.);
}
println!("{} {}", file_name, sim);
}
}
#[test]
fn test_aspect_ratio() {
let files: Vec<std::path::PathBuf> = std::fs::read_dir("res/integration/")
.unwrap()
.map(|f| f.unwrap().path())
.collect();
let feats: Vec<FeatureGenerator> = vec![aspect_ratio];
let db = Database::new(&files, feats).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.);
}
println!("{} {}", file_name, sim);
}
}
}

View File

@ -1,6 +1,25 @@
//! # Imsearch
//! Extensible library for creating an image based search engine.
//! The library exposes the functionality to create databases which index various images stored as png files.
//! # Examples
//! ```ignore
//! let files: Vec<PathBuf> = std::fs::read_dir("image/folder/")
//! .unwrap()
//! .map(|f| f.unwrap().path())
//! .collect();
//!
//! let feats: Vec<FeatureGenerator> = vec![average_rgb_value];
//!
//! let db = Database::new(&files, feats).unwrap();
//!
//! db.write_to_file(json);
//! ```
extern crate core;
pub mod image;
pub mod image_loader;
pub mod multithreading;
pub mod search_index;
pub mod feature;