1use nalgebra::{
2 allocator::Allocator,
3 base::storage::{Owned, Storage},
4 convert, DefaultAllocator, Dim, OMatrix, RealField, U2, U3,
5};
6
7#[cfg(feature = "serde-serialize")]
8use serde::{Deserialize, Serialize};
9
10use crate::{
11 coordinate_system::CameraFrame, Bundle, IntrinsicParameters, Pixels, Points, RayBundle,
12};
13
14use crate::ray_bundle_types::SharedDirectionRayBundle;
15
16#[derive(Debug, Clone, PartialEq)]
44#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
45pub struct OrthographicParams<R: RealField> {
46 pub sx: R,
48 pub sy: R,
50 pub cx: R,
52 pub cy: R,
54}
55
56impl<R: RealField> From<OrthographicParams<R>> for IntrinsicParametersOrthographic<R> {
57 #[inline]
58 fn from(params: OrthographicParams<R>) -> Self {
59 Self { params }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq)]
68#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
69pub struct IntrinsicParametersOrthographic<R: RealField> {
70 params: OrthographicParams<R>,
71}
72
73impl<R> IntrinsicParameters<R> for IntrinsicParametersOrthographic<R>
74where
75 R: RealField,
76{
77 type BundleType = SharedDirectionRayBundle<R>;
78
79 fn pixel_to_camera<IN, NPTS>(
80 &self,
81 pixels: &Pixels<R, NPTS, IN>,
82 ) -> RayBundle<CameraFrame, Self::BundleType, R, NPTS, Owned<R, NPTS, U3>>
83 where
84 Self::BundleType: Bundle<R>,
85 IN: Storage<R, NPTS, U2>,
86 NPTS: Dim,
87 DefaultAllocator: Allocator<NPTS, U3>,
88 {
89 let zero: R = convert(0.0);
90
91 let mut result = RayBundle::new_shared_plusz_direction(OMatrix::zeros_generic(
93 NPTS::from_usize(pixels.data.nrows()),
94 U3::from_usize(3),
95 ));
96
97 let origin = &mut result.data;
98
99 for i in 0..pixels.data.nrows() {
104 let u = pixels.data[(i, 0)].clone();
105 let v = pixels.data[(i, 1)].clone();
106
107 let x: R = (u - self.params.cx.clone()) / self.params.sx.clone();
108 let y: R = (v - self.params.cy.clone()) / self.params.sy.clone();
109
110 origin[(i, 0)] = x;
111 origin[(i, 1)] = y;
112 origin[(i, 2)] = zero.clone();
113 }
114 result
115 }
116
117 fn camera_to_pixel<IN, NPTS>(
118 &self,
119 camera: &Points<CameraFrame, R, NPTS, IN>,
120 ) -> Pixels<R, NPTS, Owned<R, NPTS, U2>>
121 where
122 IN: Storage<R, NPTS, U3>,
123 NPTS: Dim,
124 DefaultAllocator: Allocator<NPTS, U2>,
125 {
126 let mut result = Pixels::new(OMatrix::zeros_generic(
127 NPTS::from_usize(camera.data.nrows()),
128 U2::from_usize(2),
129 ));
130
131 for i in 0..camera.data.nrows() {
136 result.data[(i, 0)] =
137 camera.data[(i, 0)].clone() * self.params.sx.clone() + self.params.cx.clone();
138 result.data[(i, 1)] =
139 camera.data[(i, 1)].clone() * self.params.sy.clone() + self.params.cy.clone();
140 }
141 result
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use nalgebra::Vector3;
148
149 use super::{IntrinsicParametersOrthographic, OrthographicParams};
150 use crate::camera::{roundtrip_camera, Camera};
151 use crate::extrinsics::ExtrinsicParameters;
152 use crate::intrinsic_test_utils::roundtrip_intrinsics;
153
154 #[test]
155 fn roundtrip() {
156 let params = OrthographicParams {
157 sx: 100.0,
158 sy: 102.0,
159 cx: 321.0,
160 cy: 239.9,
161 };
162 let cam: IntrinsicParametersOrthographic<_> = params.into();
163
164 roundtrip_intrinsics(&cam, 640, 480, 5, 0, nalgebra::convert(1e-10));
165
166 let extrinsics = ExtrinsicParameters::from_view(
167 &Vector3::new(1.2, 3.4, 5.6), &Vector3::new(2.2, 3.4, 5.6), &nalgebra::Unit::new_normalize(Vector3::new(0.0, 0.0, 1.0)), );
171
172 let full_cam = Camera::new(cam, extrinsics);
173 roundtrip_camera(full_cam, 640, 480, 5, 0, nalgebra::convert(1e-10));
174 }
175
176 #[test]
177 #[cfg(feature = "serde-serialize")]
178 fn test_serde() {
179 let params = OrthographicParams {
180 sx: 100.0,
181 sy: 102.0,
182 cx: 321.0,
183 cy: 239.9,
184 };
185 let expected: IntrinsicParametersOrthographic<_> = params.into();
186
187 let buf = serde_json::to_string(&expected).unwrap();
188 let actual: IntrinsicParametersOrthographic<f64> = serde_json::from_str(&buf).unwrap();
189 assert!(expected == actual);
190 }
191}