Skip to main content

fpm_rs/diagnostics/
records.rs

1//! Run-scoped metadata attached to reusable metric values.
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5use crate::metrics::intensity::{IntensityComparisonMetrics, IntensityStats};
6
7/// Comparison metrics for one predicted and reference acquisition frame.
8#[derive(Clone, Debug)]
9pub struct FrameDiagnosticRecord {
10    /// Optional one-based iteration at which the frame was evaluated.
11    pub iteration: Option<usize>,
12    /// Zero-based acquisition-frame index.
13    pub frame_index: usize,
14    /// Individual source index, or frame-associated illumination index for this record.
15    pub illumination_index: usize,
16    /// Intensity-domain comparison metrics and summary statistics.
17    pub metrics: IntensityComparisonMetrics,
18}
19
20#[derive(Serialize)]
21struct FrameDiagnosticRecordRef {
22    #[serde(skip_serializing_if = "Option::is_none")]
23    iteration: Option<usize>,
24    frame_index: usize,
25    illumination_index: usize,
26    reference_sum: f64,
27    estimate_sum: f64,
28    residual_l1: f64,
29    residual_l2: f64,
30    residual_mean: f64,
31    residual_std: f64,
32    residual_max_abs: f64,
33    normalized_l2: f64,
34    saturated_pixels: Option<usize>,
35}
36
37#[derive(Deserialize)]
38struct FrameDiagnosticRecordOwned {
39    #[serde(default)]
40    iteration: Option<usize>,
41    frame_index: usize,
42    illumination_index: usize,
43    reference_sum: f64,
44    estimate_sum: f64,
45    residual_l1: f64,
46    residual_l2: f64,
47    residual_mean: f64,
48    residual_std: f64,
49    residual_max_abs: f64,
50    normalized_l2: f64,
51    saturated_pixels: Option<usize>,
52}
53
54impl Serialize for FrameDiagnosticRecord {
55    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
56    where
57        S: Serializer,
58    {
59        FrameDiagnosticRecordRef {
60            iteration: self.iteration,
61            frame_index: self.frame_index,
62            illumination_index: self.illumination_index,
63            reference_sum: self.metrics.reference_sum,
64            estimate_sum: self.metrics.estimate_sum,
65            residual_l1: self.metrics.residual_l1,
66            residual_l2: self.metrics.residual_l2,
67            residual_mean: self.metrics.residual_mean,
68            residual_std: self.metrics.residual_std,
69            residual_max_abs: self.metrics.residual_max_abs,
70            normalized_l2: self.metrics.normalized_l2,
71            saturated_pixels: self.metrics.saturated_pixels,
72        }
73        .serialize(serializer)
74    }
75}
76
77impl<'de> Deserialize<'de> for FrameDiagnosticRecord {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: Deserializer<'de>,
81    {
82        let record = FrameDiagnosticRecordOwned::deserialize(deserializer)?;
83        Ok(Self {
84            iteration: record.iteration,
85            frame_index: record.frame_index,
86            illumination_index: record.illumination_index,
87            metrics: IntensityComparisonMetrics {
88                reference_sum: record.reference_sum,
89                estimate_sum: record.estimate_sum,
90                residual_l1: record.residual_l1,
91                residual_l2: record.residual_l2,
92                residual_mean: record.residual_mean,
93                residual_std: record.residual_std,
94                residual_max_abs: record.residual_max_abs,
95                normalized_l2: record.normalized_l2,
96                saturated_pixels: record.saturated_pixels,
97            },
98        })
99    }
100}
101
102/// Raw measured-intensity statistics for one acquisition frame.
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct RawFrameStatisticsRecord {
105    /// Zero-based acquisition-frame index.
106    pub frame_index: usize,
107    #[serde(flatten)]
108    /// Descriptive intensity statistics for unmasked pixels.
109    pub metrics: IntensityStats,
110}