1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
use re_types::{archetypes, components};
use crate::resolution_of_image_at;
/// A pinhole camera model.
///
/// Corresponds roughly to the [`re_types::archetypes::Pinhole`] archetype, but uses `glam` types.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Pinhole {
pub image_from_camera: glam::Mat3,
pub resolution: glam::Vec2,
}
impl Pinhole {
/// Width/height ratio of the camera sensor.
#[inline]
pub fn aspect_ratio(&self) -> f32 {
self.resolution.x / self.resolution.y
}
/// Principal point of the pinhole camera,
/// i.e. the intersection of the optical axis and the image plane.
///
/// [see definition of intrinsic matrix](https://en.wikipedia.org/wiki/Camera_resectioning#Intrinsic_parameters)
#[inline]
pub fn principal_point(&self) -> glam::Vec2 {
glam::vec2(
self.image_from_camera.col(2)[0],
self.image_from_camera.col(2)[1],
)
}
/// X & Y focal length in pixels.
///
/// [see definition of intrinsic matrix](https://en.wikipedia.org/wiki/Camera_resectioning#Intrinsic_parameters)
#[inline]
pub fn focal_length_in_pixels(&self) -> glam::Vec2 {
glam::vec2(
self.image_from_camera.col(0)[0],
self.image_from_camera.col(1)[1],
)
}
/// Field of View on the Y axis, i.e. the angle between top and bottom (in radians).
#[inline]
pub fn fov_y(&self) -> f32 {
2.0 * (0.5 * self.resolution[1] / self.image_from_camera.col(1)[1]).atan()
}
/// The pinhole sensor rectangle: [0, 0] - [width, height],
/// ignoring principal point.
#[inline]
pub fn resolution_rect(&self) -> egui::Rect {
egui::Rect::from_min_max(
egui::Pos2::ZERO,
egui::pos2(self.resolution.x, self.resolution.y),
)
}
/// Project camera-space coordinates into pixel coordinates,
/// returning the same z/depth.
#[inline]
pub fn project(&self, pixel: glam::Vec3) -> glam::Vec3 {
((pixel.truncate() * self.focal_length_in_pixels()) / pixel.z + self.principal_point())
.extend(pixel.z)
}
/// Given pixel coordinates and a world-space depth,
/// return a position in the camera space.
///
/// The returned z is the same as the input z (depth).
#[inline]
pub fn unproject(&self, pixel: glam::Vec3) -> glam::Vec3 {
((pixel.truncate() - self.principal_point()) * pixel.z / self.focal_length_in_pixels())
.extend(pixel.z)
}
}
/// Utility for querying the pinhole from the store.
///
/// Fallback provider will be used for everything but the projection itself.
/// Does NOT take into account blueprint overrides, defaults and fallbacks.
/// However, it will use the resolution of the image at the entity path if available.
///
/// If the projection isn't present, returns `None`.
// TODO(andreas): Give this another pass and think about how we can remove this.
// Being disconnected from the blueprint & fallbacks makes this a weird snowflake with unexpected behavior.
// Also, figure out how this might actually relate to the transform cache.
pub fn query_pinhole_and_view_coordinates_from_store_without_blueprint(
ctx: &re_viewer_context::ViewerContext<'_>,
query: &re_chunk_store::LatestAtQuery,
entity_path: &re_log_types::EntityPath,
) -> Option<(Pinhole, components::ViewCoordinates)> {
let entity_db = ctx.recording();
let query_results = entity_db.latest_at(
query,
entity_path,
[
archetypes::Pinhole::descriptor_image_from_camera().component_name,
archetypes::Pinhole::descriptor_resolution().component_name,
archetypes::Pinhole::descriptor_camera_xyz().component_name,
],
);
let pinhole_projection =
query_results.component_mono_quiet::<components::PinholeProjection>()?;
let resolution = query_results
.component_mono_quiet::<components::Resolution>()
.unwrap_or_else(|| {
resolution_of_image_at(ctx, query, entity_path).unwrap_or([100.0, 100.0].into())
});
let camera_xyz = query_results
.component_mono_quiet::<components::ViewCoordinates>()
.unwrap_or(archetypes::Pinhole::DEFAULT_CAMERA_XYZ);
Some((
Pinhole {
image_from_camera: pinhole_projection.0.into(),
resolution: resolution.into(),
},
camera_xyz,
))
}