clustering

This commit is contained in:
2025-05-04 20:13:33 +02:00
parent 37008cbcbf
commit 66dae40de8
2 changed files with 38 additions and 19 deletions

View File

@@ -11,6 +11,4 @@ image = "0.25.6"
show-image = "0.14.1" show-image = "0.14.1"
rand = "0.9.1" rand = "0.9.1"
rayon = "1.10.0" rayon = "1.10.0"
linfa = "0.7.1" hdbscan = "0.10.0"
linfa-clustering = "0.7.1"
ndarray = "0.16.1"

View File

@@ -1,20 +1,15 @@
use linfa::traits::Transformer;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::iter::zip;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use image::{DynamicImage, ImageReader, Rgb}; use hdbscan::{Hdbscan, HdbscanHyperParams};
use linfa_clustering::Dbscan; use image::{DynamicImage, ImageReader, Rgb, RgbImage};
use rand::{Rng}; use rand::{Rng};
use show_image::{create_window, ImageInfo, ImageView, PixelFormat}; use show_image::{create_window, ImageInfo, ImageView, PixelFormat};
use rayon::prelude::*; use rayon::prelude::*;
use ndarray::{Array2, Axis};
use linfa::prelude::*; fn extract_highlights_and_distort(image: &Vec<u8>, width: u32, threshold: u8, downsample: f64) -> Vec<Vec<f64>> {
// We'll build our dataset on our own using ndarray and rand
use ndarray::prelude::*;
fn extract_highlights(image: &Vec<u8>, width: u32, threshold: u8, downsample: f64) -> Array2<u32> {
let max_rng = (u32::MAX as f64 * downsample) as u32; let max_rng = (u32::MAX as f64 * downsample) as u32;
let width_2 = width as f64 / 2.0;
let poi = image let poi = image
.par_iter() .par_iter()
.enumerate() .enumerate()
@@ -23,11 +18,13 @@ fn extract_highlights(image: &Vec<u8>, width: u32, threshold: u8, downsample: f6
let mut rng = rand::rng(); let mut rng = rand::rng();
rng.random::<u32>() < max_rng rng.random::<u32>() < 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(); let mut vec = Vec::new();
vec.par_extend(poi); vec.par_extend(poi);
Array2::from(vec) vec
} }
#[show_image::main] #[show_image::main]
@@ -40,15 +37,39 @@ fn main() {
let window = create_window("image", Default::default()).unwrap(); let window = create_window("image", Default::default()).unwrap();
let mut t: VecDeque<Duration> = VecDeque::with_capacity(25); let mut t: VecDeque<Duration> = VecDeque::with_capacity(25);
let params = HdbscanHyperParams::builder()
.epsilon(5.0)
.min_samples(5)
.min_cluster_size(100)
.build();
loop { loop {
let start = Instant::now(); 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| { zip(poi, labels).for_each(|(pixel, label)| {
rgb_image.put_pixel(pixel[0], pixel[1], Rgb([255, 0, 0])) 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()); let image = ImageView::new(ImageInfo::new(PixelFormat::Rgb8, width, height), rgb_image.iter().as_slice());