sciimg/camera/
cahvor.rs

1use crate::{
2    camera::cahv::*, camera::model::*, error, matrix::Matrix, max, min, util::vec_to_str,
3    vector::Vector,
4};
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Serialize, Deserialize, Debug, Clone)]
9pub struct Cahvor {
10    // Camera center vector C
11    #[serde(with = "crate::vector::vector_format")]
12    pub c: Vector,
13
14    // Camera axis unit vector A
15    #[serde(with = "crate::vector::vector_format")]
16    pub a: Vector,
17
18    // Horizontal information vector H
19    #[serde(with = "crate::vector::vector_format")]
20    pub h: Vector,
21
22    // Vertical information vector V
23    #[serde(with = "crate::vector::vector_format")]
24    pub v: Vector,
25
26    // Optical axis unit vector O
27    #[serde(with = "crate::vector::vector_format")]
28    pub o: Vector,
29
30    // Radial lens distortion coefficients
31    #[serde(with = "crate::vector::vector_format")]
32    pub r: Vector,
33}
34
35impl Cahvor {
36    pub fn default() -> Self {
37        Cahvor {
38            c: Vector::default(),
39            a: Vector::default(),
40            h: Vector::default(),
41            v: Vector::default(),
42            o: Vector::default(),
43            r: Vector::default(),
44        }
45    }
46
47    pub fn hc(&self) -> f64 {
48        self.a.dot_product(&self.h)
49    }
50
51    pub fn vc(&self) -> f64 {
52        self.a.dot_product(&self.v)
53    }
54
55    pub fn hs(&self) -> f64 {
56        let cp = self.a.cross_product(&self.h);
57        cp.len()
58    }
59
60    // Alias to hs() for focal length
61
62    pub fn vs(&self) -> f64 {
63        let cp = self.a.cross_product(&self.v);
64        cp.len()
65    }
66
67    pub fn zeta(&self, p: &Vector) -> f64 {
68        p.subtract(&self.c).dot_product(&self.o)
69    }
70
71    pub fn _lambda(&self, p: &Vector, z: f64) -> Vector {
72        let o = self.o.scale(z);
73        p.subtract(&self.c).subtract(&o)
74    }
75
76    pub fn lambda(&self, p: &Vector) -> Vector {
77        let z = self.zeta(p);
78        self._lambda(&p, z)
79    }
80
81    pub fn tau(&self, p: &Vector) -> f64 {
82        let z = self.zeta(p);
83        let l = self._lambda(p, z);
84
85        l.dot_product(&l) / z.powi(2)
86    }
87
88    pub fn mu(&self, p: &Vector) -> f64 {
89        let t = self.tau(p);
90        self.r.x + self.r.y * t + self.r.z * t.powi(2)
91    }
92
93    pub fn corrected_point(&self, p: &Vector) -> Vector {
94        let mut l = self.lambda(p);
95        let m = self.mu(p);
96        l = l.scale(m);
97        p.add(&l)
98    }
99
100    pub fn rotation_matrix(&self, _w: f64, _o: f64, _k: f64) -> Matrix {
101        let w = _w.to_radians();
102        let o = _o.to_radians();
103        let k = _k.to_radians();
104
105        Matrix::new_with_values(
106            o.cos() * k.cos(),
107            w.sin() * o.sin() * k.sin() + w.cos() * k.sin(),
108            -(w.cos() * o.sin() * k.cos() + w.sin() * k.sin()),
109            0.0,
110            -(o.cos() * k.sin()),
111            -(w.sin() * o.sin() * k.sin() + w.cos() * k.cos()),
112            w.cos() * o.sin() * k.sin() + w.sin() * k.cos(),
113            0.0,
114            o.sin(),
115            -(w.sin() * o.cos()),
116            w.cos() * o.cos(),
117            0.0,
118            0.0,
119            0.0,
120            0.0,
121            1.0,
122        )
123    }
124
125    pub fn project_object_to_image_point(&self, p: &Vector) -> ImageCoordinate {
126        ImageCoordinate {
127            sample: self.i(p),
128            line: self.j(p),
129        }
130    }
131
132    // i -> column (origin at upper left)
133    pub fn i(&self, p: &Vector) -> f64 {
134        let pmc = p.subtract(&self.c);
135        let a = pmc.dot_product(&self.h);
136        let b = pmc.dot_product(&self.a);
137        a / b
138    }
139
140    // j -> row (origin at upper left)
141    pub fn j(&self, p: &Vector) -> f64 {
142        let pmc = p.subtract(&self.c);
143        let a = pmc.dot_product(&self.v);
144        let b = pmc.dot_product(&self.a);
145        a / b
146    }
147}
148
149impl CameraModelTrait for Cahvor {
150    fn model_type(&self) -> ModelType {
151        ModelType::CAHVOR
152    }
153
154    fn c(&self) -> Vector {
155        self.c
156    }
157
158    fn a(&self) -> Vector {
159        self.a
160    }
161
162    fn h(&self) -> Vector {
163        self.h
164    }
165
166    fn v(&self) -> Vector {
167        self.v
168    }
169
170    fn o(&self) -> Vector {
171        self.o
172    }
173
174    fn r(&self) -> Vector {
175        self.r
176    }
177
178    fn e(&self) -> Vector {
179        Vector::default()
180    }
181
182    fn box_clone(&self) -> Box<dyn CameraModelTrait + 'static> {
183        Box::new((*self).clone())
184    }
185
186    fn f(&self) -> f64 {
187        self.hs()
188    }
189
190    // Adapted from https://github.com/NASA-AMMOS/VICAR/blob/master/vos/java/jpl/mipl/mars/pig/PigCoreCAHVOR.java
191    fn ls_to_look_vector(&self, coordinate: &ImageCoordinate) -> error::Result<LookVector> {
192        let line = coordinate.line;
193        let sample = coordinate.sample;
194
195        let origin = self.c;
196
197        let f = self.v.subtract(&self.a.scale(line));
198        let g = self.h.subtract(&self.a.scale(sample));
199        let mut rr = f.cross_product(&g).normalized();
200
201        let t = self.v.cross_product(&self.h);
202        if t.dot_product(&self.a) < 0.0 {
203            rr = rr.inversed();
204        }
205
206        let omega = rr.dot_product(&self.o);
207        let omega_2 = omega * omega;
208        let wo = self.o.scale(omega);
209        let lambda = rr.subtract(&wo);
210        let tau = lambda.dot_product(&lambda) / omega_2;
211        let k1 = 1.0 + self.r.x;
212        let k3 = self.r.y * tau;
213        let k5 = self.r.z * tau * tau;
214        let mu = self.r.x + k3 + k5;
215        let mut u = 1.0 - mu;
216
217        for i in 0..(MAXITER + 1) {
218            if i >= MAXITER {
219                return Err("cahvor 2d to 3d: Too many iterations");
220            }
221
222            let u_2 = u * u;
223            let poly = ((k5 * u_2 + k3) * u_2 + k1) * u - 1.0;
224            let deriv = (5.0 * k5 * u_2 + 3.0 * k3) * u_2 + k1;
225            if deriv <= EPSILON {
226                return Err("Cahvor 2d to 3d: Distortion is too negative");
227            } else {
228                let du = poly / deriv;
229                u -= du;
230                if du.abs() < CONV {
231                    break;
232                }
233            }
234        }
235
236        Ok(LookVector {
237            origin,
238            look_direction: rr.subtract(&lambda.scale(1.0 - u)).normalized(),
239        })
240    }
241
242    // Adapted from https://github.com/NASA-AMMOS/VICAR/blob/master/vos/java/jpl/mipl/mars/pig/PigCoreCAHVOR.java
243    fn xyz_to_ls(&self, xyz: &Vector, infinity: bool) -> ImageCoordinate {
244        if infinity {
245            let omega = xyz.dot_product(&self.o);
246            let omega_2 = omega * omega;
247            let wo = self.o.scale(omega);
248            let lambda = xyz.subtract(&wo);
249            let tau = lambda.dot_product(&lambda) / omega_2;
250            let mu = self.r.x + (self.r.y * tau) + (self.r.z * tau * tau);
251            let pp_c = xyz.add(&lambda.scale(mu));
252
253            let alpha = pp_c.dot_product(&self.a);
254            let beta = pp_c.dot_product(&self.h);
255            let gamma = pp_c.dot_product(&self.v);
256
257            ImageCoordinate {
258                sample: beta / alpha,
259                line: gamma / alpha,
260            }
261        } else {
262            let p_c = xyz.subtract(&self.c);
263            let omega = p_c.dot_product(&self.o);
264            let omega_2 = omega * omega;
265            let wo = self.o.scale(omega);
266            let lambda = p_c.subtract(&wo);
267            let tau = lambda.dot_product(&lambda) / omega_2;
268            let mu = self.r.x + (self.r.y * tau) + (self.r.z * tau * tau);
269            let pp = xyz.add(&lambda.scale(mu));
270
271            let pp_c = pp.subtract(&self.c);
272            let alpha = pp_c.dot_product(&self.a);
273            let beta = pp_c.dot_product(&self.h);
274            let gamma = pp_c.dot_product(&self.v);
275
276            ImageCoordinate {
277                sample: beta / alpha,
278                line: gamma / alpha,
279            }
280        }
281    }
282
283    fn pixel_angle_horiz(&self) -> f64 {
284        let a = self.v.dot_product(&self.a);
285        let s = self.a.scale(a);
286        let f = self.v.subtract(&s).len();
287        (1.0 / f).atan()
288    }
289
290    fn pixel_angle_vert(&self) -> f64 {
291        let a = self.h.dot_product(&self.a);
292        let s = self.a.scale(a);
293        let f = self.h.subtract(&s).len();
294        (1.0 / f).atan()
295    }
296
297    fn serialize(&self) -> String {
298        format!(
299            "{};{};{};{};{};{}",
300            vec_to_str(&self.c.to_vec()),
301            vec_to_str(&self.a.to_vec()),
302            vec_to_str(&self.h.to_vec()),
303            vec_to_str(&self.v.to_vec()),
304            vec_to_str(&self.o.to_vec()),
305            vec_to_str(&self.r.to_vec())
306        )
307    }
308}
309
310//  Adapted from https://github.com/digimatronics/ComputerVision/blob/master/src/vw/Camera/CAHVORModel.cc
311pub fn linearize(
312    camera_model: &Cahvor,
313    cahvor_width: usize,
314    cahvor_height: usize,
315    cahv_width: usize,
316    cahv_height: usize,
317) -> Cahv {
318    let minfov = true;
319
320    let mut output_camera = Cahv::default();
321    output_camera.c = camera_model.c;
322
323    let hpts = vec![
324        Vector::default(),
325        Vector::new(0.0, (cahvor_height as f64 - 1.0) / 2.0, 0.0),
326        Vector::new(0.0, cahvor_height as f64 - 1.0, 0.0),
327        Vector::new(cahvor_width as f64 - 1.0, 0.0, 0.0),
328        Vector::new(
329            cahvor_width as f64 - 1.0,
330            (cahvor_height as f64 - 1.0) / 2.0,
331            0.0,
332        ),
333        Vector::new(cahvor_width as f64, cahvor_height as f64, 0.0)
334            .subtract(&Vector::new(1.0, 1.0, 0.0)),
335    ];
336
337    let vpts = vec![
338        Vector::default(),
339        Vector::new((cahvor_width as f64 - 1.0) / 2.0, 0.0, 0.0),
340        Vector::new(cahvor_width as f64 - 1.0, 0.0, 0.0),
341        Vector::new(0.0, cahvor_height as f64 - 1.0, 0.0),
342        Vector::new(
343            (cahvor_width as f64 - 1.0) / 2.0,
344            cahvor_height as f64 - 1.0,
345            0.0,
346        ),
347        Vector::new(cahvor_width as f64, cahvor_height as f64, 0.0)
348            .subtract(&Vector::new(1.0, 1.0, 0.0)),
349    ];
350
351    for local in vpts.iter() {
352        if let Ok(lv) = camera_model.ls_to_look_vector(&ImageCoordinate {
353            line: local.y,
354            sample: local.x,
355        }) {
356            output_camera.a = output_camera.a.add(&lv.look_direction);
357        }
358    }
359
360    for local in hpts.iter() {
361        if let Ok(lv) = camera_model.ls_to_look_vector(&ImageCoordinate {
362            line: local.y,
363            sample: local.x,
364        }) {
365            output_camera.a = output_camera.a.add(&lv.look_direction);
366        }
367    }
368
369    output_camera.a = output_camera.a.normalized();
370
371    let mut dn = camera_model.a.cross_product(&camera_model.h).normalized();
372    let mut rt = dn.cross_product(&output_camera.a);
373    dn = output_camera.a.cross_product(&rt).normalized();
374    rt = rt.normalized();
375
376    let mut hmin = 1.0;
377    let mut hmax = -1.0;
378    for loop_ in hpts.iter() {
379        if let Ok(lv) = camera_model.ls_to_look_vector(&ImageCoordinate {
380            line: loop_.y,
381            sample: loop_.x,
382        }) {
383            let u3 = lv.look_direction;
384            let sn = output_camera
385                .a
386                .cross_product(&u3.subtract(&dn.scale(dn.dot_product(&u3))).normalized())
387                .len();
388            hmin = min!(hmin, sn);
389            hmax = max!(hmax, sn);
390        }
391    }
392
393    let mut vmin = 1.0;
394    let mut vmax = -1.0;
395    for loop_ in vpts.iter() {
396        if let Ok(lv) = camera_model.ls_to_look_vector(&ImageCoordinate {
397            line: loop_.y,
398            sample: loop_.x,
399        }) {
400            let u3 = lv.look_direction;
401            let sn = output_camera
402                .a
403                .cross_product(&u3.subtract(&rt.scale(rt.dot_product(&u3))).normalized())
404                .len();
405            vmin = min!(vmin, sn);
406            vmax = max!(vmax, sn);
407        }
408    }
409
410    println!("{}, {} -- {}, {}", hmin, hmax, vmin, vmax);
411    let image_center = Vector::new(cahv_width as f64, cahv_height as f64, 0.0)
412        .subtract(&Vector::new(1.0, 1.0, 0.0))
413        .scale(0.5);
414    println!("image_center -> {:?}", image_center);
415    let image_center_2 = image_center.multiply(&image_center);
416    println!("image_center_2 -> {:?}", image_center_2);
417
418    let scale_factors = if minfov {
419        image_center_2
420            .divide(&Vector::new(hmin * hmin, vmin * vmin, 0.0))
421            .subtract(&image_center_2)
422            .sqrt()
423    } else {
424        image_center_2
425            .divide(&Vector::new(hmax * hmax, vmax * vmax, 0.0))
426            .subtract(&image_center_2)
427            .sqrt()
428    };
429
430    println!("scale_factors -> {:?}", scale_factors);
431    output_camera.h = rt
432        .scale(scale_factors.x)
433        .add(&output_camera.a.scale(image_center.x));
434    output_camera.v = dn
435        .scale(scale_factors.y)
436        .add(&output_camera.a.scale(image_center.y));
437
438    output_camera
439}