pyarchinit-photogrammetry-core 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,321 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.4"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
16
+
17
+ [[package]]
18
+ name = "crossbeam-deque"
19
+ version = "0.8.6"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
22
+ dependencies = [
23
+ "crossbeam-epoch",
24
+ "crossbeam-utils",
25
+ ]
26
+
27
+ [[package]]
28
+ name = "crossbeam-epoch"
29
+ version = "0.9.18"
30
+ source = "registry+https://github.com/rust-lang/crates.io-index"
31
+ checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
32
+ dependencies = [
33
+ "crossbeam-utils",
34
+ ]
35
+
36
+ [[package]]
37
+ name = "crossbeam-utils"
38
+ version = "0.8.21"
39
+ source = "registry+https://github.com/rust-lang/crates.io-index"
40
+ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
41
+
42
+ [[package]]
43
+ name = "either"
44
+ version = "1.15.0"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
47
+
48
+ [[package]]
49
+ name = "heck"
50
+ version = "0.5.0"
51
+ source = "registry+https://github.com/rust-lang/crates.io-index"
52
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
53
+
54
+ [[package]]
55
+ name = "indoc"
56
+ version = "2.0.7"
57
+ source = "registry+https://github.com/rust-lang/crates.io-index"
58
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
59
+ dependencies = [
60
+ "rustversion",
61
+ ]
62
+
63
+ [[package]]
64
+ name = "libc"
65
+ version = "0.2.182"
66
+ source = "registry+https://github.com/rust-lang/crates.io-index"
67
+ checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
68
+
69
+ [[package]]
70
+ name = "matrixmultiply"
71
+ version = "0.3.10"
72
+ source = "registry+https://github.com/rust-lang/crates.io-index"
73
+ checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
74
+ dependencies = [
75
+ "autocfg",
76
+ "rawpointer",
77
+ ]
78
+
79
+ [[package]]
80
+ name = "memoffset"
81
+ version = "0.9.1"
82
+ source = "registry+https://github.com/rust-lang/crates.io-index"
83
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
84
+ dependencies = [
85
+ "autocfg",
86
+ ]
87
+
88
+ [[package]]
89
+ name = "ndarray"
90
+ version = "0.16.1"
91
+ source = "registry+https://github.com/rust-lang/crates.io-index"
92
+ checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
93
+ dependencies = [
94
+ "matrixmultiply",
95
+ "num-complex",
96
+ "num-integer",
97
+ "num-traits",
98
+ "portable-atomic",
99
+ "portable-atomic-util",
100
+ "rawpointer",
101
+ ]
102
+
103
+ [[package]]
104
+ name = "num-complex"
105
+ version = "0.4.6"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
108
+ dependencies = [
109
+ "num-traits",
110
+ ]
111
+
112
+ [[package]]
113
+ name = "num-integer"
114
+ version = "0.1.46"
115
+ source = "registry+https://github.com/rust-lang/crates.io-index"
116
+ checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
117
+ dependencies = [
118
+ "num-traits",
119
+ ]
120
+
121
+ [[package]]
122
+ name = "num-traits"
123
+ version = "0.2.19"
124
+ source = "registry+https://github.com/rust-lang/crates.io-index"
125
+ checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
126
+ dependencies = [
127
+ "autocfg",
128
+ ]
129
+
130
+ [[package]]
131
+ name = "numpy"
132
+ version = "0.22.1"
133
+ source = "registry+https://github.com/rust-lang/crates.io-index"
134
+ checksum = "edb929bc0da91a4d85ed6c0a84deaa53d411abfb387fc271124f91bf6b89f14e"
135
+ dependencies = [
136
+ "libc",
137
+ "ndarray",
138
+ "num-complex",
139
+ "num-integer",
140
+ "num-traits",
141
+ "pyo3",
142
+ "rustc-hash",
143
+ ]
144
+
145
+ [[package]]
146
+ name = "once_cell"
147
+ version = "1.21.3"
148
+ source = "registry+https://github.com/rust-lang/crates.io-index"
149
+ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
150
+
151
+ [[package]]
152
+ name = "portable-atomic"
153
+ version = "1.13.1"
154
+ source = "registry+https://github.com/rust-lang/crates.io-index"
155
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
156
+
157
+ [[package]]
158
+ name = "portable-atomic-util"
159
+ version = "0.2.5"
160
+ source = "registry+https://github.com/rust-lang/crates.io-index"
161
+ checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5"
162
+ dependencies = [
163
+ "portable-atomic",
164
+ ]
165
+
166
+ [[package]]
167
+ name = "ppg_core"
168
+ version = "0.2.1"
169
+ dependencies = [
170
+ "numpy",
171
+ "pyo3",
172
+ "rayon",
173
+ ]
174
+
175
+ [[package]]
176
+ name = "proc-macro2"
177
+ version = "1.0.106"
178
+ source = "registry+https://github.com/rust-lang/crates.io-index"
179
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
180
+ dependencies = [
181
+ "unicode-ident",
182
+ ]
183
+
184
+ [[package]]
185
+ name = "pyo3"
186
+ version = "0.22.6"
187
+ source = "registry+https://github.com/rust-lang/crates.io-index"
188
+ checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884"
189
+ dependencies = [
190
+ "cfg-if",
191
+ "indoc",
192
+ "libc",
193
+ "memoffset",
194
+ "once_cell",
195
+ "portable-atomic",
196
+ "pyo3-build-config",
197
+ "pyo3-ffi",
198
+ "pyo3-macros",
199
+ "unindent",
200
+ ]
201
+
202
+ [[package]]
203
+ name = "pyo3-build-config"
204
+ version = "0.22.6"
205
+ source = "registry+https://github.com/rust-lang/crates.io-index"
206
+ checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38"
207
+ dependencies = [
208
+ "once_cell",
209
+ "target-lexicon",
210
+ ]
211
+
212
+ [[package]]
213
+ name = "pyo3-ffi"
214
+ version = "0.22.6"
215
+ source = "registry+https://github.com/rust-lang/crates.io-index"
216
+ checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636"
217
+ dependencies = [
218
+ "libc",
219
+ "pyo3-build-config",
220
+ ]
221
+
222
+ [[package]]
223
+ name = "pyo3-macros"
224
+ version = "0.22.6"
225
+ source = "registry+https://github.com/rust-lang/crates.io-index"
226
+ checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453"
227
+ dependencies = [
228
+ "proc-macro2",
229
+ "pyo3-macros-backend",
230
+ "quote",
231
+ "syn",
232
+ ]
233
+
234
+ [[package]]
235
+ name = "pyo3-macros-backend"
236
+ version = "0.22.6"
237
+ source = "registry+https://github.com/rust-lang/crates.io-index"
238
+ checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
239
+ dependencies = [
240
+ "heck",
241
+ "proc-macro2",
242
+ "pyo3-build-config",
243
+ "quote",
244
+ "syn",
245
+ ]
246
+
247
+ [[package]]
248
+ name = "quote"
249
+ version = "1.0.44"
250
+ source = "registry+https://github.com/rust-lang/crates.io-index"
251
+ checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
252
+ dependencies = [
253
+ "proc-macro2",
254
+ ]
255
+
256
+ [[package]]
257
+ name = "rawpointer"
258
+ version = "0.2.1"
259
+ source = "registry+https://github.com/rust-lang/crates.io-index"
260
+ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
261
+
262
+ [[package]]
263
+ name = "rayon"
264
+ version = "1.11.0"
265
+ source = "registry+https://github.com/rust-lang/crates.io-index"
266
+ checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
267
+ dependencies = [
268
+ "either",
269
+ "rayon-core",
270
+ ]
271
+
272
+ [[package]]
273
+ name = "rayon-core"
274
+ version = "1.13.0"
275
+ source = "registry+https://github.com/rust-lang/crates.io-index"
276
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
277
+ dependencies = [
278
+ "crossbeam-deque",
279
+ "crossbeam-utils",
280
+ ]
281
+
282
+ [[package]]
283
+ name = "rustc-hash"
284
+ version = "1.1.0"
285
+ source = "registry+https://github.com/rust-lang/crates.io-index"
286
+ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
287
+
288
+ [[package]]
289
+ name = "rustversion"
290
+ version = "1.0.22"
291
+ source = "registry+https://github.com/rust-lang/crates.io-index"
292
+ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
293
+
294
+ [[package]]
295
+ name = "syn"
296
+ version = "2.0.116"
297
+ source = "registry+https://github.com/rust-lang/crates.io-index"
298
+ checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
299
+ dependencies = [
300
+ "proc-macro2",
301
+ "quote",
302
+ "unicode-ident",
303
+ ]
304
+
305
+ [[package]]
306
+ name = "target-lexicon"
307
+ version = "0.12.16"
308
+ source = "registry+https://github.com/rust-lang/crates.io-index"
309
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
310
+
311
+ [[package]]
312
+ name = "unicode-ident"
313
+ version = "1.0.24"
314
+ source = "registry+https://github.com/rust-lang/crates.io-index"
315
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
316
+
317
+ [[package]]
318
+ name = "unindent"
319
+ version = "0.2.4"
320
+ source = "registry+https://github.com/rust-lang/crates.io-index"
321
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
@@ -0,0 +1,25 @@
1
+ [package]
2
+ name = "ppg_core"
3
+ version = "0.2.1"
4
+ edition = "2021"
5
+ license = "GPL-2.0-or-later"
6
+ description = "Native kernels of pyarchinit-photogrammetry"
7
+ repository = "https://github.com/enzococca/pyarchinit-photogrammetry"
8
+ publish = false
9
+ readme = "README.md"
10
+
11
+ [lib]
12
+ name = "ppg_core"
13
+ crate-type = ["cdylib"]
14
+
15
+ [dependencies]
16
+ pyo3 = { version = "0.22", features = ["extension-module", "abi3-py310"] }
17
+ numpy = "0.22"
18
+ rayon = "1.10"
19
+
20
+ [profile.release]
21
+ lto = "thin"
22
+ codegen-units = 1
23
+ # Rust strips debug info by default; the stripped library is refused by macOS 26/27
24
+ # ("mis-aligned LINKEDIT string pool"), so it is kept
25
+ strip = false
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyarchinit-photogrammetry-core
3
+ Version: 0.2.1
4
+ Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Topic :: Scientific/Engineering :: GIS
8
+ Requires-Dist: numpy>=1.24
9
+ Summary: Native kernels (Rust) of pyarchinit-photogrammetry: depth maps (PatchMatch MVS), orthomosaic, confidence
10
+ Author-email: Enzo Cocca <enzo.ccc@gmail.com>
11
+ License: GPL-2.0-or-later
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
14
+ Project-URL: Homepage, https://github.com/enzococca/pyarchinit-photogrammetry
15
+
16
+ # pyarchinit-photogrammetry-core
17
+
18
+ Native kernels of [pyarchinit-photogrammetry](https://github.com/enzococca/pyarchinit-photogrammetry),
19
+ written in Rust (PyO3 + rayon) and imported as `ppg_core`. The package is optional:
20
+ pyarchinit-photogrammetry keeps the numpy version of every kernel, uses it when `ppg_core` is
21
+ missing or when `PPG_NATIVE=0`, and its tests compare the two.
22
+
23
+ Kernels: fusion of the depth maps of one photo, depth z-buffer and normals, projection of points
24
+ with the COLMAP camera models, scoring of the photos of an orthomosaic tile, agreement of points
25
+ with a depth map (confidence), PatchMatch multi-view stereo of one photo (`patchmatch_depth`: a
26
+ plane per pixel, normalised cross correlation with the best of its neighbouring photos,
27
+ red-black propagation and random refinement, from random planes or from a depth map).
28
+
29
+ Build and install in the environment of pyarchinit-photogrammetry (a Rust toolchain is needed):
30
+
31
+ ```bash
32
+ pip install ./native # or: uv pip install ./native
33
+ ```
34
+
35
+ License: GPL-2.0-or-later.
36
+
@@ -0,0 +1,20 @@
1
+ # pyarchinit-photogrammetry-core
2
+
3
+ Native kernels of [pyarchinit-photogrammetry](https://github.com/enzococca/pyarchinit-photogrammetry),
4
+ written in Rust (PyO3 + rayon) and imported as `ppg_core`. The package is optional:
5
+ pyarchinit-photogrammetry keeps the numpy version of every kernel, uses it when `ppg_core` is
6
+ missing or when `PPG_NATIVE=0`, and its tests compare the two.
7
+
8
+ Kernels: fusion of the depth maps of one photo, depth z-buffer and normals, projection of points
9
+ with the COLMAP camera models, scoring of the photos of an orthomosaic tile, agreement of points
10
+ with a depth map (confidence), PatchMatch multi-view stereo of one photo (`patchmatch_depth`: a
11
+ plane per pixel, normalised cross correlation with the best of its neighbouring photos,
12
+ red-black propagation and random refinement, from random planes or from a depth map).
13
+
14
+ Build and install in the environment of pyarchinit-photogrammetry (a Rust toolchain is needed):
15
+
16
+ ```bash
17
+ pip install ./native # or: uv pip install ./native
18
+ ```
19
+
20
+ License: GPL-2.0-or-later.
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.5,<2"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "pyarchinit-photogrammetry-core"
7
+ version = "0.2.1"
8
+ description = "Native kernels (Rust) of pyarchinit-photogrammetry: depth maps (PatchMatch MVS), orthomosaic, confidence"
9
+ readme = "README.md"
10
+ license = { text = "GPL-2.0-or-later" }
11
+ authors = [{ name = "Enzo Cocca", email = "enzo.ccc@gmail.com" }]
12
+ requires-python = ">=3.10"
13
+ dependencies = ["numpy>=1.24"]
14
+ classifiers = [
15
+ "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)",
16
+ "Programming Language :: Rust",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Scientific/Engineering :: GIS",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/enzococca/pyarchinit-photogrammetry"
23
+
24
+ [tool.maturin]
25
+ module-name = "ppg_core"
26
+ features = ["pyo3/extension-module"]
@@ -0,0 +1,145 @@
1
+ //! COLMAP camera models: the pixel of a point given in the camera frame, computed as COLMAP's
2
+ //! `ImgFromCam` does (normalised coordinates, lens distortion, focal length and principal point).
3
+
4
+ #[derive(Clone, Copy, Debug, PartialEq)]
5
+ enum Model {
6
+ SimplePinhole,
7
+ Pinhole,
8
+ SimpleRadial,
9
+ Radial,
10
+ OpenCv,
11
+ FullOpenCv,
12
+ }
13
+
14
+ #[derive(Clone, Debug)]
15
+ pub struct Camera {
16
+ model: Model,
17
+ p: [f64; 12],
18
+ pub fx: f64,
19
+ pub fy: f64,
20
+ pub cx: f64,
21
+ pub cy: f64,
22
+ pub width: f64,
23
+ pub height: f64,
24
+ }
25
+
26
+ /// Names of the models the kernels know (the others stay with the Python code).
27
+ pub const MODELS: [&str; 6] = ["SIMPLE_PINHOLE", "PINHOLE", "SIMPLE_RADIAL", "RADIAL", "OPENCV", "FULL_OPENCV"];
28
+
29
+ impl Camera {
30
+ pub fn new(model: &str, params: &[f64], width: f64, height: f64) -> Result<Self, String> {
31
+ let (model, count) = match model {
32
+ "SIMPLE_PINHOLE" => (Model::SimplePinhole, 3),
33
+ "PINHOLE" => (Model::Pinhole, 4),
34
+ "SIMPLE_RADIAL" => (Model::SimpleRadial, 4),
35
+ "RADIAL" => (Model::Radial, 5),
36
+ "OPENCV" => (Model::OpenCv, 8),
37
+ "FULL_OPENCV" => (Model::FullOpenCv, 12),
38
+ other => return Err(format!("camera model {other} is not supported by the native kernels")),
39
+ };
40
+ if params.len() < count {
41
+ return Err(format!("{count} parameters expected, {} given", params.len()));
42
+ }
43
+ let mut p = [0.0; 12];
44
+ p[..count].copy_from_slice(&params[..count]);
45
+ let (fx, fy, cx, cy) = match model {
46
+ Model::SimplePinhole | Model::SimpleRadial | Model::Radial => (p[0], p[0], p[1], p[2]),
47
+ _ => (p[0], p[1], p[2], p[3]),
48
+ };
49
+ Ok(Camera { model, p, fx, fy, cx, cy, width, height })
50
+ }
51
+
52
+ /// Mean focal length in pixels (`Sensor.focal_px`).
53
+ pub fn focal(&self) -> f64 {
54
+ (self.fx + self.fy) / 2.0
55
+ }
56
+
57
+ /// Pixel of the normalised point `(u, v) = (x / z, y / z)`.
58
+ #[inline]
59
+ pub fn pixel(&self, u: f64, v: f64) -> (f64, f64) {
60
+ let p = &self.p;
61
+ match self.model {
62
+ Model::SimplePinhole => (p[0] * u + p[1], p[0] * v + p[2]),
63
+ Model::Pinhole => (p[0] * u + p[2], p[1] * v + p[3]),
64
+ Model::SimpleRadial => {
65
+ let k = p[3];
66
+ let u2 = u * u;
67
+ let v2 = v * v;
68
+ let r2 = u2 + v2;
69
+ let radial = k * r2;
70
+ let (du, dv) = (u * radial, v * radial);
71
+ (p[0] * (u + du) + p[1], p[0] * (v + dv) + p[2])
72
+ }
73
+ Model::Radial => {
74
+ let (k1, k2) = (p[3], p[4]);
75
+ let u2 = u * u;
76
+ let v2 = v * v;
77
+ let r2 = u2 + v2;
78
+ let radial = k1 * r2 + k2 * r2 * r2;
79
+ let (du, dv) = (u * radial, v * radial);
80
+ (p[0] * (u + du) + p[1], p[0] * (v + dv) + p[2])
81
+ }
82
+ Model::OpenCv => {
83
+ let (k1, k2, p1, p2) = (p[4], p[5], p[6], p[7]);
84
+ let u2 = u * u;
85
+ let uv = u * v;
86
+ let v2 = v * v;
87
+ let r2 = u2 + v2;
88
+ let radial = k1 * r2 + k2 * r2 * r2;
89
+ let du = u * radial + 2.0 * p1 * uv + p2 * (r2 + 2.0 * u2);
90
+ let dv = v * radial + 2.0 * p2 * uv + p1 * (r2 + 2.0 * v2);
91
+ (p[0] * (u + du) + p[2], p[1] * (v + dv) + p[3])
92
+ }
93
+ Model::FullOpenCv => {
94
+ let (k1, k2, p1, p2, k3, k4, k5, k6) = (p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11]);
95
+ let u2 = u * u;
96
+ let uv = u * v;
97
+ let v2 = v * v;
98
+ let r2 = u2 + v2;
99
+ let r4 = r2 * r2;
100
+ let r6 = r4 * r2;
101
+ let radial = (1.0 + k1 * r2 + k2 * r4 + k3 * r6) / (1.0 + k4 * r2 + k5 * r4 + k6 * r6);
102
+ let du = u * radial + 2.0 * p1 * uv + p2 * (r2 + 2.0 * u2) - u;
103
+ let dv = v * radial + 2.0 * p2 * uv + p1 * (r2 + 2.0 * v2) - v;
104
+ (p[0] * (u + du) + p[2], p[1] * (v + dv) + p[3])
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ /// Rows of a `cam_from_world` matrix (3x4).
111
+ pub type Pose = [[f64; 4]; 3];
112
+
113
+ #[inline]
114
+ pub fn transform(m: &Pose, x: f64, y: f64, z: f64) -> (f64, f64, f64) {
115
+ (
116
+ m[0][0] * x + m[0][1] * y + m[0][2] * z + m[0][3],
117
+ m[1][0] * x + m[1][1] * y + m[1][2] * z + m[1][3],
118
+ m[2][0] * x + m[2][1] * y + m[2][2] * z + m[2][3],
119
+ )
120
+ }
121
+
122
+ /// What `engine.visibility.project_valid` computes for one point: pixel (NaN when the point is
123
+ /// behind the camera or far outside the field of view), depth and whether the pixel is inside the
124
+ /// photo by more than `margin`.
125
+ #[inline]
126
+ pub fn project_valid(cam: &Camera, m: &Pose, x: f64, y: f64, z: f64, margin: f64) -> (f64, f64, f64, bool) {
127
+ let (pcx, pcy, depth) = transform(m, x, y, z);
128
+ if !(depth > 1e-9) {
129
+ return (f64::NAN, f64::NAN, depth, false);
130
+ }
131
+ // far outside the field of view: left out before the lens distortion (as the Python code)
132
+ let lu = cam.fx * pcx / depth + cam.cx;
133
+ let lv = cam.fy * pcy / depth + cam.cy;
134
+ if !(lu > -0.25 * cam.width && lu < 1.25 * cam.width && lv > -0.25 * cam.height && lv < 1.25 * cam.height) {
135
+ return (f64::NAN, f64::NAN, depth, false);
136
+ }
137
+ let (pu, pv) = cam.pixel(pcx / depth, pcy / depth);
138
+ let ok = pu.is_finite()
139
+ && pv.is_finite()
140
+ && pu >= margin
141
+ && pu < cam.width - margin
142
+ && pv >= margin
143
+ && pv < cam.height - margin;
144
+ (pu, pv, depth, ok)
145
+ }