1use ndarray::ArrayView2;
4use num_complex::Complex64;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 Result,
9 array_layout::checked_len_2d,
10 measurements::MeasurementRead,
11 metrics::{
12 complex_field::{ComplexFieldComparisonMetrics, compare_complex_fields_masked},
13 intensity::{IntensityComparisonMetrics, compare_intensity_u8_masked},
14 model::{
15 FrameGainComparisonMetrics, IlluminationPositionMetrics, PupilComparisonMetrics,
16 compare_frame_gains, compare_illumination_positions, compare_pupils,
17 },
18 },
19 model::{ForwardModel, FourierOffset, ImagePlaneModel},
20 reconstruction::{ReconstructionProblem, ReconstructionResult},
21};
22
23#[derive(Clone, Debug, Serialize, Deserialize)]
25pub struct FrameIntensityEvaluation {
26 pub per_frame: Vec<IntensityComparisonMetrics>,
28}
29
30#[derive(Clone, Debug, Serialize, Deserialize)]
32pub struct ReconstructionEvaluation {
33 pub object: ComplexFieldComparisonMetrics,
35 pub pupil: Option<PupilComparisonMetrics>,
37 pub illumination: Option<IlluminationPositionMetrics>,
39 pub frame_gains: Option<FrameGainComparisonMetrics>,
41 pub intensity: Option<FrameIntensityEvaluation>,
43}
44
45pub fn evaluate_reconstruction(
49 result: &ReconstructionResult,
50 reference_object: ArrayView2<'_, Complex64>,
51 reference_model: Option<&ImagePlaneModel>,
52 valid_object_mask: Option<ArrayView2<'_, u8>>,
53) -> Result<ReconstructionEvaluation> {
54 let object =
55 compare_complex_fields_masked(reference_object, result.object.view(), valid_object_mask)?;
56 let (pupil, illumination, frame_gains) = if let Some(reference_model) = reference_model {
57 let pupil = Some(compare_pupils(
58 reference_model.pupil.values(),
59 result.recovered_pupil.values(),
60 reference_model.pupil.support(),
61 )?);
62 let illumination = None;
63 let frame_gains = result
64 .recovered_frame_gains
65 .as_ref()
66 .map(|candidate| {
67 let reference = reference_model.frame_gains.as_deref().unwrap_or(&[]);
68 if reference.is_empty() {
69 compare_frame_gains(&vec![1.0; reference_model.frame_count()], candidate)
70 } else {
71 compare_frame_gains(reference, candidate)
72 }
73 })
74 .transpose()?;
75 (pupil, illumination, frame_gains)
76 } else {
77 (None, None, None)
78 };
79 Ok(ReconstructionEvaluation {
80 object,
81 pupil,
82 illumination,
83 frame_gains,
84 intensity: None,
85 })
86}
87
88pub fn evaluate_reconstruction_with_problem<M: MeasurementRead>(
90 result: &ReconstructionResult,
91 problem: &ReconstructionProblem<M>,
92 reference_object: ArrayView2<'_, Complex64>,
93 reference_model: Option<&ImagePlaneModel>,
94 valid_object_mask: Option<ArrayView2<'_, u8>>,
95) -> Result<ReconstructionEvaluation> {
96 let mut evaluation =
97 evaluate_reconstruction(result, reference_object, reference_model, valid_object_mask)?;
98 problem.validate()?;
99 let intensity = evaluate_frame_intensity(result, problem)?;
100 if let Some(reference_model) = reference_model {
101 evaluation.illumination = Some(compare_illumination_positions(
102 reference_model,
103 &problem.model,
104 result.calibrated_illumination.as_deref(),
105 )?);
106 }
107 evaluation.intensity = Some(intensity);
108 Ok(evaluation)
109}
110
111pub fn evaluate_frame_intensity<M: MeasurementRead>(
113 result: &ReconstructionResult,
114 problem: &ReconstructionProblem<M>,
115) -> Result<FrameIntensityEvaluation> {
116 problem.validate()?;
117 let model = model_with_result_calibration(problem, result)?;
118 let forward = ForwardModel::new(&model)?;
119 let mut workspace = forward.workspace()?;
120 let mut candidate = vec![0.0; checked_len_2d(model.image_shape)?];
121 let mut per_frame = Vec::with_capacity(model.frame_count());
122 for frame in 0..model.frame_count() {
123 if problem.measurements.frame_weight(frame)? == 0.0 {
124 per_frame.push(compare_intensity_u8_masked(
125 &candidate, &candidate, None, None,
126 )?);
127 continue;
128 }
129 forward.forward_intensity_into(
130 result.object_spectrum.view(),
131 &result.recovered_pupil,
132 frame,
133 &mut workspace,
134 &mut candidate,
135 )?;
136 let reference = problem.measurements.frame(frame)?;
137 per_frame.push(compare_intensity_u8_masked(
138 &reference,
139 &candidate,
140 problem.measurements.frame_mask(frame)?,
141 None,
142 )?);
143 }
144 Ok(FrameIntensityEvaluation { per_frame })
145}
146
147fn model_with_result_calibration<M: MeasurementRead>(
148 problem: &ReconstructionProblem<M>,
149 result: &ReconstructionResult,
150) -> Result<ImagePlaneModel> {
151 let mut model = problem.model.clone();
152 model.frame_gains = result.recovered_frame_gains.clone();
153 model.background = result.recovered_background.clone();
154 if let Some(corrections) = &result.calibrated_illumination {
155 model.subpixel_offsets = Some(
156 corrections
157 .iter()
158 .enumerate()
159 .map(|(source, &(row, column))| {
160 let base = problem.model.source_offset(source)?;
161 Ok(FourierOffset::new(base.row + row, base.column + column))
162 })
163 .collect::<Result<Vec<_>>>()?,
164 );
165 }
166 model.validate()?;
167 Ok(model)
168}