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