diff --git a/Cargo.toml b/Cargo.toml index d74896c..eb198de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,4 @@ image = "0.25.6" show-image = "0.14.1" rand = "0.9.1" rayon = "1.10.0" -linfa = "0.7.1" -linfa-clustering = "0.7.1" -ndarray = "0.16.1" \ No newline at end of file +hdbscan = "0.10.0" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fab0196..3ffabae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,15 @@ -use linfa::traits::Transformer; use std::collections::VecDeque; +use std::iter::zip; use std::time::{Duration, Instant}; -use image::{DynamicImage, ImageReader, Rgb}; -use linfa_clustering::Dbscan; +use hdbscan::{Hdbscan, HdbscanHyperParams}; +use image::{DynamicImage, ImageReader, Rgb, RgbImage}; use rand::{Rng}; use show_image::{create_window, ImageInfo, ImageView, PixelFormat}; use rayon::prelude::*; -use ndarray::{Array2, Axis}; -use linfa::prelude::*; -// We'll build our dataset on our own using ndarray and rand -use ndarray::prelude::*; - - -fn extract_highlights(image: &Vec, width: u32, threshold: u8, downsample: f64) -> Array2 { +fn extract_highlights_and_distort(image: &Vec, width: u32, threshold: u8, downsample: f64) -> Vec> { let max_rng = (u32::MAX as f64 * downsample) as u32; + let width_2 = width as f64 / 2.0; let poi = image .par_iter() .enumerate() @@ -23,11 +18,13 @@ fn extract_highlights(image: &Vec, width: u32, threshold: u8, downsample: f6 let mut rng = rand::rng(); rng.random::() < max_rng }) - .map(move |(i, _)| [ i as u32 % width, i as u32 / width ]); + .map(move |(i, _)| { + vec![ i as f64 % width as f64 - width_2, i as f64 / width as f64 ] + }); let mut vec = Vec::new(); vec.par_extend(poi); - Array2::from(vec) + vec } #[show_image::main] @@ -40,15 +37,39 @@ fn main() { let window = create_window("image", Default::default()).unwrap(); let mut t: VecDeque = VecDeque::with_capacity(25); + let params = HdbscanHyperParams::builder() + .epsilon(5.0) + .min_samples(5) + .min_cluster_size(100) + .build(); + loop { let start = Instant::now(); - let poi = extract_highlights(&img_data, width, 196, 1.0); + let poi = extract_highlights_and_distort(&img_data, width, 196, 0.05); - let mut rgb_image = img.to_rgb8(); + let clusterer = Hdbscan::new(&poi, params.clone()); + let labels = clusterer.cluster().unwrap(); + let mut rgb_image = RgbImage::new(width, height); - poi.outer_iter().for_each(|pixel| { - rgb_image.put_pixel(pixel[0], pixel[1], Rgb([255, 0, 0])) + zip(poi, labels).for_each(|(pixel, label)| { + if label != -1 { + let color = vec![Rgb([255, 0, 0]), Rgb([0, 255, 0]), Rgb([0, 0, 255]), Rgb([0, 0, 0])][label as usize]; + let x = (pixel[0] + width as f64 / 2.0) as u32; + rgb_image.put_pixel(x, pixel[1] as u32, color); + if pixel[0] > 0.0 { + rgb_image.put_pixel(x - 1, pixel[1] as u32, color); + } + if pixel[0] < width as f64 - 1.0 { + rgb_image.put_pixel(x + 1, pixel[1] as u32, color); + } + if pixel[1] > 0.0 { + rgb_image.put_pixel(x, pixel[1] as u32 - 1, color); + } + if pixel[1] < height as f64 - 1.0 { + rgb_image.put_pixel(x, pixel[1] as u32 + 1, Rgb([label as u8 * 8, 0, 0])); + } + } }); let image = ImageView::new(ImageInfo::new(PixelFormat::Rgb8, width, height), rgb_image.iter().as_slice());