fpm_rs/reconstruction/
problem.rs1use crate::{Result, error::Error, measurements::MeasurementRead, model::ImagePlaneModel};
2
3#[derive(Clone, Debug)]
8pub struct ReconstructionProblem<M> {
9 pub measurements: M,
11 pub model: ImagePlaneModel,
13 pub name: Option<String>,
15}
16
17impl<M: MeasurementRead> ReconstructionProblem<M> {
18 pub fn new(measurements: M, model: ImagePlaneModel) -> Result<Self> {
20 let problem = Self {
21 measurements,
22 model,
23 name: None,
24 };
25 problem.validate()?;
26 Ok(problem)
27 }
28
29 pub fn named(mut self, name: impl Into<String>) -> Self {
31 self.name = Some(name.into());
32 self
33 }
34
35 pub fn validate(&self) -> Result<()> {
37 self.model.validate()?;
38 self.measurements.validate()?;
39 if self.measurements.frame_count() != self.model.frame_count() {
40 return Err(Error::InvalidModel(format!(
41 "{} measurement frames but {} model frames",
42 self.measurements.frame_count(),
43 self.model.frame_count()
44 )));
45 }
46 if self.measurements.image_shape() != self.model.image_shape {
47 return Err(Error::InvalidShape(format!(
48 "measurement shape {:?} differs from model image shape {:?}",
49 self.measurements.image_shape(),
50 self.model.image_shape
51 )));
52 }
53 let mut positive_weight_frames = 0;
54 for frame in 0..self.measurements.frame_count() {
55 if self.measurements.frame_weight(frame)? > 0.0 {
56 positive_weight_frames += 1;
57 }
58 if self.measurements.frame_weight(frame)? > 0.0
59 && self
60 .measurements
61 .frame_mask(frame)?
62 .is_some_and(|mask| mask.iter().all(|&value| value == 0))
63 {
64 return Err(Error::InvalidMeasurements(format!(
65 "positive-weight frame {frame} has no unmasked pixels"
66 )));
67 }
68 }
69 if positive_weight_frames == 0 {
70 return Err(Error::InvalidMeasurements(
71 "at least one frame must have positive weight".into(),
72 ));
73 }
74 Ok(())
75 }
76}