1use crate::error::Result;
7use image::{DynamicImage, Rgb, RgbImage};
8use scirs2_core::parallel_ops::*;
9use scirs2_core::random::prelude::*;
10use scirs2_core::random::seq::SliceRandom;
11use scirs2_core::random::{Rng, RngExt};
12use std::collections::HashMap;
13
14#[derive(Debug, Copy, Clone)]
16pub struct KMeansParams {
17 pub k: usize,
19 pub max_iterations: usize,
21 pub epsilon: f32,
23 pub init_method: InitMethod,
25}
26
27impl Default for KMeansParams {
28 fn default() -> Self {
29 Self {
30 k: 16,
31 max_iterations: 100,
32 epsilon: 1.0,
33 init_method: InitMethod::KMeansPlusPlus,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy)]
40pub enum InitMethod {
41 Random,
43 KMeansPlusPlus,
45 Frequency,
47}
48
49#[allow(dead_code)]
73pub fn kmeans_quantize(img: &DynamicImage, params: &KMeansParams) -> Result<DynamicImage> {
74 let rgb = img.to_rgb8();
75 let (width, height) = rgb.dimensions();
76
77 let mut colors = Vec::new();
79 for pixel in rgb.pixels() {
80 colors.push([pixel[0] as f32, pixel[1] as f32, pixel[2] as f32]);
81 }
82
83 let mut centers = initialize_centers(&colors, params);
85
86 for _iter in 0..params.max_iterations {
88 let assignments: Vec<usize> = colors
90 .par_iter()
91 .map(|color| {
92 let mut min_dist = f32::INFINITY;
93 let mut best_cluster = 0;
94
95 for (i, center) in centers.iter().enumerate() {
96 let dist = color_distance(color, center);
97 if dist < min_dist {
98 min_dist = dist;
99 best_cluster = i;
100 }
101 }
102
103 best_cluster
104 })
105 .collect();
106
107 let new_centers = update_centers(&colors, &assignments, params.k);
109
110 let mut max_change = 0.0f32;
112 for (old, new) in centers.iter().zip(new_centers.iter()) {
113 let change = color_distance(old, new);
114 if change > max_change {
115 max_change = change;
116 }
117 }
118
119 centers = new_centers;
120
121 if max_change < params.epsilon {
122 break;
123 }
124 }
125
126 let mut result = RgbImage::new(width, height);
128 let mut idx = 0;
129
130 for y in 0..height {
131 for x in 0..width {
132 let color = &colors[idx];
133 let mut min_dist = f32::INFINITY;
134 let mut best_center = ¢ers[0];
135
136 for center in ¢ers {
137 let dist = color_distance(color, center);
138 if dist < min_dist {
139 min_dist = dist;
140 best_center = center;
141 }
142 }
143
144 result.put_pixel(
145 x,
146 y,
147 Rgb([
148 best_center[0] as u8,
149 best_center[1] as u8,
150 best_center[2] as u8,
151 ]),
152 );
153
154 idx += 1;
155 }
156 }
157
158 Ok(DynamicImage::ImageRgb8(result))
159}
160
161#[allow(dead_code)]
163fn initialize_centers(colors: &[[f32; 3]], params: &KMeansParams) -> Vec<[f32; 3]> {
164 match params.init_method {
165 InitMethod::Random => initialize_random(colors, params.k),
166 InitMethod::KMeansPlusPlus => initialize_kmeans_plus_plus(colors, params.k),
167 InitMethod::Frequency => initialize_frequency(colors, params.k),
168 }
169}
170
171#[allow(dead_code)]
173fn initialize_random(colors: &[[f32; 3]], k: usize) -> Vec<[f32; 3]> {
174 let mut rng = scirs2_core::random::rng();
175 let mut centers = Vec::new();
176 let mut indices: Vec<_> = (0..colors.len()).collect();
177 indices.shuffle(&mut rng);
178
179 centers.extend(indices.iter().take(k.min(colors.len())).map(|&i| colors[i]));
180
181 centers
182}
183
184#[allow(dead_code)]
186fn initialize_kmeans_plus_plus(colors: &[[f32; 3]], k: usize) -> Vec<[f32; 3]> {
187 let mut rng = scirs2_core::random::rng();
188 let mut centers = Vec::new();
189
190 centers.push(colors[rng.random_range(0..colors.len())]);
192
193 for _ in 1..k {
195 let mut distances = vec![0.0f32; colors.len()];
196 let mut sum = 0.0f32;
197
198 for (i, color) in colors.iter().enumerate() {
200 let mut min_dist = f32::INFINITY;
201 for center in ¢ers {
202 let dist = color_distance(color, center);
203 if dist < min_dist {
204 min_dist = dist;
205 }
206 }
207 distances[i] = min_dist * min_dist; sum += distances[i];
209 }
210
211 let mut threshold = rng.random::<f32>() * sum;
213 let mut chosen = 0;
214
215 for (i, &dist) in distances.iter().enumerate() {
216 threshold -= dist;
217 if threshold <= 0.0 {
218 chosen = i;
219 break;
220 }
221 }
222
223 centers.push(colors[chosen]);
224 }
225
226 centers
227}
228
229#[allow(dead_code)]
231fn initialize_frequency(colors: &[[f32; 3]], k: usize) -> Vec<[f32; 3]> {
232 let mut color_counts = HashMap::new();
234
235 for color in colors {
236 let key = (color[0] as u8, color[1] as u8, color[2] as u8);
237 *color_counts.entry(key).or_insert(0) += 1;
238 }
239
240 let mut sorted: Vec<_> = color_counts.into_iter().collect();
242 sorted.sort_by_key(|(_, count)| -count);
243
244 let mut centers = Vec::new();
246 for ((r, g, b), _) in sorted.iter().take(k.min(sorted.len())) {
247 centers.push([*r as f32, *g as f32, *b as f32]);
248 }
249
250 let mut rng = scirs2_core::random::rng();
252 while centers.len() < k {
253 centers.push(colors[rng.random_range(0..colors.len())]);
254 }
255
256 centers
257}
258
259#[allow(dead_code)]
261fn update_centers(colors: &[[f32; 3]], assignments: &[usize], k: usize) -> Vec<[f32; 3]> {
262 let mut new_centers = vec![[0.0, 0.0, 0.0]; k];
263 let mut counts = vec![0; k];
264
265 for (color, &cluster) in colors.iter().zip(assignments.iter()) {
267 new_centers[cluster][0] += color[0];
268 new_centers[cluster][1] += color[1];
269 new_centers[cluster][2] += color[2];
270 counts[cluster] += 1;
271 }
272
273 new_centers
275 .iter_mut()
276 .zip(counts.iter())
277 .filter(|(_, &count)| count > 0)
278 .for_each(|(center, &count)| {
279 let count_f32 = count as f32;
280 center[0] /= count_f32;
281 center[1] /= count_f32;
282 center[2] /= count_f32;
283 });
284
285 new_centers
286}
287
288#[allow(dead_code)]
290fn color_distance(a: &[f32; 3], b: &[f32; 3]) -> f32 {
291 let dr = a[0] - b[0];
292 let dg = a[1] - b[1];
293 let db = a[2] - b[2];
294 dr * dr + dg * dg + db * db
295}
296
297#[allow(dead_code)]
308pub fn median_cut_quantize(img: &DynamicImage, ncolors: usize) -> Result<DynamicImage> {
309 let rgb = img.to_rgb8();
310 let (width, height) = rgb.dimensions();
311
312 let mut colors = Vec::new();
314 for pixel in rgb.pixels() {
315 colors.push([pixel[0], pixel[1], pixel[2]]);
316 }
317
318 let mut boxes = vec![ColorBox::new(&colors)];
320
321 while boxes.len() < ncolors && boxes.iter().any(|b| b.can_split()) {
323 let mut max_volume = 0;
325 let mut split_idx = 0;
326
327 for (i, box_) in boxes.iter().enumerate() {
328 if box_.can_split() && box_.volume() > max_volume {
329 max_volume = box_.volume();
330 split_idx = i;
331 }
332 }
333
334 let box_to_split = boxes.remove(split_idx);
336 let (box1, box2) = box_to_split.split();
337 boxes.push(box1);
338 boxes.push(box2);
339 }
340
341 let palette: Vec<[u8; 3]> = boxes.iter().map(|b| b.average()).collect();
343
344 let mut result = RgbImage::new(width, height);
346
347 for (pixel, result_pixel) in rgb.pixels().zip(result.pixels_mut()) {
348 let color = [pixel[0], pixel[1], pixel[2]];
349 let mut min_dist = u32::MAX;
350 let mut best_color = palette[0];
351
352 for &palette_color in &palette {
353 let dist = color_distance_u8(&color, &palette_color);
354 if dist < min_dist {
355 min_dist = dist;
356 best_color = palette_color;
357 }
358 }
359
360 *result_pixel = Rgb(best_color);
361 }
362
363 Ok(DynamicImage::ImageRgb8(result))
364}
365
366struct ColorBox {
368 colors: Vec<[u8; 3]>,
369 min: [u8; 3],
370 max: [u8; 3],
371}
372
373impl ColorBox {
374 fn new(colors: &[[u8; 3]]) -> Self {
375 let mut min = [255u8; 3];
376 let mut max = [0u8; 3];
377
378 for color in colors {
379 for i in 0..3 {
380 min[i] = min[i].min(color[i]);
381 max[i] = max[i].max(color[i]);
382 }
383 }
384
385 Self {
386 colors: colors.to_vec(),
387 min,
388 max,
389 }
390 }
391
392 fn can_split(&self) -> bool {
393 self.colors.len() > 1
394 }
395
396 fn volume(&self) -> u32 {
397 let r = (self.max[0] - self.min[0]) as u32;
398 let g = (self.max[1] - self.min[1]) as u32;
399 let b = (self.max[2] - self.min[2]) as u32;
400 r * g * b
401 }
402
403 fn split(mut self) -> (ColorBox, ColorBox) {
404 let r_range = self.max[0] - self.min[0];
406 let g_range = self.max[1] - self.min[1];
407 let b_range = self.max[2] - self.min[2];
408
409 let axis = if r_range >= g_range && r_range >= b_range {
410 0
411 } else if g_range >= b_range {
412 1
413 } else {
414 2
415 };
416
417 self.colors.sort_by_key(|c| c[axis]);
419
420 let mid = self.colors.len() / 2;
422 let colors2 = self.colors.split_off(mid);
423
424 (ColorBox::new(&self.colors), ColorBox::new(&colors2))
425 }
426
427 fn average(&self) -> [u8; 3] {
428 let mut sum = [0u32; 3];
429
430 for color in &self.colors {
431 sum[0] += color[0] as u32;
432 sum[1] += color[1] as u32;
433 sum[2] += color[2] as u32;
434 }
435
436 let n = self.colors.len() as u32;
437 [(sum[0] / n) as u8, (sum[1] / n) as u8, (sum[2] / n) as u8]
438 }
439}
440
441#[allow(dead_code)]
443fn color_distance_u8(a: &[u8; 3], b: &[u8; 3]) -> u32 {
444 let dr = a[0] as i32 - b[0] as i32;
445 let dg = a[1] as i32 - b[1] as i32;
446 let db = a[2] as i32 - b[2] as i32;
447 (dr * dr + dg * dg + db * db) as u32
448}
449
450#[cfg(test)]
451mod tests {
452 use super::*;
453
454 #[test]
455 fn test_kmeans_quantize() {
456 let img = DynamicImage::new_rgb8(20, 20);
457 let params = KMeansParams {
458 k: 4,
459 max_iterations: 10,
460 ..Default::default()
461 };
462
463 let result = kmeans_quantize(&img, ¶ms);
464 assert!(result.is_ok());
465
466 let quantized = result.expect("Operation failed");
467 assert_eq!(quantized.width(), 20);
468 assert_eq!(quantized.height(), 20);
469 }
470
471 #[test]
472 fn test_median_cut() {
473 let img = DynamicImage::new_rgb8(20, 20);
474 let result = median_cut_quantize(&img, 8);
475 assert!(result.is_ok());
476 }
477
478 #[test]
479 fn test_color_box() {
480 let colors = vec![[0, 0, 0], [255, 255, 255], [128, 128, 128]];
481
482 let box_ = ColorBox::new(&colors);
483 assert_eq!(box_.min, [0, 0, 0]);
484 assert_eq!(box_.max, [255, 255, 255]);
485 assert!(box_.can_split());
486 }
487}