"""
Scene Module
============
This module defines the top-level data structures for representing a complete
driving scene, including the ego vehicle, its sensors, and all actors
(other objects) in the environment.
The ``Scene`` class serves as the root container for all scene data and is
the primary entry point for working with the Data API.
Classes
-------
Sensors
Collection of all sensors mounted on the ego vehicle.
EgoVehicle
The autonomous/recording vehicle with its trajectory and sensor suite.
Scene
Complete scene representation including ego vehicle and all actors.
Architecture
------------
The scene data model follows a hierarchical structure::
Scene
├── EgoVehicle
│ ├── trajectory: WorldTrajectory
│ └── sensors: Sensors
│ ├── cameras: list[Camera]
│ ├── lidars: list[Lidar] (optional)
│ └── radars: list[Radar] (optional)
└── actors: list[Actor]
Example
-------
Creating a complete scene::
from mimic_data_api.scene import Scene, EgoVehicle, Sensors
from mimic_data_api.camera import Camera
from mimic_data_api.actor import Actor
scene = Scene(
ego_vehicle=EgoVehicle(
trajectory=ego_trajectory,
sensors=Sensors(
cameras=[front_camera, rear_camera],
lidars=[top_lidar],
radars=[front_radar]
)
),
actors=[vehicle_1, pedestrian_1, cyclist_1]
)
"""
from pydantic import BaseModel, Field
from mimic_data_api.camera import Camera
from mimic_data_api.actor import Actor
from mimic_data_api.lidar import Lidar
from mimic_data_api.radar import Radar
from mimic_data_api.trajectory_numpy import WorldTrajectory
from mimic_data_api.unstructured.pointclouds import ColorizedPointCloud
class BackgroundPointCloudPaths(BaseModel):
lidar_accumulated_pcd_filepath: str | None = None
lidar_accumulated_pcd: ColorizedPointCloud | None = None
colmap_pcd_filepath: str | None = None
colmap_pcd: ColorizedPointCloud | None = None
background_init_pcd_filepath: str | None = None
background_init_pcd: ColorizedPointCloud | None = None
background_road_pcd_filepath: str | None = None
background_road_pcd: ColorizedPointCloud | None = None
background_non_road_pcd_filepath: str | None = None
background_non_road_pcd: ColorizedPointCloud | None = None
[docs]
class Sensors(BaseModel):
"""Collection of sensors mounted on the ego vehicle.
This class aggregates all perception sensors attached to the ego vehicle,
providing a unified interface for accessing camera, LiDAR, and radar data.
Attributes
----------
cameras : list of Camera
List of camera sensors. At least one camera is required.
lidars : list of Lidar
List of LiDAR sensors. At least one LiDAR is required.
radars : list of Radar or None
Optional list of radar sensors. ``None`` if no radars are present.
Note
----
Every scene must contain at least one camera and one LiDAR. Radar sensors
remain optional.
Example
-------
>>> sensors = Sensors(
... cameras=[front_cam, rear_cam, left_cam, right_cam],
... lidars=[top_lidar, front_lidar],
... radars=[front_radar, rear_radar]
... )
>>> print(f"Number of cameras: {len(sensors.cameras)}")
Number of cameras: 4
"""
cameras: list[Camera] = Field(min_length=1)
lidars: list[Lidar] = Field(min_length=1)
radars: list[Radar] | None = None
[docs]
class EgoVehicle(BaseModel):
"""The autonomous or recording vehicle in the scene.
Represents the vehicle carrying the sensor suite, including its
trajectory through the scene and all mounted sensors.
Attributes
----------
trajectory : WorldTrajectory
Time-series of ego vehicle poses in world coordinates.
Defines the vehicle's position and orientation over time.
sensors : Sensors
Collection of all sensors mounted on the ego vehicle.
Note
----
The ego vehicle's trajectory defines the reference frame for sensor
data alignment. All sensor extrinsics are defined relative to the
vehicle body frame, and the trajectory transforms from body frame
to world frame.
Coordinate conventions:
- **Vehicle Frame**: X-forward, Y-left, Z-up (FLU)
- **World Frame**: Typically ENU (East-North-Up) or local tangent plane
Example
-------
>>> ego = EgoVehicle(
... trajectory=WorldTrajectory(
... timestamps=np.arange(0, 10, 0.1),
... object2world=poses # (100, 4, 4) array
... ),
... sensors=Sensors(cameras=[camera], lidars=[lidar])
... )
"""
trajectory: WorldTrajectory
sensors: Sensors
[docs]
class Scene(BaseModel):
"""Complete driving scene representation.
The ``Scene`` class is the top-level container for all data in a driving
scenario. It includes the ego vehicle (with sensors and trajectory) and
all detected/annotated actors in the environment.
This class serves as the primary interface for loading, saving, and
manipulating scene data in the Data API.
Attributes
----------
ego_vehicle : EgoVehicle
The autonomous/recording vehicle with trajectory and sensors.
actors : list of Actor
List of all other objects (vehicles, pedestrians, cyclists, etc.)
present in the scene.
world_offset : list of float or None
Constant (x, y, z) translation subtracted from every global
``object2world`` translation (e.g. world re-centering during
preprocessing); add it back to recover the source world frame.
``None`` means no offset was applied.
Note
----
The scene data structure is designed for:
- **Serialization**: Easy JSON export/import for data exchange
- **Simulation**: Providing ground truth for rendering and replay
- **Analysis**: Querying and filtering scene elements
- **Editing**: Adding, removing, or modifying scene components
Example
-------
Creating and serializing a scene::
>>> scene = Scene(
... ego_vehicle=ego,
... actors=[car_1, car_2, pedestrian_1]
... )
>>> # Serialize to JSON
>>> json_data = scene.model_dump_json(indent=2)
>>> # Load from JSON
>>> loaded_scene = Scene.model_validate_json(json_data)
Accessing scene components::
>>> # Get all camera data
>>> cameras = scene.ego_vehicle.sensors.cameras
>>> # Get all dynamic actors
>>> dynamic_actors = [a for a in scene.actors if a.dynamic]
>>> # Get ego trajectory timestamps
>>> timestamps = scene.ego_vehicle.trajectory.timestamps
"""
ego_vehicle: EgoVehicle
actors: list[Actor]
world_offset: list[float] | None = None
"""(x, y, z) offset subtracted from global object2world translations; see class docstring."""
background_pcd_paths: BackgroundPointCloudPaths | None = None
base_folderpath: str | None = Field(
default=None,
exclude=True,
description=(
"Path where the scene.json file is loaded from. "
"Excluded from serialization; useful for resolving relative paths."
),
)