Skip to main content

flydra_mvg/
flydra_xml_support.rs

1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use nalgebra as na;
5use nalgebra::RealField;
6use nalgebra::core::OMatrix;
7use nalgebra::core::dimension::{U3, U4};
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Default, Deserialize, PartialEq)]
12#[serde(rename = "multi_camera_reconstructor", deny_unknown_fields)]
13pub struct FlydraReconstructor<R: RealField + serde::Serialize> {
14    #[serde(rename = "single_camera_calibration")]
15    pub cameras: Vec<SingleCameraCalibration<R>>,
16    /// This is ignored when reading and not written.
17    #[serde(default)]
18    pub minimum_eccentricity: R,
19    #[serde(default)]
20    pub water: Option<R>,
21    #[serde(default)]
22    pub comment: Option<String>,
23}
24
25#[derive(Debug, Serialize, Deserialize, PartialEq)]
26#[serde(deny_unknown_fields, rename = "single_camera_calibration")]
27pub struct SingleCameraCalibration<R: RealField + serde::Serialize> {
28    // changes to this should update BraidMetadataSchemaTag
29    pub cam_id: String,
30    #[serde(
31        serialize_with = "serialize_matrix",
32        deserialize_with = "deserialize_matrix"
33    )]
34    pub calibration_matrix: OMatrix<R, U3, U4>,
35    #[serde(
36        serialize_with = "serialize_two_ints",
37        deserialize_with = "deserialize_two_ints"
38    )]
39    pub resolution: (usize, usize),
40    /// Only values of None or Some(1.0) are supported.
41    #[serde(default, skip_serializing)]
42    pub scale_factor: Option<R>,
43    pub non_linear_parameters: FlydraDistortionModel<R>,
44}
45
46#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
47#[serde(deny_unknown_fields)]
48pub struct FlydraDistortionModel<R: RealField + serde::Serialize> {
49    pub fc1: R,
50    pub fc2: R,
51    pub cc1: R,
52    pub cc2: R,
53    pub k1: R,
54    pub k2: R,
55    pub p1: R,
56    pub p2: R,
57    #[serde(default, skip_serializing_if = "is_zero")]
58    pub k3: R,
59    pub alpha_c: R,
60    #[serde(default, skip_serializing)]
61    pub fc1p: Option<R>,
62    #[serde(default, skip_serializing)]
63    pub fc2p: Option<R>,
64    #[serde(default, skip_serializing)]
65    pub cc1p: Option<R>,
66    #[serde(default, skip_serializing)]
67    pub cc2p: Option<R>,
68}
69
70impl<R> FlydraDistortionModel<R>
71where
72    R: RealField + serde::Serialize,
73{
74    /// create a FlydraDistortionModel with only linear parameters from the given
75    /// Pmat, and all non-linear parameters set to 0.
76    pub fn linear(pmat: &OMatrix<R, U3, U4>) -> Self {
77        Self {
78            fc1: pmat[(0, 0)].clone(),
79            fc2: pmat[(1, 1)].clone(),
80            cc1: pmat[(0, 2)].clone(),
81            cc2: pmat[(1, 2)].clone(),
82            alpha_c: na::convert(0.0),
83            k1: na::convert(0.0),
84            k2: na::convert(0.0),
85            p1: na::convert(0.0),
86            p2: na::convert(0.0),
87            k3: na::convert(0.0),
88            fc1p: None,
89            fc2p: None,
90            cc1p: None,
91            cc2p: None,
92        }
93    }
94}
95
96fn is_zero<R: RealField>(val: &R) -> bool {
97    let zero: R = na::convert(0.0);
98    val == &zero
99}
100
101/// Adds indentation to all lines except the first.
102///
103/// This is a hack to make the XML output look nicer.
104fn extra_indent(s: String, prefix: &str) -> String {
105    let mut v = Vec::new();
106    for (i, line) in s.lines().enumerate() {
107        if i == 0 {
108            v.push(line.to_string());
109        } else {
110            v.push(format!("{prefix}{line}"));
111        }
112    }
113    v.join("\n")
114}
115
116pub(crate) fn serialize_recon<R>(
117    recon: &FlydraReconstructor<R>,
118) -> std::result::Result<String, serde_xml_rs::Error>
119where
120    R: RealField + Serialize,
121{
122    // this is a total hack. TODO make it not a hack
123
124    // changes to this should update BraidMetadataSchemaTag
125
126    let prefix = "    ";
127    let s = serde_xml_rs::SerdeXml::new().emitter(
128        xml::EmitterConfig::new()
129            .write_document_declaration(false)
130            .perform_indent(true),
131    );
132    let v: Result<Vec<String>, serde_xml_rs::Error> = recon
133        .cameras
134        .iter()
135        .map(|item| match s.clone().to_string(&item) {
136            Ok(st) => Ok(extra_indent(st, prefix)),
137            Err(e) => Err(e),
138        })
139        .collect();
140    let v: Vec<String> = v?;
141    let v_indented: Vec<String> = v.iter().map(|s| format!("{prefix}{s}")).collect();
142    let cams_buf = v_indented.join("\n");
143
144    let mut v = vec!["<multi_camera_reconstructor>".to_string()];
145    v.push(cams_buf);
146    if let Some(ref w) = recon.water {
147        v.push(format!("    <water>{w}</water>"));
148    }
149    if let Some(ref c) = recon.comment {
150        v.push(format!("    <comment>{c}</comment>"));
151    }
152    v.push("</multi_camera_reconstructor>\n".to_string());
153    let buf = v.join("\n");
154    Ok(buf)
155}
156
157#[rustfmt::skip]
158fn serialize_matrix<S, R>(m: &OMatrix<R,U3,U4>, serializer: S) -> Result<S::Ok, S::Error>
159    where S: serde::Serializer,
160         R: RealField + Serialize,
161{
162    let buf = format!("{} {} {} {}; {} {} {} {}; {} {} {} {}",
163        m[(0,0)], m[(0,1)], m[(0,2)], m[(0,3)],
164        m[(1,0)], m[(1,1)], m[(1,2)], m[(1,3)],
165        m[(2,0)], m[(2,1)], m[(2,2)], m[(2,3)]);
166    serializer.serialize_str(&buf)
167}
168
169fn deserialize_matrix<'de, D, R>(deserializer: D) -> Result<OMatrix<R, U3, U4>, D::Error>
170where
171    D: serde::Deserializer<'de>,
172    R: RealField,
173{
174    use std::str::FromStr;
175
176    let s = String::deserialize(deserializer)?;
177    let rows: Vec<&str> = s.split(';').collect();
178    if rows.len() != 3 {
179        return Err(serde::de::Error::custom("expected exactly 3 rows"));
180    }
181    let mut elements: Vec<R> = Vec::new();
182    for row in rows.iter() {
183        let cols: Vec<&str> = row.split_whitespace().collect();
184        if cols.len() != 4 {
185            return Err(serde::de::Error::custom("expected exactly 4 columns"));
186        }
187        for col in cols.iter() {
188            let element = f64::from_str(col).map_err(serde::de::Error::custom)?;
189            elements.push(na::convert(element));
190        }
191    }
192    Ok(OMatrix::<R, U3, U4>::from_row_slice(elements.as_slice()))
193}
194
195fn serialize_two_ints<S>(two_ints: &(usize, usize), serializer: S) -> Result<S::Ok, S::Error>
196where
197    S: serde::Serializer,
198{
199    let buf = format!("{} {}", two_ints.0, two_ints.1);
200    serializer.serialize_str(&buf)
201}
202
203fn deserialize_two_ints<'de, D>(deserializer: D) -> Result<(usize, usize), D::Error>
204where
205    D: serde::Deserializer<'de>,
206{
207    use std::str::FromStr;
208
209    let s = String::deserialize(deserializer)?;
210    let nums: Vec<&str> = s.split(' ').collect();
211    if nums.len() != 2 {
212        return Err(serde::de::Error::custom("expected exactly 2 numbers"));
213    }
214    Ok((
215        usize::from_str(nums[0]).map_err(serde::de::Error::custom)?,
216        usize::from_str(nums[1]).map_err(serde::de::Error::custom)?,
217    ))
218}