Skip to main content

braid_sim/
projection.rs

1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Project 3D ground-truth points into each camera's distorted pixel, with
5//! field-of-view culling.
6
7use braid_mvg::PointWorldFrame;
8use flydra_mvg::FlydraMultiCameraSystem;
9
10use crate::scenario::Scenario;
11
12/// A single camera's observation of a 3D point: the distorted pixel, or `None`
13/// if the point projects outside the image (culled).
14#[derive(Debug, Clone, PartialEq)]
15pub struct Observation {
16    /// Camera name (see [`Scenario::camera_name`]).
17    pub cam_name: String,
18    /// Pixel `(x, y)`, or `None` if outside the image bounds.
19    pub pixel: Option<(f64, f64)>,
20}
21
22/// Whether pixel `(x, y)` lies within a `width` x `height` image.
23fn in_image(x: f64, y: f64, width: usize, height: usize) -> bool {
24    x >= 0.0 && y >= 0.0 && x < width as f64 && y < height as f64
25}
26
27/// Project a 3D point into a single named camera, returning the pixel `(x, y)`
28/// or `None` if the camera is unknown or the point lands outside a
29/// `width` x `height` image.
30pub fn project_pixel(
31    system: &FlydraMultiCameraSystem<f64>,
32    cam_name: &str,
33    width: usize,
34    height: usize,
35    pt: &PointWorldFrame<f64>,
36) -> Option<(f64, f64)> {
37    let cam = system.cam_by_name(cam_name)?;
38    let dp = cam.project_3d_to_distorted_pixel(pt);
39    let (x, y) = (dp.coords.x, dp.coords.y);
40    if in_image(x, y, width, height) {
41        Some((x, y))
42    } else {
43        None
44    }
45}
46
47/// Project a 3D point into every camera of `system`, culling points that land
48/// outside the image.
49///
50/// Note: this culls on image bounds only. For the perfect-world ring geometry,
51/// all arena points are in front of all cameras; if cameras are later placed so
52/// that points can fall behind a camera, add a camera-frame depth (`z > 0`)
53/// check here.
54pub fn project_all(
55    system: &FlydraMultiCameraSystem<f64>,
56    scenario: &Scenario,
57    pt: &PointWorldFrame<f64>,
58) -> Vec<Observation> {
59    (0..scenario.cameras.count)
60        .map(|k| {
61            let cam_name = Scenario::camera_name(k);
62            let pixel = system.cam_by_name(&cam_name).and_then(|cam| {
63                let dp = cam.project_3d_to_distorted_pixel(pt);
64                let (x, y) = (dp.coords.x, dp.coords.y);
65                if in_image(
66                    x,
67                    y,
68                    scenario.cameras.image_width,
69                    scenario.cameras.image_height,
70                ) {
71                    Some((x, y))
72                } else {
73                    None
74                }
75            });
76            Observation { cam_name, pixel }
77        })
78        .collect()
79}