parent
2f228ff2cf
commit
956894c07e
|
@ -1,69 +1,94 @@
|
||||||
use std::collections::HashMap;
|
#[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 historgrams
|
||||||
|
Indices(Vec<u64>)
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_color_distribution(image_data: &[u8]) -> HashMap<u32, f32> {
|
impl Default for FeatureResult {
|
||||||
let mut color_distribution: HashMap<u32, f32> = HashMap::new();
|
fn default() -> Self {
|
||||||
let total_pixels = image_data.len() as f32 / 4.0;//für 4 Werte
|
FeatureResult::Bool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for pixel in image_data.chunks_exact(4) {
|
/// For some feature return type we want to implement a custom compare function
|
||||||
let r = pixel[0] as u32;
|
/// for example: historgrams are compared with cosine similarity
|
||||||
let g = pixel[1] as u32;
|
impl PartialEq for FeatureResult {
|
||||||
let b = pixel[2] as u32;
|
fn eq(&self, other: &Self) -> bool {
|
||||||
let a = pixel[3] as u32;
|
match (self, other) {
|
||||||
let rgba = (r << 24) | (g << 16) | (b << 8) | a;
|
(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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*color_distribution.entry(rgba).or_insert(0.0) += 1.0;
|
type FeatureGenerator = Box<dyn Fn(crate::Image<f32>) -> (String, FeatureResult)>;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
|
struct Database {
|
||||||
|
images: HashMap<String, 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 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])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, count) in &mut color_distribution {
|
pub fn add_image(&mut self, path: String) {
|
||||||
*count /= total_pixels;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
color_distribution
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_average_brightness(image: &[u8]) -> u8 {
|
/// example feature implementation
|
||||||
let mut sum: u32 = 0;
|
fn average_luminance(image: Image<f32>) -> (String, FeatureResult) {
|
||||||
let mut count: u32 = 0;
|
(String::from("average-brightness"), FeatureResult::F32(0.0))
|
||||||
|
|
||||||
for i in (0..image.len()).step_by(4) {
|
|
||||||
let r = image[i] as u32;
|
|
||||||
let g = image[i + 1] as u32;
|
|
||||||
let b = image[i + 2] as u32;
|
|
||||||
|
|
||||||
// (0.299 * R) + (0.587 * G) + (0.114 * B)
|
|
||||||
let brightness = ((0.299 * r as f32) + (0.587 * g as f32) + (0.114 * b as f32)).round() as u32;
|
|
||||||
|
|
||||||
sum += brightness;
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
let average_brightness = (sum / count) as u8;
|
|
||||||
average_brightness
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let mut data = Database::default();
|
||||||
|
|
||||||
fn main() {
|
data.add_feature(Box::new(average_luminance));
|
||||||
|
|
||||||
test2();
|
|
||||||
|
|
||||||
}
|
let _as_json = serde_json::to_string(&data);
|
||||||
|
|
||||||
fn test2(){
|
|
||||||
|
|
||||||
let image_data: Vec<(u8, u8, u8, u8)> = vec![
|
|
||||||
(255, 0, 0, 255), // Red
|
|
||||||
(0, 255, 0, 255), // Green
|
|
||||||
(0, 0, 255, 255), // Blue
|
|
||||||
];
|
|
||||||
//convert image data to useable &u8 slice
|
|
||||||
let byte_slice: &[u8] = unsafe {
|
|
||||||
std::slice::from_raw_parts(
|
|
||||||
image_data.as_ptr() as *const u8,
|
|
||||||
image_data.len() * 4,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let color_distribution = extract_color_distribution(&byte_slice);
|
|
||||||
let color_distribution_vec: Vec<f32> = color_distribution.values().cloned().collect();
|
|
||||||
let average_brightness = extract_average_brightness(&byte_slice);
|
|
||||||
println!("{:?}", average_brightness);
|
|
||||||
println!("{:?}", color_distribution_vec);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue