From 1b6d8544ffd905479f864795a685abf84a5ecb97 Mon Sep 17 00:00:00 2001 From: colbyn Date: Wed, 4 Mar 2020 11:31:28 -0700 Subject: [PATCH] Misc tweaks. --- classifier/assets/.gitignore | 3 +- classifier/src/color/quant.rs | 6 +- classifier/src/process.rs | 263 ++++++++++++++++++++++++++-------- 3 files changed, 205 insertions(+), 67 deletions(-) diff --git a/classifier/assets/.gitignore b/classifier/assets/.gitignore index 179f20e..acae3f0 100644 --- a/classifier/assets/.gitignore +++ b/classifier/assets/.gitignore @@ -1,3 +1,4 @@ output/ samples/ -ref \ No newline at end of file +ref +ref1 \ No newline at end of file diff --git a/classifier/src/color/quant.rs b/classifier/src/color/quant.rs index ce81149..d85533d 100644 --- a/classifier/src/color/quant.rs +++ b/classifier/src/color/quant.rs @@ -45,7 +45,7 @@ pub fn compress(source: &DynamicImage, num_colors: usize) -> Result, Str let (ditherer, optimizer) = { // VALUES let d = ditherer::None; - let o = optimizer::WeightedKMeans; + let o = optimizer::KMeans; // DONE let d: Box = Box::new(d); let o: Box = Box::new(o); @@ -62,13 +62,13 @@ pub fn compress(source: &DynamicImage, num_colors: usize) -> Result, Str for _ in 0..num_colors { quantizer.step(); if quantizer.num_colors() % kmeans_step == 0 { - quantizer = quantizer.optimize(&*optimizer, 2); + quantizer = quantizer.optimize(&*optimizer, 4); } // quantizer = quantizer.optimize(&*optimizer, 16); } // PALETTE DATA let palette = quantizer.colors(&colorspace); - let palette = optimizer.optimize_palette(&colorspace, &palette, &histogram, 8); + let palette = optimizer.optimize_palette(&colorspace, &palette, &histogram, 16); let remapper = Remapper::new(&palette, &colorspace, &*ditherer); // PIXEL DATA let out_data: Vec = remapper diff --git a/classifier/src/process.rs b/classifier/src/process.rs index 77d3c5a..bef4ec6 100644 --- a/classifier/src/process.rs +++ b/classifier/src/process.rs @@ -19,89 +19,222 @@ use serde::{Serialize, Deserialize}; use crate::color::palette::{self, ToPrettyRgbPalette}; + /////////////////////////////////////////////////////////////////////////////// // NOISY LAYER /////////////////////////////////////////////////////////////////////////////// +#[derive(Clone)] pub struct NoisyLayer(DynamicImage); impl NoisyLayer { - pub fn new(input: DynamicImage) -> Self { - unimplemented!() + pub fn new(source: Image>) -> Self { + // INIT PIXEL-TABLE + let mut pixel_table = HashMap::>::new(); + for (x, y, px) in source.enumerate_pixels() { + if let Some(xs) = pixel_table.get_mut(&px.0[0]) { + xs.push((x, y)); + } else { + pixel_table.insert(px.0[0], vec![(x, y)]); + } + } + // COMPUTE CENTER POINTS + let centers = pixel_table + .into_iter() + .map(|(px, cs)| { + let cs_len = cs.len() as u32; + let sum = cs + .into_iter() + .fold((0u32, 0u32), |(xs, ys), (x, y)| { + (xs + x, ys + y) + }); + (sum.0 / cs_len, sum.1 / cs_len) + }) + .collect::>(); + let mut output = ::image::GrayImage::from_pixel( + source.width(), + source.height(), + Luma([0]) + ); + for (cx, cy) in centers.into_iter() { + let px = output.get_pixel_mut(cx, cy); + px[0] = std::u8::MAX; + } + // // INVERT & FILTER + // let empty_pixel = Luma([std::u8::MAX]); + // let feature_pixel = Luma([0]); + // let mut output = ::imageproc::map::map_pixels(&output, |x, y, mut px| { + // // NOTHING TO DO + // if px.0[0] == 0 { + // return empty_pixel; + // } + // // FILTER + // let lookup = |cx: u32, cy: u32| -> Option> { + // if output.in_bounds(cx, cy) { + // Some(*output.get_pixel(cx, cy)) + // } else { + // None + // } + // }; + // let north = lookup(x, y + 1); + // let south = lookup(x, y - 1); + // let east = lookup(x + 1, y); + // let west = lookup(x - 1, y); + // let neighbords = vec![north, south, east, west]; + // let connected = neighbords + // .into_iter() + // .filter_map(|x| x) + // .any(|x| x.0[0] != 0); + // if connected { + // feature_pixel + // } else { + // empty_pixel + // } + // }); + // imageproc::morphology::open_mut(&mut output, Norm::L1, 2); + // // INVERT + // for (x, y, px) in output.enumerate_pixels_mut() { + // if px.0[0] == 0 { + // *px = Luma([std::u8::MAX]); + // } else { + // *px = Luma([0]); + // } + // } + // // MISC + // imageproc::morphology::dilate_mut(&mut output, Norm::L1, 2); + // DONE + NoisyLayer(DynamicImage::ImageLuma8(output)) } } - /////////////////////////////////////////////////////////////////////////////// -// PASSES +// DENSE LAYER /////////////////////////////////////////////////////////////////////////////// +#[derive(Clone)] +pub struct DenseLayer(DynamicImage); -pub fn quantizer(image: &DynamicImage) -> DynamicImage { - let image = image.resize_exact(600, 600, FilterType::Lanczos3); - let image = image.unsharpen(1.2, 4); - let image = crate::color::quant::reduce_palette(&image, 64); - let image = image.to_luma(); - let image = ::imageproc::map::map_pixels(&image, |x, y, mut px| { - if px.0[0] == 0 { - px.0[0] = 1; +impl DenseLayer { + pub fn new(source: Image>) -> Self { + // INIT PIXEL-TABLE + let mut pixel_table = HashMap::>::new(); + for (x, y, px) in source.enumerate_pixels() { + if let Some(xs) = pixel_table.get_mut(&px.0[0]) { + xs.push((x, y)); + } else { + pixel_table.insert(px.0[0], vec![(x, y)]); + } } - px - }); - let image = imageproc::region_labelling::connected_components( - &image, - Connectivity::Eight, - Luma::black() - ); - let image = palette::set_region(&image, Luma([std::u32::MAX]), |_, count| count > (120 * 120)); - - // DONE - let image = image.to_pretty_rgb_palette(); - DynamicImage::ImageRgb8(image) + // FILTER + let mut output = ::image::GrayImage::from_fn(source.width(), source.height(), |x, y| { + let px = source.get_pixel(x, y); + let val = pixel_table.get(&px.0[0]).expect("missing pixel"); + if val.len() <= (2 * 2) { + Luma([std::u8::MAX]) + } else { + Luma([0]) + } + }); + // INVERT + for (x, y, px) in output.enumerate_pixels_mut() { + if px.0[0] == 0 { + *px = Luma([std::u8::MAX]); + } else { + *px = Luma([0]); + } + } + // DONE + DenseLayer(DynamicImage::ImageLuma8(output)) + } } /////////////////////////////////////////////////////////////////////////////// -// CLASSIFY +// GRADIENT LAYER /////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum Class { - HiBasic, - Hi, - ExLo, - Lo, -} +#[derive(Clone)] +pub struct GradientLayer(DynamicImage); -impl Class { - pub fn all_variants() -> Vec { - vec![ - Class::HiBasic, - Class::Hi, - Class::ExLo, - Class::Lo, - ] - } - pub fn id(&self) -> u8 { - match self { - Class::HiBasic => 0, - Class::Hi => 1, - Class::ExLo => 2, - Class::Lo => 3, - } +impl GradientLayer { + pub fn new(source: &Image>) -> Self { + let output = ::imageproc::edges::canny(source, 10.0, 20.0); + GradientLayer(DynamicImage::ImageLuma8(output)) } } -impl std::fmt::Display for Class { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = match self { - Class::HiBasic => "hi-basic", - Class::Hi => "hi", - Class::ExLo => "ex-lo", - Class::Lo => "lo", - }; - write!(f, "{}", value) - } + +/////////////////////////////////////////////////////////////////////////////// +// EVAL +/////////////////////////////////////////////////////////////////////////////// + +// pub fn eval(image: &DynamicImage) -> DynamicImage { +// let image = image.resize_exact(600, 600, FilterType::Lanczos3); +// let image = image.unsharpen(1.2, 4); +// let image = crate::color::quant::reduce_palette(&image, 64); +// let image = image.to_luma(); +// let image = ::imageproc::map::map_pixels(&image, |x, y, mut px| { +// if px.0[0] == 0 { +// px.0[0] = 1; +// } +// px +// }); +// let image = imageproc::region_labelling::connected_components( +// &image, +// Connectivity::Eight, +// Luma::black() +// ); + +// // let image = palette::set_region(&image, Luma([std::u32::MAX]), |_, count| count > (120 * 120)); + +// let image = DenseLayer::new(image); +// image.0 + +// // DONE +// // let image = image.to_pretty_rgb_palette(); +// // DynamicImage::ImageRgb8(image) +// } + +pub fn eval(source: &DynamicImage) -> DynamicImage { + let output = source.resize_exact(600, 600, FilterType::Lanczos3); + // let output = output.unsharpen(1.2, 4); + // let output = crate::color::quant::reduce_palette(&output, 12); + // REGION-LABEL + let output = output.to_luma(); + // let output = ::imageproc::map::map_pixels(&output, |x, y, mut px| { + // if px.0[0] == 0 { + // px.0[0] = 1; + // } + // px + // }); + // let output = imageproc::region_labelling::connected_components( + // &output, + // Connectivity::Eight, + // Luma::black() + // ); + + // // LAYERS + // let output = NoisyLayer::new(output); + // output.0 + // DONE + + // MAIN-FILTERS + // let output = palette::set_region(&output, Luma([0]), |_, count| count < (60 * 60)); + // let output = palette::set_region(&output, Luma([0]), |pixel, count| { + // if pixel.0[0] == 0 { + // false + // } else { + // count > (200 * 200) + // } + // }); + + // // GRADIENT + // let output = GradientLayer::new(&output.to_luma()); + + // DONE + DynamicImage::ImageLuma8(output) + // let output = output.to_pretty_rgb_palette(); + // DynamicImage::ImageRgb8(output) } @@ -113,9 +246,13 @@ impl std::fmt::Display for Class { pub fn run() { let output_dir = PathBuf::from("assets/output"); std::fs::create_dir_all(&output_dir); - let paths = glob::glob("assets/samples/focus/**/*.jpeg") - .expect("input glob") - .filter_map(Result::ok) + let paths = vec![ + glob::glob("assets/samples/focus/**/*.jpeg"), + glob::glob("assets/samples/focus/**/*.png"), + ]; + paths + .into_iter() + .flat_map(|x| x.expect("input glob").filter_map(Result::ok)) .collect::>() .into_par_iter() .for_each(|input_path| { @@ -123,9 +260,9 @@ pub fn run() { .file_name() .map(|name| output_dir.join(name)) .expect("init output file name"); - output_path.set_extension("jpeg"); + output_path.set_extension("png"); let src_image = ::image::open(input_path).expect("open source image"); - let out_image = quantizer(&src_image); + let out_image = eval(&src_image); out_image.save(output_path); }); }