1use std::cmp::Ordering;
5use std::ops::Deref;
6
7use num::{FromPrimitive, ToPrimitive};
8
9use crate::im::PineappleViewBuffer;
10
11#[inline]
12pub fn intensity_min<T>(pixels: &[T], channels: usize) -> Vec<f32>
13where
14 T: ToPrimitive,
15{
16 let mut min = vec![f32::INFINITY; channels];
17 for pixel in pixels.chunks_exact(channels) {
18 for (i, v) in pixel.iter().enumerate() {
19 let v = v.to_f32().unwrap();
20 if v < min[i] && v > 0. {
21 min[i] = v;
22 }
23 }
24 }
25
26 for v in min.iter_mut() {
27 if *v == f32::INFINITY {
28 *v = 0.0;
29 }
30 }
31
32 min
33}
34
35#[inline]
36pub fn intensity_max<T>(pixels: &[T], channels: usize) -> Vec<f32>
37where
38 T: ToPrimitive,
39{
40 let mut max = vec![f32::NEG_INFINITY; channels];
41 for pixel in pixels.chunks_exact(channels) {
42 for (i, v) in pixel.iter().enumerate() {
43 let v = v.to_f32().unwrap();
44 if v > max[i] && v > 0. {
45 max[i] = v;
46 }
47 }
48 }
49
50 for v in max.iter_mut() {
51 if *v == f32::NEG_INFINITY {
52 *v = 0.0;
53 }
54 }
55
56 max
57}
58
59#[inline]
60pub fn intensity_mean<T>(pixels: &[T], channels: usize) -> Vec<f32>
61where
62 T: ToPrimitive,
63{
64 let mut n = vec![0; channels];
65 let mut mean = vec![0.0; channels];
66 for pixel in pixels.chunks_exact(channels) {
67 for (i, v) in pixel.iter().enumerate() {
68 let v = v.to_f32().unwrap();
69 if v > 0. {
70 n[i] += 1;
71 mean[i] += v;
72 }
73 }
74 }
75
76 for i in 0..channels {
77 if n[i] > 0 {
78 mean[i] *= 1.0 / n[i] as f32;
79 }
80 }
81
82 mean
83}
84
85#[inline]
86pub fn intensity_sum<T>(pixels: &[T], channels: usize) -> Vec<f32>
87where
88 T: ToPrimitive,
89{
90 let mut sum = vec![0.0; channels];
91 for pixel in pixels.chunks_exact(channels) {
92 for (i, v) in pixel.iter().enumerate() {
93 sum[i] += v.to_f32().unwrap();
94 }
95 }
96
97 sum
98}
99
100#[inline]
101pub fn intensity_std<T>(pixels: &[T], channels: usize) -> Vec<f32>
102where
103 T: ToPrimitive,
104{
105 let mut n = vec![0; channels];
106 let mean = intensity_mean(pixels, channels);
107
108 let mut std = vec![0.0; channels];
109 for pixel in pixels.chunks_exact(channels) {
110 for (i, v) in pixel.iter().enumerate() {
111 let v = v.to_f32().unwrap();
112 if v > 0. {
113 n[i] += 1;
114 std[i] += (v - mean[i]) * (v - mean[i]);
115 }
116 }
117 }
118
119 for i in 0..channels {
120 if n[i] > 0 {
121 std[i] = (std[i] * 1.0 / n[i] as f32).sqrt()
122 }
123 }
124
125 std
126}
127
128#[inline]
129pub fn intensity_median<T>(pixels: &[T]) -> f32
130where
131 T: Copy + Into<f32> + PartialOrd + ToPrimitive,
132{
133 let mut pixels: Vec<f32> = pixels
134 .to_vec()
135 .iter()
136 .map(|x| x.to_f32().unwrap())
137 .filter(|x| *x > 0.)
138 .collect();
139
140 pixels.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
141
142 let len = pixels.len();
143 let mid = len / 2;
144
145 if len.is_multiple_of(2) {
146 let left = pixels[mid - 1];
147 let right = pixels[mid];
148 (left + right) / 2.0
149 } else {
150 pixels[mid]
151 }
152}
153
154#[inline]
155pub fn intensity_mad<T>(pixels: &[T]) -> f32
156where
157 T: Copy + Into<f32> + PartialOrd + ToPrimitive,
158{
159 let median = intensity_median(pixels);
160 let mut mad = Vec::new();
161 for pixel in pixels.iter() {
162 let pixel = pixel.to_f32().unwrap();
163 if pixel > 0. {
164 mad.push((pixel - median).abs());
165 }
166 }
167
168 mad.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
169 let mid = mad.len() / 2;
170 if mad.len() % 2 == 0 {
171 (mad[mid] + mad[mid - 1]) / 2.0
172 } else {
173 mad[mid]
174 }
175}
176
177#[inline]
178#[allow(clippy::all)]
179pub fn descriptors<T>(pixels: &[T], channels: usize) -> Vec<f32>
180where
181 T: ToPrimitive,
182{
183 let mut n = vec![0; channels];
184
185 let mut results = vec![0.0; channels * 5 + 2];
190
191 for i in 0..channels {
192 results[i + 0 * channels] = f32::INFINITY;
193 results[i + 1 * channels] = f32::NEG_INFINITY;
194 }
195
196 let mut store: Vec<f32> = Vec::with_capacity(pixels.len());
197
198 for pixel in pixels.chunks_exact(channels) {
199 for (i, v) in pixel.iter().enumerate() {
200 let v = v.to_f32().unwrap();
201
202 if v > 0. {
203 n[i] += 1;
204
205 results[i + 0 * channels] = results[i + 0 * channels].min(v);
207
208 results[i + 1 * channels] = results[i + 1 * channels].max(v);
210
211 results[i + 2 * channels] += v;
213 }
214
215 store.push(v);
216 }
217 }
218
219 for v in results.iter_mut().take(channels * 2) {
220 if *v == f32::NEG_INFINITY || *v == f32::INFINITY {
221 *v = 0.
222 }
223 }
224
225 for i in 0..channels {
227 if n[i] > 0 {
228 results[i + 3 * channels] = results[i + 2 * channels] * 1.0 / n[i] as f32;
229 }
230 }
231
232 for pixel in store.chunks_exact(channels) {
234 for (i, &v) in pixel.iter().enumerate() {
235 results[i + 4 * channels] += (v - results[i + 3 * channels]).powi(2);
236 }
237 }
238
239 for i in 0..channels {
240 if n[i] > 0 {
241 results[i + 4 * channels] = (results[i + 4 * channels] * 1.0 / n[i] as f32).sqrt();
242 }
243 }
244
245 store.retain(|v| *v > 0.);
246
247 if store.is_empty() {
248 return results;
249 }
250
251 store.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
252
253 let n = store.len();
254 let mid = n / 2;
255 let len = results.len();
256
257 results[len - 2] = if n % 2 == 0 {
259 (store[mid - 1] + store[mid]) / 2.0
260 } else {
261 store[mid]
262 };
263
264 store
265 .iter_mut()
266 .for_each(|pixel| *pixel = (*pixel - results[len - 2]).abs());
267
268 store.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
269
270 results[len - 1] = if n % 2 == 0 {
272 (store[mid] + store[mid - 1]) / 2.0
273 } else {
274 store[mid]
275 };
276
277 results
278}
279
280#[inline]
281#[allow(clippy::all)]
282pub fn objects<T, Container>(object: &PineappleViewBuffer<T, Container>) -> Vec<f32>
283where
284 T: ToPrimitive + FromPrimitive,
285 Container: Deref<Target = [T]>,
286{
287 let c = object.channels();
288 let mut n = vec![0; c];
289
290 let mut results = vec![0.0; c * 5 + 2];
295
296 for i in 0..c {
297 results[i + 0 * c] = f32::INFINITY;
298 results[i + 1 * c] = f32::NEG_INFINITY;
299 }
300
301 let mut store: Vec<f32> = Vec::with_capacity(object.len());
302
303 for pixel in object.iter_pixels() {
304 for (i, v) in pixel.iter().enumerate() {
305 let v = v.to_f32().unwrap();
306
307 if v > 0. {
308 n[i] += 1;
309
310 results[i + 0 * c] = results[i + 0 * c].min(v);
312
313 results[i + 1 * c] = results[i + 1 * c].max(v);
315
316 results[i + 2 * c] += v;
318 }
319
320 store.push(v);
321 }
322 }
323
324 for v in results.iter_mut().take(c * 2) {
325 if *v == f32::NEG_INFINITY || *v == f32::INFINITY {
326 *v = 0.
327 }
328 }
329
330 for i in 0..c {
332 if n[i] > 0 {
333 results[i + 3 * c] = results[i + 2 * c] * 1.0 / n[i] as f32;
334 }
335 }
336
337 for pixel in store.chunks_exact(c) {
339 for (i, &v) in pixel.iter().enumerate() {
340 if v > 0. {
341 results[i + 4 * c] += (v - results[i + 3 * c]).powi(2);
342 }
343 }
344 }
345
346 for i in 0..c {
347 if n[i] > 0 {
348 results[i + 4 * c] = (results[i + 4 * c] * 1.0 / n[i] as f32).sqrt();
349 }
350 }
351
352 store.retain(|x| *x > 0.);
353
354 if store.is_empty() {
355 return results;
356 }
357
358 store.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
359
360 let n = store.len();
361 let mid = n / 2;
362 let len = results.len();
363
364 results[len - 2] = if n % 2 == 0 {
366 (store[mid - 1] + store[mid]) / 2.0
367 } else {
368 store[mid]
369 };
370
371 store
372 .iter_mut()
373 .for_each(|pixel| *pixel = (*pixel - results[len - 2]).abs());
374
375 store.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
376
377 results[len - 1] = if n % 2 == 0 {
379 (store[mid] + store[mid - 1]) / 2.0
380 } else {
381 store[mid]
382 };
383
384 results
385}
386
387#[cfg(test)]
388mod test {
389
390 use super::*;
391 use crate::im::PineappleBuffer;
392
393 fn test_pixels() -> (Vec<u8>, usize) {
394 let channels = 3;
395 let pixels: Vec<u8> = vec![0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 1, 5];
396 (pixels, channels)
397 }
398
399 fn test_object() -> PineappleBuffer<u8, Vec<u8>> {
400 let pixels: Vec<u8> = vec![0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 1, 5];
401 PineappleBuffer::new(2, 2, 3, pixels).unwrap()
402 }
403
404 #[test]
405 fn test_intensity_min() {
406 let (pixels, channels) = test_pixels();
407 let min = intensity_min(&pixels, channels);
408 assert_eq!(min, vec![0.0, 1.0, 2.0]);
409 }
410
411 #[test]
412 fn test_intensity_max() {
413 let (pixels, channels) = test_pixels();
414 let max = intensity_max(&pixels, channels);
415 assert_eq!(max, vec![0.0, 1.0, 5.0]);
416 }
417
418 #[test]
419 fn test_intensity_mean() {
420 let (pixels, channels) = test_pixels();
421 let mean = intensity_mean(&pixels, channels);
422 assert_eq!(mean, vec![0.0, 1.0, 3.5]);
423 }
424
425 #[test]
426 fn test_intensity_sum() {
427 let (pixels, channels) = test_pixels();
428 let sum = intensity_sum(&pixels, channels);
429 assert_eq!(sum, vec![0.0, 4.0, 14.0]);
430 }
431
432 #[test]
433 fn test_intensity_std() {
434 let (pixels, channels) = test_pixels();
435 let std = intensity_std(&pixels, channels);
436 assert_eq!(std, vec![0.0, 0.0, 1.118034]);
437 }
438
439 #[test]
440 fn test_intensity_median() {
441 let (pixels, _) = test_pixels();
442 let median = intensity_median(&pixels);
443 assert_eq!(median, 1.5);
444 }
445
446 #[test]
447 fn test_intensity_mad() {
448 let (pixels, _) = test_pixels();
449 let mad = intensity_mad(&pixels);
450 assert_eq!(mad, 0.5);
451 }
452
453 #[test]
454 fn test_descriptors() {
455 let (pixels, channels) = test_pixels();
456
457 let min = intensity_min(&pixels, channels);
458 let max = intensity_max(&pixels, channels);
459 let sum = intensity_sum(&pixels, channels);
460 let mean = intensity_mean(&pixels, channels);
461 let std = intensity_std(&pixels, channels);
462 let median = intensity_median(&pixels);
463 let mad = intensity_mad(&pixels);
464
465 let results = descriptors(&pixels, channels);
466
467 for i in 0..3 {
468 assert_eq!(min[i], results[i]);
469 assert_eq!(max[i], results[i + 3]);
470 assert_eq!(sum[i], results[i + 6]);
471 assert_eq!(mean[i], results[i + 9]);
472 assert_eq!(std[i], results[i + 12]);
473 }
474
475 assert_eq!(median, results[3 + 12]);
476 assert_eq!(mad, results[3 + 13]);
477 }
478
479 #[test]
480 fn test_objects() {
481 let (pixels, channels) = test_pixels();
482
483 let min = intensity_min(&pixels, channels);
484 let max = intensity_max(&pixels, channels);
485 let sum = intensity_sum(&pixels, channels);
486 let mean = intensity_mean(&pixels, channels);
487 let std = intensity_std(&pixels, channels);
488 let median = intensity_median(&pixels);
489 let mad = intensity_mad(&pixels);
490
491 let buffer = test_object();
492 let object = buffer.crop_view(0, 0, 2, 2);
493 let results = objects(&object);
494
495 for i in 0..3 {
496 assert_eq!(min[i], results[i]);
497 assert_eq!(max[i], results[i + 3]);
498 assert_eq!(sum[i], results[i + 6]);
499 assert_eq!(mean[i], results[i + 9]);
500 assert_eq!(std[i], results[i + 12]);
501 }
502
503 assert_eq!(median, results[3 + 12]);
504 assert_eq!(mad, results[3 + 13]);
505 }
506}