fpm_rs/metrics/intensity/
statistics.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, Serialize, Deserialize)]
6pub struct IntensityStatistics {
7 pub mean: f64,
8 pub std: f64,
9 pub min: f64,
10 pub max: f64,
11 pub sum: f64,
12 pub saturated_pixels: usize,
13 pub zero_pixels: usize,
14}
15
16pub fn intensity_statistics(
17 values: &[f64],
18 saturation_value: Option<f64>,
19) -> crate::Result<IntensityStatistics> {
20 if values.is_empty() {
21 return Err(crate::Error::InvalidShape(
22 "intensity statistics require non-empty values".into(),
23 ));
24 }
25 let mut sum = 0.0;
26 let mut sum_squares = 0.0;
27 let mut min = f64::INFINITY;
28 let mut max = f64::NEG_INFINITY;
29 let mut saturated_pixels = 0;
30 let mut zero_pixels = 0;
31 for &value in values {
32 if !value.is_finite() {
33 return Err(crate::Error::Numerical(
34 "intensity statistics contain a non-finite value".into(),
35 ));
36 }
37 sum += value;
38 sum_squares += value * value;
39 min = min.min(value);
40 max = max.max(value);
41 zero_pixels += usize::from(value == 0.0);
42 saturated_pixels += usize::from(saturation_value.is_some_and(|limit| value >= limit));
43 }
44 let count = values.len() as f64;
45 let mean = sum / count;
46 Ok(IntensityStatistics {
47 mean,
48 std: (sum_squares / count - mean * mean).max(0.0).sqrt(),
49 min,
50 max,
51 sum,
52 saturated_pixels,
53 zero_pixels,
54 })
55}