diff --git a/Cargo.lock b/Cargo.lock index b1f2f33..bfb2345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,6 +304,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "image" version = "0.23.14" @@ -954,8 +960,10 @@ name = "yew-app" version = "0.1.0" dependencies = [ "gloo-utils", + "hex", "image", "imageproc", + "lazy_static", "log", "wasm-bindgen", "wasm-logger", diff --git a/Cargo.toml b/Cargo.toml index b2bccab..991092e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,6 @@ web-sys = {version = "0.3.56", features = [ ]} yew = "0.19" image = "0.23.14" -imageproc="0.22.0" \ No newline at end of file +imageproc="0.22.0" +lazy_static="1.4.0" +hex="0.4.3" \ No newline at end of file diff --git a/index.html b/index.html index d553e16..711f95d 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Spiegel diff --git a/src/app.rs b/src/app.rs index 0f41c61..433ea69 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,24 +1,33 @@ use gloo_utils::document; -use wasm_bindgen::JsCast; +use image::RgbImage; +use wasm_bindgen::{Clamped, JsCast}; use web_sys::{DragEvent, HtmlImageElement}; use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData}; use web_sys::Url; use yew::{Component, Context, html, Html}; +use crate::transform; +use crate::transform::ColorSample; + pub enum Msg { Dropped(DragEvent), Dragged(DragEvent), ImageLoaded, } -pub struct DropPhoto {} +pub struct DropPhoto { + // color_samples: Vec +} impl Component for DropPhoto { type Message = Msg; type Properties = (); fn create(_ctx: &Context) -> Self { - Self {} + transform::init(); + Self { + // color_samples: transform::init().expect("") + } } fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { @@ -44,7 +53,6 @@ impl Component for DropPhoto { let img = document().get_element_by_id("source-image").expect("cannot get #source-image").dyn_into::().unwrap(); img.set_src(&url); } - } true } @@ -64,6 +72,17 @@ impl Component for DropPhoto { if let Some(drop_zone) = document().get_element_by_id("drop-zone") { drop_zone.set_attribute("style", "display:none").expect("Cannot update attribute"); } + + let imgdata = ctx + .get_image_data(0.0, 0.0, canvas.width() as f64, canvas.height() as f64) + .unwrap(); + let raw_pixels: Vec = imgdata.data().to_vec(); + let rgb_src = RgbImage::from_raw(canvas.width(), canvas.height(), raw_pixels).unwrap(); + // let transformed = transform::apply(rgb_src, &self.color_samples).expect("Cannot transform image"); + let image_data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&rgb_src.to_vec()), + canvas.width(), canvas.height()); + + ctx.put_image_data(&image_data.expect(""), 0.0, 0.0); } true } diff --git a/src/main.rs b/src/main.rs index 7909bb2..c3ce883 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod app; mod transform; mod quantizer; +mod samples; fn main() { wasm_logger::init(wasm_logger::Config::default()); diff --git a/src/transform.rs b/src/transform.rs index 40b85ec..28a5e0f 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -1,37 +1,38 @@ use std::{error::Error, fs, result::Result}; +use std::collections::HashMap; use image::{GenericImageView, ImageBuffer, Pixel, Rgb, RgbImage}; use imageproc::point::Point; -use crate::quantizer; -pub fn run(image_filename: &String) -> Result<(), Box> { - println!("reading image samples"); - let color_samples = read_color_samples()?; +use crate::{quantizer, samples}; - let src: RgbImage = image::open(image_filename).unwrap().into_rgb8(); - - println!("applying gaussian blur filter"); - let gauss = imageproc::filter::gaussian_blur_f32(&src, 2.0); - println!("applying median filter"); - let median = imageproc::filter::median_filter(&gauss, 2, 2); - println!("applying color quantization filter"); - let quantized = quantizer::quantize(&median, 256); - - println!("applying samples"); - let out = apply_samples_to_image(quantized, &color_samples); - out.save_with_format("output.jpg", image::ImageFormat::Jpeg)?; - - Ok(()) +struct Transformer { + color_samples: HashMap, //cache } -fn apply_samples_to_image(mut src: RgbImage, color_samples: &Vec) -> RgbImage{ +impl Transformer { + fn new() -> Self { + Self { color_samples: HashMap::new() } + } + + + pub fn apply(&mut self, src: RgbImage, color_samples: &mut HashMap) -> Result> { + let gauss = imageproc::filter::gaussian_blur_f32(&src, 2.0); + let median = imageproc::filter::median_filter(&gauss, 2, 2); + let quantized = quantizer::quantize(&median, 256); + let out = apply_samples_to_image(quantized, color_samples); + + Ok(out) + } +} +fn apply_samples_to_image(mut src: RgbImage, color_samples: &mut HashMap) -> RgbImage { let mut imgbuf = RgbImage::new(src.width(), src.height()); unsafe { for y in 0..src.height() { for x in 0..src.width() { let pixel = &src.unsafe_get_pixel(x, y); if imgbuf.unsafe_get_pixel(x, y).channels() == [0, 0, 0] { - if let Some(sample) = get_closest(&color_samples, pixel) { + if let Some(sample) = get_closest(olor_samples, pixel) { fill(&mut src, sample, &mut imgbuf, pixel, x, y); } } @@ -105,21 +106,31 @@ fn is_same(p1: &Rgb, p2: &Rgb) -> bool { && i16::abs(p1[2] as i16 - p2[2] as i16) < 4 } -fn get_closest<'a>( - color_samples: &'a Vec, +fn get_closest( + color_samples: &mut HashMap, pixel: &Rgb, -) -> Option<&'a ColorSample> { +) -> Option<&ColorSample> { let mut closest = None; let mut min_diff: f32 = 4294967295.0; //0xFFFFFFFF - for sample in color_samples { - let diff = get_distance(sample.r, sample.g, sample.b, pixel); + for sample in samples::SAMPLES { + let r = hex::decode(sample[0..2], ).unwrap()[0]; + let g = hex::decode(sample[2..4], ).unwrap()[0]; + let b = hex::decode(sample[4..6], ).unwrap()[0]; + let diff = get_distance(r,g,b, pixel); if diff < min_diff { closest = Some(sample); min_diff = diff; } } - closest + if color_samples.contains(&closest){ + return color_samples.get(&closest); + } else { + //download image + let image = ColorSample{r,g,b, image: RgbImage::from_raw(0,0,vec![]).unwrap()}; + color_samples.insert(sample.to_owned(), image); + } + None } fn get_distance(r: u8, g: u8, b: u8, c2: &Rgb) -> f32 { @@ -129,36 +140,46 @@ fn get_distance(r: u8, g: u8, b: u8, c2: &Rgb) -> f32 { return f32::sqrt(red_dif * red_dif + green_dif * green_dif + blue_dif * blue_dif); } -fn read_color_samples() -> Result, Box> { - let paths = fs::read_dir("samples")?; - let mut color_samples: Vec = Vec::new(); - for path in paths { - let path = path?.path(); - let filename = path.to_str().unwrap().to_owned(); +// fn read_color_samples() -> Result, Box> { +// let paths = fs::read_dir("samples")?; +// let mut color_samples: Vec = Vec::new(); +// for path in paths { +// let path = path?.path(); +// let filename = path.to_str().unwrap().to_owned(); +// +// if filename.ends_with(".jpg") { +// let sample_image: RgbImage = image::open(&filename).unwrap().into_rgb8(); +// let hex_r = &filename[8..10]; +// let hex_g = &filename[10..12]; +// let hex_b = &filename[12..14]; +// color_samples.push(ColorSample { +// r: u8::from_str_radix(&hex_r, 16).unwrap(), +// g: u8::from_str_radix(&hex_g, 16).unwrap(), +// b: u8::from_str_radix(&hex_b, 16).unwrap(), +// image: sample_image, +// }); +// } +// } +// Ok(color_samples) +// } - if filename.ends_with(".jpg") { - let sample_image: RgbImage = image::open(&filename).unwrap().into_rgb8(); - let hex_r = &filename[8..10]; - let hex_g = &filename[10..12]; - let hex_b = &filename[12..14]; - color_samples.push(ColorSample { - r: u8::from_str_radix(&hex_r, 16).unwrap(), - g: u8::from_str_radix(&hex_g, 16).unwrap(), - b: u8::from_str_radix(&hex_b, 16).unwrap(), - image: sample_image, - }); - } - } - Ok(color_samples) -} - -struct ColorSample { +pub struct ColorSample { r: u8, g: u8, b: u8, image: RgbImage, } +impl PartialEq for ColorSample{ + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } + + fn ne(&self, other: &Self) -> bool { + self.name != other.name + } +} + #[derive(Debug)] struct List { head: Option>, @@ -197,7 +218,6 @@ struct Node { #[cfg(test)] mod test { - use super::*; #[test]