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