Skip to content

Commit 0154535

Browse files
authored
Adds an example showcasing different camera types (#565)
# Description This MR fixes some of the docstrings related to the tile-rendering camera. Additionally, it adds a demo script showcasing the images obtained through the different camera implementations in IsaacLab. ## Type of change - New feature (non-breaking change which adds functionality) - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have run all the tests with `./isaaclab.sh --test` and they pass - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
1 parent 3ef7e67 commit 0154535

File tree

10 files changed

+473
-114
lines changed

10 files changed

+473
-114
lines changed

docs/source/api/lab/omni.isaac.lab.sensors.rst

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
Camera
1919
CameraData
2020
CameraCfg
21+
TiledCamera
22+
TiledCameraCfg
2123
ContactSensor
2224
ContactSensorData
2325
ContactSensorCfg
@@ -29,8 +31,6 @@
2931
RayCasterCfg
3032
RayCasterCamera
3133
RayCasterCameraCfg
32-
TiledCamera
33-
TiledCameraCfg
3434

3535
Sensor Base
3636
-----------
@@ -61,6 +61,20 @@ USD Camera
6161
:show-inheritance:
6262
:exclude-members: __init__, class_type
6363

64+
Tile-Rendered USD Camera
65+
------------------------
66+
67+
.. autoclass:: TiledCamera
68+
:members:
69+
:inherited-members:
70+
:show-inheritance:
71+
72+
.. autoclass:: TiledCameraCfg
73+
:members:
74+
:inherited-members:
75+
:show-inheritance:
76+
:exclude-members: __init__, class_type
77+
6478
Contact Sensor
6579
--------------
6680

@@ -80,7 +94,6 @@ Contact Sensor
8094
:show-inheritance:
8195
:exclude-members: __init__, class_type
8296

83-
8497
Frame Transformer
8598
-----------------
8699

@@ -137,17 +150,3 @@ Ray-Cast Camera
137150
:inherited-members:
138151
:show-inheritance:
139152
:exclude-members: __init__, class_type
140-
141-
Tiled Rendering
142-
---------------
143-
144-
.. autoclass:: TiledCamera
145-
:members:
146-
:inherited-members:
147-
:show-inheritance:
148-
149-
.. autoclass:: TiledCameraCfg
150-
:members:
151-
:inherited-members:
152-
:show-inheritance:
153-
:exclude-members: __init__, class_type

source/extensions/omni.isaac.lab/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
# Note: Semantic Versioning is used: https://semver.org/
4-
version = "0.18.1"
4+
version = "0.18.2"
55

66
# Description
77
title = "Isaac Lab framework for Robot Learning"

source/extensions/omni.isaac.lab/docs/CHANGELOG.rst

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
Changelog
22
---------
33

4+
0.18.2 (2024-06-25)
5+
~~~~~~~~~~~~~~~~~~~
6+
7+
Changed
8+
^^^^^^^
9+
10+
* Moved the configuration for tile-rendered camera into its own file named ``tiled_camera_cfg.py``.
11+
This makes it easier to follow where the configuration is located and how it is related to the class.
12+
13+
414
0.18.1 (2024-06-25)
515
~~~~~~~~~~~~~~~~~~~
616

717
Changed
818
^^^^^^^
919

10-
* Ensured that a parity between class and its configuration class is explicitly visible in the :class:`omni.isaac.lab.envs`
11-
module. This makes it easier to follow where definitions are located and how they are related. This should not be
12-
a breaking change as the classes are still accessible through the same module.
20+
* Ensured that a parity between class and its configuration class is explicitly visible in the
21+
:mod:`omni.isaac.lab.envs` module. This makes it easier to follow where definitions are located and how
22+
they are related. This should not be a breaking change as the classes are still accessible through the same module.
1323

1424

1525
0.18.0 (2024-06-13)

source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/camera/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"""Sub-module for camera wrapper around USD camera prim."""
77

88
from .camera import Camera
9-
from .camera_cfg import CameraCfg, TiledCameraCfg
9+
from .camera_cfg import CameraCfg
1010
from .camera_data import CameraData
1111
from .tiled_camera import TiledCamera
12+
from .tiled_camera_cfg import TiledCameraCfg
1213
from .utils import * # noqa: F401, F403

source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/camera/camera_cfg.py

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from ..sensor_base_cfg import SensorBaseCfg
1313
from .camera import Camera
14-
from .tiled_camera import TiledCamera
1514

1615

1716
@configclass
@@ -107,64 +106,3 @@ class OffsetCfg:
107106
If True, instance segmentation is converted to an image where instance IDs are mapped to colors.
108107
and returned as a ``uint8`` 4-channel array. If False, the output is returned as a ``int32`` array.
109108
"""
110-
111-
112-
@configclass
113-
class TiledCameraCfg(SensorBaseCfg):
114-
"""Configuration for a tiled rendering camera sensor."""
115-
116-
@configclass
117-
class OffsetCfg:
118-
"""The offset pose of the sensor's frame from the sensor's parent frame."""
119-
120-
pos: tuple[float, float, float] = (0.0, 0.0, 0.0)
121-
"""Translation w.r.t. the parent frame. Defaults to (0.0, 0.0, 0.0)."""
122-
123-
rot: tuple[float, float, float, float] = (1.0, 0.0, 0.0, 0.0)
124-
"""Quaternion rotation (w, x, y, z) w.r.t. the parent frame. Defaults to (1.0, 0.0, 0.0, 0.0)."""
125-
126-
convention: Literal["opengl", "ros", "world"] = "ros"
127-
"""The convention in which the frame offset is applied. Defaults to "ros".
128-
129-
- ``"opengl"`` - forward axis: ``-Z`` - up axis: ``+Y`` - Offset is applied in the OpenGL (Usd.Camera) convention.
130-
- ``"ros"`` - forward axis: ``+Z`` - up axis: ``-Y`` - Offset is applied in the ROS convention.
131-
- ``"world"`` - forward axis: ``+X`` - up axis: ``+Z`` - Offset is applied in the World Frame convention.
132-
133-
"""
134-
135-
class_type: type = TiledCamera
136-
137-
offset: OffsetCfg = OffsetCfg()
138-
"""The offset pose of the sensor's frame from the sensor's parent frame. Defaults to identity.
139-
140-
Note:
141-
The parent frame is the frame the sensor attaches to. For example, the parent frame of a
142-
camera at path ``/World/envs/env_0/Robot/Camera`` is ``/World/envs/env_0/Robot``.
143-
"""
144-
145-
spawn: PinholeCameraCfg | FisheyeCameraCfg | None = MISSING
146-
"""Spawn configuration for the asset.
147-
148-
If None, then the prim is not spawned by the asset. Instead, it is assumed that the
149-
asset is already present in the scene.
150-
"""
151-
152-
data_types: list[str] = ["rgb"]
153-
"""List of sensor names/types to enable for the camera. Defaults to ["rgb"].
154-
155-
Please refer to the :class:`Camera` class for a list of available data types.
156-
"""
157-
158-
width: int = MISSING
159-
"""Width of the image in pixels."""
160-
161-
height: int = MISSING
162-
"""Height of the image in pixels."""
163-
164-
return_latest_camera_pose: bool = False
165-
"""Whether to return the latest camera pose when fetching the camera's data. Defaults to False.
166-
167-
If True, the latest camera pose is returned in the camera's data which will slow down performance
168-
due to the use of :class:`XformPrimView`.
169-
If False, the pose of the camera during initialization is returned.
170-
"""

source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/camera/tiled_camera.py

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,27 @@
2727

2828

2929
class TiledCamera(Camera):
30-
r"""The tiled rendering camera sensor for acquiring RGB and depth data.
30+
r"""The tiled rendering based camera sensor for acquiring RGB and depth data.
3131
32-
This class wraps over the `UsdGeom Camera`_ for providing a consistent API for acquiring visual data.
33-
It ensures that the camera follows the ROS convention for the coordinate system.
32+
This class inherits from the :class:`Camera` class but uses the tiled-rendering API from Replicator to acquire
33+
the visual data. Tiled-rendering concatenates the rendered images from multiple cameras into a single image.
34+
This allows for rendering multiple cameras in parallel and is useful for rendering large scenes with multiple
35+
cameras efficiently.
3436
3537
The following sensor types are supported:
3638
3739
- ``"rgb"``: A rendered color image.
3840
- ``"depth"``: An image containing the distance to camera optical center.
3941
40-
.. _USDGeom Camera: https://graphics.pixar.com/usd/docs/api/class_usd_geom_camera.html
42+
.. versionadded:: Isaac Sim 4.0
43+
44+
This feature is available starting from Isaac Sim 4.0. Before this version, the tiled rendering APIs
45+
were not available.
46+
47+
.. attention::
48+
Please note that the fidelity of RGB images may be lower than the standard camera sensor due to the
49+
tiled rendering process. Various ray tracing effects such as reflections, refractions, and shadows may not be
50+
accurately captured in the RGB images. We are currently working on improving the fidelity of the RGB images.
4151
4252
"""
4353

@@ -61,7 +71,9 @@ def __init__(self, cfg: TiledCameraCfg):
6171

6272
def __del__(self):
6373
"""Unsubscribes from callbacks and detach from the replicator registry."""
74+
# unsubscribe from callbacks
6475
SensorBase.__del__(self)
76+
# detach from the replicator registry
6577
self._annotator.detach(self.render_product_paths)
6678

6779
def __str__(self) -> str:
@@ -86,9 +98,10 @@ def reset(self, env_ids: Sequence[int] | None = None):
8698
)
8799
# reset the timestamps
88100
SensorBase.reset(self, env_ids)
101+
# resolve None
89102
if env_ids is None:
90-
env_ids = self._ALL_INDICES
91-
# Reset the frame count
103+
env_ids = slice(None)
104+
# reset the frame count
92105
self._frame[env_ids] = 0
93106

94107
"""
@@ -142,37 +155,38 @@ def _initialize_impl(self):
142155
sensor_prim = UsdGeom.Camera(cam_prim)
143156
self._sensor_prims.append(sensor_prim)
144157

158+
# start the orchestrator (if not already started)
145159
rep.orchestrator._orchestrator._is_started = True
146-
sensor = rep.create.tiled_sensor(
160+
# Create a tiled sensor from the camera prims
161+
rep_sensor = rep.create.tiled_sensor(
147162
cameras=self._view.prim_paths,
148163
camera_resolution=[self.image_shape[1], self.image_shape[0]],
149164
tiled_resolution=self._tiled_image_shape(),
150165
output_types=self.cfg.data_types,
151166
)
152-
render_prod_path = rep.create.render_product(camera=sensor, resolution=self._tiled_image_shape())
167+
# Get render product
168+
render_prod_path = rep.create.render_product(camera=rep_sensor, resolution=self._tiled_image_shape())
153169
if not isinstance(render_prod_path, str):
154170
render_prod_path = render_prod_path.path
155171
self._render_product_paths = [render_prod_path]
172+
# Attach the annotator
156173
self._annotator = rep.AnnotatorRegistry.get_annotator("RtxSensorGpu", device=self.device, do_array_copy=False)
157174
self._annotator.attach(self._render_product_paths)
175+
158176
# Create internal buffers
159177
self._create_buffers()
160178

161-
def _create_annotator_data(self):
162-
raise RuntimeError("Annotator data is not available for the tiled camera sensor.")
163-
164-
def _process_annotator_output(self, name: str, output: Any) -> tuple[torch.tensor, dict | None]:
165-
raise RuntimeError("Annotator data is not available for the tiled camera sensor.")
166-
167179
def _update_buffers_impl(self, env_ids: Sequence[int]):
168180
# Increment frame count
169181
self._frame[env_ids] += 1
182+
170183
# Extract the flattened image buffer
171184
tiled_data_buffer = self._annotator.get_data()
172185
if isinstance(tiled_data_buffer, np.ndarray):
173186
tiled_data_buffer = wp.array(tiled_data_buffer, device=self.device)
174187
else:
175188
tiled_data_buffer = tiled_data_buffer.to(device=self.device)
189+
176190
# The offset is needed when the buffer contains rgb and depth (the buffer has RGB data first and then depth)
177191
offset = self._data.output["rgb"].numel() if "rgb" in self.cfg.data_types else 0
178192
for data_type in self.cfg.data_types:
@@ -193,17 +207,6 @@ def _update_buffers_impl(self, env_ids: Sequence[int]):
193207
Private Helpers
194208
"""
195209

196-
def _tiled_image_shape(self) -> tuple[int, int]:
197-
"""A tuple containing the dimension of the tiled image."""
198-
cols, rows = self._tiling_grid_shape()
199-
return (self.cfg.width * cols, self.cfg.height * rows)
200-
201-
def _tiling_grid_shape(self) -> tuple[int, int]:
202-
"""A tuple containing the tiling grid dimension."""
203-
cols = round(math.sqrt(self._view.count))
204-
rows = math.ceil(self._view.count / cols)
205-
return (cols, rows)
206-
207210
def _check_supported_data_types(self, cfg: TiledCameraCfg):
208211
"""Checks if the data types are supported by the camera."""
209212
if not set(cfg.data_types).issubset(TiledCamera.SUPPORTED_TYPES):
@@ -235,6 +238,25 @@ def _create_buffers(self):
235238
).contiguous()
236239
self._data.output = TensorDict(data_dict, batch_size=self._view.count, device=self.device)
237240

241+
def _tiled_image_shape(self) -> tuple[int, int]:
242+
"""Returns a tuple containing the dimension of the tiled image."""
243+
cols, rows = self._tiling_grid_shape()
244+
return (self.cfg.width * cols, self.cfg.height * rows)
245+
246+
def _tiling_grid_shape(self) -> tuple[int, int]:
247+
"""Returns a tuple containing the tiling grid dimension."""
248+
cols = round(math.sqrt(self._view.count))
249+
rows = math.ceil(self._view.count / cols)
250+
return (cols, rows)
251+
252+
def _create_annotator_data(self):
253+
# we do not need to create annotator data for the tiled camera sensor
254+
raise RuntimeError("This function should not be called for the tiled camera sensor.")
255+
256+
def _process_annotator_output(self, name: str, output: Any) -> tuple[torch.tensor, dict | None]:
257+
# we do not need to process annotator output for the tiled camera sensor
258+
raise RuntimeError("This function should not be called for the tiled camera sensor.")
259+
238260
"""
239261
Internal simulation callbacks.
240262
"""

0 commit comments

Comments
 (0)