Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helpers for perspective cameras #5238

Merged
merged 12 commits into from
Feb 22, 2024
1 change: 1 addition & 0 deletions crates/re_types/definitions/rerun/archetypes/pinhole.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace rerun.archetypes;
/// Camera perspective projection (a.k.a. intrinsics).
///
/// \example pinhole_simple title="Simple Pinhole Camera" image="https://static.rerun.io/pinhole_simple/9af9441a94bcd9fd54e1fea44fb0c59ff381a7f2/1200w.png"
/// \example pinhole_perspective title="Perspective Pinhole Camera" image="https://static.rerun.io/pinhole_perspective/d0bd02a0cf354a5c8eafb79a84fe8674335cab98/1200w.png"
table Pinhole (
"attr.rust.derive": "PartialEq"
) {
Expand Down
33 changes: 32 additions & 1 deletion crates/re_types/src/archetypes/pinhole.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/re_types/src/archetypes/pinhole_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ impl Pinhole {
.with_resolution(resolution)
}

/// Creates a pinhole from the camera vertical field of view and aspect ratio.
teh-cmc marked this conversation as resolved.
Show resolved Hide resolved
///
/// Assumes the principal point to be in the middle of the sensor.
#[inline]
pub fn from_fov_and_aspect_ratio(fov_y: f32, aspect_ratio: f32) -> Self {
let focal_length_y = 0.5 / (fov_y * 0.5).max(f32::EPSILON).tan();
let focal_length = [focal_length_y, focal_length_y];
Self::from_focal_length_and_resolution(focal_length, [aspect_ratio, 1.0])
}

/// Field of View on the Y axis, i.e. the angle between top and bottom (in radians).
#[inline]
pub fn fov_y(&self) -> Option<f32> {
Expand Down
21 changes: 21 additions & 0 deletions docs/code-examples/all/pinhole_perspective.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Logs a point cloud and a perspective camera looking at it.

#include <rerun.hpp>

int main() {
const auto rec = rerun::RecordingStream("rerun_example_pinhole_perspective");
rec.spawn().exit_on_failure();

const float fov = 0.7853982f;
const float aspect_ratio = 1.7777778f;
rec.log(
"world/cam",
rerun::Pinhole::from_fov_and_aspect_ratio(fov, aspect_ratio)
teh-cmc marked this conversation as resolved.
Show resolved Hide resolved
.with_camera_xyz(rerun::components::ViewCoordinates::RUB)
);

rec.log(
"world/points",
rerun::Points3D({{0.0, 0.0, -0.5}, {0.1, 0.1, -0.5}, {-0.1, -0.1, -0.5}})
);
}
8 changes: 8 additions & 0 deletions docs/code-examples/all/pinhole_perspective.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Logs a point cloud and a perspective camera looking at it."""
import rerun as rr

rr.init("rerun_example_pinhole_perspective", spawn=True)

rr.log("world/cam", rr.Pinhole(fov_y=0.7853982, aspect_ratio=1.7777778, camera_xyz=rr.ViewCoordinates.RUB))

rr.log("world/points", rr.Points3D([(0.0, 0.0, -0.5), (0.1, 0.1, -0.5), (-0.1, -0.1, -0.5)]))
20 changes: 20 additions & 0 deletions docs/code-examples/all/pinhole_perspective.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! Logs a point cloud and a perspective camera looking at it.

fn main() -> Result<(), Box<dyn std::error::Error>> {
let rec = rerun::RecordingStreamBuilder::new("rerun_example_pinhole_perspective").spawn()?;

let fov = 0.7853982;
let aspect_ratio = 1.7777778;
rec.log(
"world/cam",
&rerun::Pinhole::from_fov_and_aspect_ratio(fov, aspect_ratio)
teh-cmc marked this conversation as resolved.
Show resolved Hide resolved
.with_camera_xyz(rerun::components::ViewCoordinates::RUB),
)?;

rec.log(
"world/points",
&rerun::Points3D::new([(0.0, 0.0, -0.5), (0.1, 0.1, -0.5), (-0.1, -0.1, -0.5)]),
)?;

Ok(())
}
16 changes: 15 additions & 1 deletion docs/content/reference/types/archetypes/pinhole.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 40 additions & 1 deletion rerun_cpp/src/rerun/archetypes/pinhole.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions rerun_cpp/src/rerun/archetypes/pinhole_ext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ namespace rerun {
#ifdef CODEGEN
// <CODEGEN_COPY_TO_HEADER>

#include <cmath>
#include <limits>

/// Creates a pinhole from the camera focal length and resolution, both specified in pixels.
///
/// The focal length is the diagonal of the projection matrix.
Expand All @@ -28,6 +31,18 @@ namespace rerun {
return from_focal_length_and_resolution({focal_length, focal_length}, resolution);
}

/// Creates a pinhole from the camera vertical field of view and aspect ratio.
///
/// Assumes the principal point to be in the middle of the sensor.
teh-cmc marked this conversation as resolved.
Show resolved Hide resolved
static Pinhole from_fov_and_aspect_ratio(float fov_y, float aspect_ratio) {
const float EPSILON = std::numeric_limits<float>::epsilon();
const float focal_length_y = 0.5f / std::tan(std::max(fov_y * 0.5f, EPSILON));
return from_focal_length_and_resolution(
{focal_length_y, focal_length_y},
{aspect_ratio, 1.0}
);
}

/// Pixel resolution (usually integers) of child image space. Width and height.
///
/// `image_from_camera` project onto the space spanned by `(0,0)` and `resolution - 1`.
Expand Down
2 changes: 1 addition & 1 deletion rerun_cpp/src/rerun/c/rerun.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 22 additions & 2 deletions rerun_py/rerun_sdk/rerun/archetypes/pinhole.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions rerun_py/rerun_sdk/rerun/archetypes/pinhole_ext.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import math
from typing import Any, cast

import numpy.typing as npt
Expand All @@ -23,6 +24,8 @@ def __init__(
height: int | float | None = None,
focal_length: float | npt.ArrayLike | None = None,
principal_point: npt.ArrayLike | None = None,
fov_y: float | None = None,
aspect_ratio: float | None = None,
) -> None:
"""
Create a new instance of the Pinhole archetype.
Expand Down Expand Up @@ -78,6 +81,11 @@ def __init__(
Width of the image in pixels.
height:
Height of the image in pixels.
fov_y:
Vertical field of view.
aspect_ratio
Aspect ratio.
teh-cmc marked this conversation as resolved.
Show resolved Hide resolved

"""

with catch_and_log_exceptions(context=self.__class__.__name__):
Expand All @@ -88,6 +96,11 @@ def __init__(

# TODO(andreas): Use a union type for the Pinhole component instead ~Zof converting to a matrix here
if image_from_camera is None:
if fov_y is not None and aspect_ratio is not None:
EPSILON = 1.19209e-07
focal_length = focal_length = 0.5 / math.tan(max(fov_y * 0.5, EPSILON))
resolution = [aspect_ratio, 1.0]

if resolution is not None:
res_vec = Vec2D(resolution)
width = cast(float, res_vec.xy[0])
Expand Down Expand Up @@ -132,6 +145,8 @@ def __init__(
_send_warning_or_raise("Both image_from_camera and focal_length set", 1)
if principal_point is not None:
_send_warning_or_raise("Both image_from_camera and principal_point set", 1)
if fov_y is not None or aspect_ratio is not None:
_send_warning_or_raise("Both image_from_camera and fov_y or aspect_ratio set", 1)

self.__attrs_init__(image_from_camera=image_from_camera, resolution=resolution, camera_xyz=camera_xyz)
return
Expand Down
Loading