1use crate::error::{Error, Result};
35use crate::geometry::{Mat3, Rigid3};
36
37pub const DESCRIPTOR_DIM: usize = 128;
39
40#[derive(Clone, Copy, Debug, PartialEq, Default)]
45pub struct Keypoint {
46 pub x: f32,
48 pub y: f32,
50 pub a11: f32,
52 pub a12: f32,
54 pub a21: f32,
56 pub a22: f32,
58}
59
60impl Keypoint {
61 pub fn new(x: f32, y: f32) -> Self {
63 Self { x, y, a11: 1.0, a12: 0.0, a21: 0.0, a22: 1.0 }
64 }
65 pub fn from_scale_orientation(x: f32, y: f32, scale: f32, orientation: f32) -> Self {
67 let (s, c) = orientation.sin_cos();
68 Self { x, y, a11: scale * c, a12: -scale * s, a21: scale * s, a22: scale * c }
69 }
70 pub fn from_row(row: &[f32]) -> Result<Self> {
72 match row.len() {
73 2 => Ok(Self::new(row[0], row[1])),
74 4 => Ok(Self::from_scale_orientation(row[0], row[1], row[2], row[3])),
75 6 => Ok(Self { x: row[0], y: row[1], a11: row[2], a12: row[3], a21: row[4], a22: row[5] }),
76 n => Err(Error::Format(format!("키포인트 열 수 {n} 는 지원하지 않음"))),
77 }
78 }
79 pub fn to_row(&self) -> [f32; 6] {
81 [self.x, self.y, self.a11, self.a12, self.a21, self.a22]
82 }
83 pub fn scale_x(&self) -> f32 {
85 (self.a11 * self.a11 + self.a21 * self.a21).sqrt()
86 }
87 pub fn scale_y(&self) -> f32 {
89 (self.a12 * self.a12 + self.a22 * self.a22).sqrt()
90 }
91 pub fn scale(&self) -> f32 {
93 0.5 * (self.scale_x() + self.scale_y())
94 }
95 pub fn orientation(&self) -> f32 {
97 self.a21.atan2(self.a11)
98 }
99 pub fn shear(&self) -> f32 {
101 (-self.a12).atan2(self.a22) - self.orientation()
102 }
103 pub fn rescale(&mut self, sx: f32, sy: f32) {
105 self.x *= sx;
106 self.y *= sy;
107 self.a11 *= sx;
108 self.a21 *= sx;
109 self.a12 *= sy;
110 self.a22 *= sy;
111 }
112}
113
114#[derive(Clone, Debug, PartialEq, Default)]
116pub struct Descriptors {
117 data: Vec<u8>,
118}
119
120impl Descriptors {
121 pub fn new() -> Self {
123 Self::default()
124 }
125 pub fn with_capacity(n: usize) -> Self {
127 Self { data: Vec::with_capacity(n * DESCRIPTOR_DIM) }
128 }
129 pub fn from_vec(data: Vec<u8>) -> Result<Self> {
131 if !data.len().is_multiple_of(DESCRIPTOR_DIM) {
132 return Err(Error::Format(format!("기술자 바이트 수 {} 가 128 의 배수가 아님", data.len())));
133 }
134 Ok(Self { data })
135 }
136 pub fn len(&self) -> usize {
138 self.data.len() / DESCRIPTOR_DIM
139 }
140 pub fn is_empty(&self) -> bool {
142 self.data.is_empty()
143 }
144 pub fn row(&self, i: usize) -> &[u8] {
146 &self.data[i * DESCRIPTOR_DIM..(i + 1) * DESCRIPTOR_DIM]
147 }
148 pub fn row_mut(&mut self, i: usize) -> &mut [u8] {
150 &mut self.data[i * DESCRIPTOR_DIM..(i + 1) * DESCRIPTOR_DIM]
151 }
152 pub fn push(&mut self, d: &[u8; DESCRIPTOR_DIM]) {
154 self.data.extend_from_slice(d);
155 }
156 pub fn as_slice(&self) -> &[u8] {
158 &self.data
159 }
160 pub fn into_vec(self) -> Vec<u8> {
162 self.data
163 }
164 pub fn truncate(&mut self, n: usize) {
166 self.data.truncate(n * DESCRIPTOR_DIM);
167 }
168}
169
170#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
172pub struct FeatureMatch {
173 pub idx1: u32,
175 pub idx2: u32,
177}
178
179impl FeatureMatch {
180 pub fn new(idx1: u32, idx2: u32) -> Self {
182 Self { idx1, idx2 }
183 }
184 pub fn swapped(&self) -> Self {
186 Self { idx1: self.idx2, idx2: self.idx1 }
187 }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
192pub enum TwoViewGeometryConfig {
193 #[default]
194 Undefined = 0,
196 Degenerate = 1,
198 Calibrated = 2,
200 Uncalibrated = 3,
202 Planar = 4,
204 Panoramic = 5,
206 PlanarOrRotation = 6,
208 Watermark = 7,
210 Multiple = 8,
212 CalibratedRig = 9,
214}
215
216impl TwoViewGeometryConfig {
217 pub fn from_i32(v: i32) -> Option<Self> {
219 use TwoViewGeometryConfig::*;
220 Some(match v {
221 0 => Undefined,
222 1 => Degenerate,
223 2 => Calibrated,
224 3 => Uncalibrated,
225 4 => Planar,
226 5 => Panoramic,
227 6 => PlanarOrRotation,
228 7 => Watermark,
229 8 => Multiple,
230 9 => CalibratedRig,
231 _ => return None,
232 })
233 }
234 pub fn as_i32(self) -> i32 {
236 self as i32
237 }
238}
239
240#[derive(Clone, Debug, PartialEq, Default)]
242pub struct TwoViewGeometry {
243 pub config: TwoViewGeometryConfig,
245 pub e: Option<Mat3>,
247 pub f: Option<Mat3>,
249 pub h: Option<Mat3>,
251 pub cam1_to_cam2: Option<Rigid3>,
253 pub inlier_matches: Vec<FeatureMatch>,
255 pub tri_angle: Option<f64>,
257}
258
259impl TwoViewGeometry {
260 pub fn inverted(&self) -> Self {
262 Self {
263 config: self.config,
264 e: self.e.map(|m| m.transpose()),
265 f: self.f.map(|m| m.transpose()),
266 h: self.h.and_then(|m| m.try_inverse()),
267 cam1_to_cam2: self.cam1_to_cam2.map(|p| p.inverse()),
268 inlier_matches: self.inlier_matches.iter().map(|m| m.swapped()).collect(),
269 tri_angle: self.tri_angle,
270 }
271 }
272 pub fn invert(&mut self) {
274 *self = self.inverted();
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn keypoint_shape() {
284 let k = Keypoint::from_scale_orientation(1600.5, 1200.25, 2.0, 0.3);
285 assert!((k.scale() - 2.0).abs() < 1e-6);
286 assert!((k.orientation() - 0.3).abs() < 1e-6);
287 assert!(k.shear().abs() < 1e-6);
288 let mut k2 = k;
289 k2.rescale(4000.0 / 3200.0, 3000.0 / 2400.0);
290 assert!((k2.x - 2000.625).abs() < 1e-5 * 2000.0);
291 assert!((k2.y - 1500.3125).abs() < 1e-5 * 1500.0);
292 assert!((k2.scale() - 2.5).abs() < 1e-5);
293 assert!((k2.orientation() - 0.3).abs() < 1e-5);
294 assert_eq!(Keypoint::from_row(&k.to_row()).unwrap(), k);
295 assert!(Keypoint::from_row(&[1.0; 3]).is_err());
296 }
297
298 #[test]
299 fn tvg_invert() {
300 let h = Mat3::new(1.0, 0.1, 5.0, 0.0, 1.2, -3.0, 0.001, 0.0, 1.0);
301 let g = TwoViewGeometry {
302 config: TwoViewGeometryConfig::Calibrated,
303 h: Some(h),
304 f: Some(h),
305 inlier_matches: vec![FeatureMatch::new(1, 2)],
306 ..Default::default()
307 };
308 let gi = g.inverted();
309 assert!((gi.h.unwrap() * h - Mat3::identity()).norm() < 1e-12);
310 assert_eq!(gi.f.unwrap(), h.transpose());
311 assert_eq!(gi.inlier_matches[0], FeatureMatch::new(2, 1));
312 let gii = gi.inverted();
313 assert!((gii.h.unwrap() - h).norm() < 1e-12);
314 }
315}