Skip to content

OrientedBox2D

SUMMARY

An oriented bounding box in 2D space.

python
from telekinesis import datatypes
oriented_box2d = datatypes.OrientedBox2D([0.5, 0.5, 0.5, 0.5, 30.0])
API Reference
Complete API documentation for OrientedBox2D, including parameters, attributes, and methods.
View Reference →

Parameters

ParameterTypeDefaultDescription
datanp.ndarray | list | tupleRequiredBox coordinates [cx, cy, width, height, yaw_deg] (center, size, and yaw rotation in degrees).

Raises

ExceptionCondition
TypeErrordata can't be converted into a uniform float32 array (e.g. ragged nested lists)
ValueErrordata is not rank-1 (e.g. a (N, 5) batch — use OrientedBoxes2D instead)
ValueErrordata does not have exactly 5 elements
ValueErrorAny element is non-finite (NaN/Inf)
ValueErrorwidth or height (data[2], data[3]) is negative

A zero-sized width/height is allowed but logs a warning (area will be 0). yaw_deg is otherwise unconstrained — any finite degree value is accepted, including values outside [0, 360).

Attributes

AttributeTypeDescription
datanp.ndarrayDefensive copy of the underlying (5,) array [cx, cy, width, height, yaw_deg]. Reading it returns a copy; writing re-validates the same way as construction.
shapetuple[int, ...]Always (5,).
ndimintAlways 1.
dtypenp.dtypeAlways float32.
sizeintAlways 5.
shape_spectuple[int, ...]Class-level shape spec (5,).
centernp.ndarray[cx, cy]data[:2] directly.
dimensionsnp.ndarray[width, height] (data[2:4]), non-negative, unaffected by rotation.
rotationnp.ndarray, shape (1,)[yaw_deg]data[4:5], the yaw rotation in degrees.
areanp.ndarray (scalar)width * height (rotation-invariant).

Methods

MethodTypeDescription
OrientedBox2D.coerce(value)OrientedBox2DConverts array-like data into an OrientedBox2D. Accepts a [cx, cy, width, height, yaw_deg] array-like of shape (5,), checked the same way as the constructor. If value is already an OrientedBox2D, it is returned unchanged.
OrientedBox2D.from_xywh(data)OrientedBox2DBuilds an OrientedBox2D from [x_min, y_min, width, height, yaw_deg] coordinates (shape (5,)), converting the position and size into the native center-based format. yaw_deg passes through unchanged.
OrientedBox2D.from_xyxy(data)OrientedBox2DBuilds an OrientedBox2D from [x_min, y_min, x_max, y_max, yaw_deg] corner coordinates (shape (5,)), converting them into the native center-based format. yaw_deg passes through unchanged.
as_xywh()np.ndarrayReturns this box's coordinates as [x_min, y_min, width, height, yaw_deg]. yaw_deg passes through unchanged.
as_xyxy()np.ndarrayReturns this box's coordinates as [x_min, y_min, x_max, y_max, yaw_deg]. yaw_deg passes through unchanged.
to_numpy(copy=True)np.ndarrayReturns the box's coordinates as a plain array. With the default copy=True you get an independent copy; pass copy=False to get a direct reference to the internal array instead, so mutating it also mutates the OrientedBox2D.
copy()OrientedBox2DReturns a new, independent OrientedBox2D with the same coordinates.

Operators

OperationBehavior
box == otherTrue only if other is also an OrientedBox2D with element-equal data. False for anything else.
len(box)Returns 5 — the length of the coordinate vector, not a box count (a single box is conceptually one item).
box[i]Raises TypeErrorOrientedBox2D defines no __getitem__, so a single box isn't indexable like a batch. Use .data[i] for raw coordinate access instead.
for x in boxRaises TypeError — with no __getitem__/__iter__, a single OrientedBox2D isn't iterable.
np.asarray(box)Returns a copy of data as an np.ndarray; NumPy functions accept an OrientedBox2D directly.

Visualization

python
import rerun as rr

# Your code block
# ....

rr.init("oriented_box2d_example", spawn=True)
datatypes.visualize(oriented_box2d, entity_path="/oriented_box2d", label="OrientedBox2D")

Example

python
"""Demonstrates the Telekinesis OrientedBox2D datatype."""

import time

import numpy as np
import rerun as rr
from loguru import logger

from telekinesis import datatypes

def oriented_box2d_example():
    """Demonstrate creation, inspection, operations, visualization, and serialization."""

    # ======================= Create ============================================
    # OrientedBox2D format is CXCYWH = [cx, cy, width, height] + rotation [yaw_deg]
    coords = [0.5, 0.5, 0.5, 0.5, 30.0]
    oriented_box2d = datatypes.OrientedBox2D(coords)
    logger.info(f"Created OrientedBox2D: {oriented_box2d}")

    xyxy_coords = [1.0, 1.5, 3.5, 3.0, 45.0]
    oriented_box2d_from_xyxy = datatypes.OrientedBox2D.from_xyxy(xyxy_coords)
    logger.info(f"OrientedBox2D created from xyxy format: {oriented_box2d_from_xyxy}")

    xywh_coords = [1.0, 1.5, 2.5, 1.5, 45.0]
    oriented_box2d_from_xywh = datatypes.OrientedBox2D.from_xywh(xywh_coords)
    logger.info(f"OrientedBox2D created from xywh format: {oriented_box2d_from_xywh}")

    # ======================= Inspect ===========================================
    logger.info(f"data={oriented_box2d.data}")
    logger.info(f"dtype={oriented_box2d.dtype}")
    logger.info(f"ndim={oriented_box2d.ndim}")
    logger.info(f"shape={oriented_box2d.shape}")
    logger.info(f"size={oriented_box2d.size}")
    logger.info(f"center={oriented_box2d.center}")
    logger.info(f"dimensions={oriented_box2d.dimensions}")
    logger.info(f"area={oriented_box2d.area}")
    logger.info(f"rotation={oriented_box2d.rotation}")

    # ======================= Operations =========================================
    updated_coords = [2.0, 2.0, 1.5, 3.0, 45.0]
    oriented_box2d.data = updated_coords
    logger.info(f"Updated OrientedBox2D: {oriented_box2d}")

    xyxy_view = oriented_box2d.as_xyxy()
    logger.info(f"OrientedBox2D converted to xyxy format: {xyxy_view}")

    xywh_view = oriented_box2d.as_xywh()
    logger.info(f"OrientedBox2D converted to xywh format: {xywh_view}")

    oriented_box2d_copy = oriented_box2d.copy()
    logger.info(f"Copied OrientedBox2D: {oriented_box2d_copy}")

    # Returns the internal data as a NumPy array. If copy=True, returns a copy; otherwise, returns a view.
    oriented_box2d_numpy = oriented_box2d.to_numpy(copy=False)
    logger.info(f"NumPy OrientedBox2D:\n{oriented_box2d_numpy}")

    # Translate, scale, and rotate by operating on the underlying NumPy array directly.
    translation = [1.0, 1.0]
    translated_data = oriented_box2d.data.copy()
    translated_data[:2] += translation
    translated_oriented_box2d = datatypes.OrientedBox2D(translated_data)
    logger.info(f"Translated OrientedBox2D: {translated_oriented_box2d}")

    scale_factors = [1.5, 1.5]
    scaled_data = oriented_box2d.data.copy()
    scaled_data[2:4] *= np.asarray(scale_factors, dtype=np.float32)
    scaled_oriented_box2d = datatypes.OrientedBox2D(scaled_data)
    logger.info(f"Scaled OrientedBox2D: {scaled_oriented_box2d}")

    rotation_delta_deg = 15.0
    rotated_data = oriented_box2d.data.copy()
    rotated_data[4] += rotation_delta_deg
    rotated_oriented_box2d = datatypes.OrientedBox2D(rotated_data)
    logger.info(f"Rotated OrientedBox2D: {rotated_oriented_box2d}")

    numpy_array = np.asarray(oriented_box2d)
    logger.info(f"NumPy array via __array__: {numpy_array}")

    # ======================= Visualize =========================================
    rr.init("oriented_box2d_example", spawn=True)
    datatypes.visualize(oriented_box2d, entity_path="/oriented_box2d/updated", label="Updated Oriented Box2D")
    datatypes.visualize(
        translated_oriented_box2d,
        entity_path="/oriented_box2d/translated",
        label="Translated Oriented Box2D",
    )
    datatypes.visualize(
        scaled_oriented_box2d, entity_path="/oriented_box2d/scaled", label="Scaled Oriented Box2D"
    )
    datatypes.visualize(
        rotated_oriented_box2d, entity_path="/oriented_box2d/rotated", label="Rotated Oriented Box2D"
    )

    # ======================= Serialize / Deserialize ===========================
    start = time.perf_counter()
    serialized = datatypes.serialize(oriented_box2d)
    serialization_ms = (time.perf_counter() - start) * 1000

    start = time.perf_counter()
    deserialized = datatypes.deserialize(serialized)["param_0"]
    deserialization_ms = (time.perf_counter() - start) * 1000

    logger.info(f"Deserialized OrientedBox2D: {deserialized}")
    logger.info(f"Round-trip successful: {oriented_box2d == deserialized}")
    logger.info(f"Serialization time: {serialization_ms:.3f} ms")
    logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")


if __name__ == "__main__":
    oriented_box2d_example()