Skip to content

Commit 25cd416

Browse files
authored
Subdivide struct Camera into Extrinsics and Intrinsics (#165)
* Subdivide `struct Camera` into `Extrinsics` and `Intrinsics` * lint.py: ignore target_wasm directory
1 parent 396bc1e commit 25cd416

File tree

8 files changed

+76
-39
lines changed

8 files changed

+76
-39
lines changed

crates/re_log_types/src/data.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,17 @@ pub type Quaternion = [f32; 4];
387387
#[derive(Clone, Debug, PartialEq)]
388388
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
389389
pub struct Camera {
390+
pub extrinsics: Extrinsics,
391+
pub intrinsics: Option<Intrinsics>,
392+
393+
/// The 2D space that this camera projects into.
394+
pub target_space: Option<ObjPath>,
395+
}
396+
397+
/// Camera pose
398+
#[derive(Copy, Clone, Debug, PartialEq)]
399+
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
400+
pub struct Extrinsics {
390401
/// How is the camera rotated?
391402
///
392403
/// This transforms world-space from camera-space.
@@ -402,7 +413,12 @@ pub struct Camera {
402413

403414
/// What is the users camera-space coordinate system?
404415
pub camera_space_convention: CameraSpaceConvention,
416+
}
405417

418+
/// Camera projection
419+
#[derive(Copy, Clone, Debug, PartialEq)]
420+
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
421+
pub struct Intrinsics {
406422
/// Column-major intrinsics matrix.
407423
/// Image coordinates from view coordinates (via projection).
408424
///
@@ -412,18 +428,22 @@ pub struct Camera {
412428
/// [0.0, 1496.1, 0.0], // col 1
413429
/// [980.5, 744.5, 1.0]] // col 2
414430
/// ```
415-
pub intrinsics: Option<[[f32; 3]; 3]>,
431+
pub intrinsics_matrix: [[f32; 3]; 3],
416432

417433
/// Pixel resolution (usually integers). Width and height.
418434
///
419435
/// Example:
420436
/// ```text
421437
/// [1920.0, 1440.0]
422438
/// ```
423-
pub resolution: Option<[f32; 2]>,
439+
pub resolution: [f32; 2],
440+
}
424441

425-
/// The 2D space that this camera projects into.
426-
pub target_space: Option<ObjPath>,
442+
impl Intrinsics {
443+
/// Field of View on the Y axis, i.e. the angle between top and bottom.
444+
pub fn fov_y(&self) -> f32 {
445+
2.0 * (0.5 * self.resolution[1] / self.intrinsics_matrix[1][1]).atan()
446+
}
427447
}
428448

429449
/// Convention for the coordinate system of the camera.

crates/re_viewer/src/misc.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ pub fn calc_bbox_3d(objects: &re_data_store::Objects<'_>) -> macaw::BoundingBox
158158
}
159159

160160
for (_, obj) in objects.camera.iter() {
161-
bbox.extend(obj.camera.position.into());
161+
bbox.extend(obj.camera.extrinsics.position.into());
162162
}
163163

164164
bbox
@@ -174,7 +174,7 @@ pub mod cam {
174174
/// Rerun uses a RHS view-space with +X=right, +Y=up, -Z=fwd.
175175
/// This creates a transform from the Rerun view-space
176176
/// to the parent space of the camera.
177-
pub fn world_from_view(cam: &re_log_types::Camera) -> macaw::IsoTransform {
177+
pub fn world_from_view(cam: &re_log_types::Extrinsics) -> macaw::IsoTransform {
178178
use re_log_types::CameraSpaceConvention;
179179

180180
let rotation = Quat::from_slice(&cam.rotation);
@@ -192,15 +192,15 @@ pub mod cam {
192192
macaw::IsoTransform::from_rotation_translation(rotation, translation)
193193
}
194194

195-
pub fn view_from_world(cam: &re_log_types::Camera) -> macaw::IsoTransform {
195+
pub fn view_from_world(cam: &re_log_types::Extrinsics) -> macaw::IsoTransform {
196196
world_from_view(cam).inverse()
197197
}
198198

199199
/// Projects image coordinates into world coordinates
200200
pub fn world_from_image(cam: &re_log_types::Camera) -> Option<glam::Affine3A> {
201201
cam.intrinsics.map(|intrinsics| {
202-
let intrinsics = glam::Mat3::from_cols_array_2d(&intrinsics);
203-
world_from_view(cam)
202+
let intrinsics = glam::Mat3::from_cols_array_2d(&intrinsics.intrinsics_matrix);
203+
world_from_view(&cam.extrinsics)
204204
* Affine3A::from_scale([1.0, -1.0, -1.0].into()) // negate Y and Z here here because image space and view space are different.
205205
* Affine3A::from_mat3(intrinsics.inverse())
206206
})
@@ -209,10 +209,10 @@ pub mod cam {
209209
/// Projects world coordinates onto 2D image coordinates
210210
pub fn image_from_world(cam: &re_log_types::Camera) -> Option<glam::Affine3A> {
211211
cam.intrinsics.map(|intrinsics| {
212-
let intrinsics = glam::Mat3::from_cols_array_2d(&intrinsics);
212+
let intrinsics = glam::Mat3::from_cols_array_2d(&intrinsics.intrinsics_matrix);
213213
Affine3A::from_mat3(intrinsics)
214214
* Affine3A::from_scale([1.0, -1.0, -1.0].into()) // negate Y and Z here here because image space and view space are different.
215-
* view_from_world(cam)
215+
* view_from_world(&cam.extrinsics)
216216
})
217217
}
218218

@@ -227,7 +227,7 @@ pub mod cam {
227227
/// Unproject a 2D image coordinate as a ray in 3D space
228228
pub fn unproject_as_ray(cam: &re_log_types::Camera, pos2d: Vec2) -> Option<Ray3> {
229229
world_from_image(cam).map(|world_from_pixel| {
230-
let origin = Vec3::from_slice(&cam.position);
230+
let origin = Vec3::from_slice(&cam.extrinsics.position);
231231
let stop = world_from_pixel.transform_point3(pos2d.extend(1.0));
232232
let dir = (stop - origin).normalize();
233233
Ray3::from_origin_dir(origin, dir)

crates/re_viewer/src/ui/data_ui.rs

+20-13
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,8 @@ pub(crate) fn ui_data_vec(ui: &mut egui::Ui, data_vec: &DataVec) -> egui::Respon
371371

372372
fn ui_camera(ui: &mut egui::Ui, cam: &Camera) -> egui::Response {
373373
let Camera {
374-
rotation,
375-
position,
376-
camera_space_convention,
374+
extrinsics,
377375
intrinsics,
378-
resolution,
379376
target_space,
380377
} = cam;
381378
ui.vertical(|ui| {
@@ -385,6 +382,12 @@ fn ui_camera(ui: &mut egui::Ui, cam: &Camera) -> egui::Response {
385382
.striped(true)
386383
.num_columns(2)
387384
.show(ui, |ui| {
385+
let Extrinsics {
386+
rotation,
387+
position,
388+
camera_space_convention,
389+
} = extrinsics;
390+
388391
ui.label("rotation");
389392
ui.monospace(format!("{rotation:?}"));
390393
ui.end_row();
@@ -397,15 +400,19 @@ fn ui_camera(ui: &mut egui::Ui, cam: &Camera) -> egui::Response {
397400
ui.monospace(format!("{camera_space_convention:?}"));
398401
ui.end_row();
399402

400-
ui.label("intrinsics");
401-
if let Some(intrinsics) = intrinsics {
402-
ui_intrinsics(ui, intrinsics);
403+
if let Some(Intrinsics {
404+
intrinsics_matrix,
405+
resolution,
406+
}) = intrinsics
407+
{
408+
ui.label("intrinsics");
409+
ui_intrinsics_matrix(ui, intrinsics_matrix);
410+
ui.end_row();
411+
412+
ui.label("resolution");
413+
ui.monospace(format!("{resolution:?}"));
414+
ui.end_row();
403415
}
404-
ui.end_row();
405-
406-
ui.label("resolution");
407-
ui.monospace(format!("{resolution:?}"));
408-
ui.end_row();
409416

410417
ui.label("target_space");
411418
if let Some(target_space) = target_space {
@@ -418,7 +425,7 @@ fn ui_camera(ui: &mut egui::Ui, cam: &Camera) -> egui::Response {
418425
.response
419426
}
420427

421-
fn ui_intrinsics(ui: &mut egui::Ui, intrinsics: &[[f32; 3]; 3]) {
428+
fn ui_intrinsics_matrix(ui: &mut egui::Ui, intrinsics: &[[f32; 3]; 3]) {
422429
egui::Grid::new("intrinsics").num_columns(3).show(ui, |ui| {
423430
ui.monospace(intrinsics[0][0].to_string());
424431
ui.monospace(intrinsics[1][0].to_string());

crates/re_viewer/src/ui/view3d.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ fn show_projections_from_2d_space(
459459
}
460460
}
461461
let length = if let Some(hit_pos) = hit_pos {
462-
hit_pos.distance(Vec3::from_slice(&cam.position))
462+
hit_pos.distance(Vec3::from_slice(&cam.extrinsics.position))
463463
} else {
464464
4.0 * state.scene_bbox.half_size().length() // should be long enough
465465
};

crates/re_viewer/src/ui/view3d/eye.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ pub struct Eye {
1717

1818
impl Eye {
1919
pub fn from_camera(cam: &re_log_types::Camera) -> Eye {
20-
let fov_y = if let (Some(intrinsis), Some([_w, h])) = (cam.intrinsics, cam.resolution) {
21-
2.0 * (0.5 * h / intrinsis[1][1]).atan()
20+
let fov_y = if let Some(intrinsis) = cam.intrinsics {
21+
intrinsis.fov_y()
2222
} else {
2323
DEFAULT_FOV_Y
2424
};
2525

2626
Self {
27-
world_from_view: crate::misc::cam::world_from_view(cam),
27+
world_from_view: crate::misc::cam::world_from_view(&cam.extrinsics),
2828
fov_y,
2929
}
3030
}

crates/re_viewer/src/ui/view3d/scene.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ impl Scene {
247247

248248
let instance_id = InstanceIdHash::from_props(props);
249249

250-
let world_from_view = crate::misc::cam::world_from_view(camera);
250+
let world_from_view = crate::misc::cam::world_from_view(&camera.extrinsics);
251251

252252
let dist_to_eye = eye_camera_plane.distance(world_from_view.translation());
253253
let color = object_color(ctx, props);
@@ -281,11 +281,11 @@ impl Scene {
281281
}
282282

283283
if ctx.options.show_camera_axes_in_3d {
284-
let world_from_view = crate::misc::cam::world_from_view(camera);
285284
let center = world_from_view.translation();
286285
let radius = dist_to_eye * line_radius_from_distance * 2.0;
287286

288287
for (axis_index, dir) in camera
288+
.extrinsics
289289
.camera_space_convention
290290
.axis_dirs_in_rerun_view_space()
291291
.iter()
@@ -320,10 +320,12 @@ impl Scene {
320320
line_radius: f32,
321321
color: [u8; 4],
322322
) {
323-
if let (Some(world_from_image), Some([w, h])) =
324-
(crate::misc::cam::world_from_image(cam), cam.resolution)
323+
if let (Some(world_from_image), Some(intrinsics)) =
324+
(crate::misc::cam::world_from_image(cam), cam.intrinsics)
325325
{
326-
let world_from_view = crate::misc::cam::world_from_view(cam);
326+
let [w, h] = intrinsics.resolution;
327+
328+
let world_from_view = crate::misc::cam::world_from_view(&cam.extrinsics);
327329

328330
// At what distance do we end the frustum?
329331
let d = scene_bbox.size().length() * 0.3;

rerun_py/src/python_bridge.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ fn convert_color(color: Vec<u8>) -> PyResult<[u8; 4]> {
409409
fn log_camera(
410410
obj_path: &str,
411411
resolution: [f32; 2],
412-
intrinsics: [[f32; 3]; 3],
412+
intrinsics_matrix: [[f32; 3]; 3],
413413
rotation_q: re_log_types::Quaternion,
414414
position: [f32; 3],
415415
camera_space_convention: &str,
@@ -436,12 +436,20 @@ fn log_camera(
436436
None
437437
};
438438

439-
let camera = re_log_types::Camera {
439+
let extrinsics = re_log_types::Extrinsics {
440440
rotation: rotation_q,
441441
position,
442-
intrinsics: Some(intrinsics),
443-
resolution: Some(resolution),
444442
camera_space_convention: convention,
443+
};
444+
445+
let intrinsics = re_log_types::Intrinsics {
446+
intrinsics_matrix,
447+
resolution,
448+
};
449+
450+
let camera = re_log_types::Camera {
451+
extrinsics,
452+
intrinsics: Some(intrinsics),
445453
target_space,
446454
};
447455

scripts/lint.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def lint_file(filepath, args):
9999
root_dirpath = os.path.abspath(f'{script_dirpath}/..')
100100
os.chdir(root_dirpath)
101101

102-
exclude_dirs = set(['env', 'venv', 'target'])
102+
exclude_dirs = set(['env', 'venv', 'target', 'target_wasm'])
103103

104104
exclude_paths = set([
105105
'./CONTRIBUTING.md',

0 commit comments

Comments
 (0)