Skip to content

Commit e6f1244

Browse files
authored
fix: resolve input types of viewer (#187)
* docs: update installation section Signed-off-by: ktro2828 <kotaro.uetake@tier4.jp> * fix: validate field types of record Signed-off-by: ktro2828 <kotaro.uetake@tier4.jp> * docs: update tutotial document for visualization Signed-off-by: ktro2828 <kotaro.uetake@tier4.jp> --------- Signed-off-by: ktro2828 <kotaro.uetake@tier4.jp>
1 parent dd77966 commit e6f1244

8 files changed

Lines changed: 124 additions & 63 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ A toolkit to load and operate T4 dataset.
1515

1616
### Installation
1717

18-
#### Install via GitHub
18+
#### [For Users] Install via GitHub
1919

2020
Note that the following command installs the latest `main` branch:
2121

@@ -31,7 +31,7 @@ By specifying `@<TAG_OR_BRANCH>`, you can install the particular version of `t4-
3131
pip install git+https://github.com/tier4/t4-devkit.git@main
3232
```
3333

34-
#### Install from source
34+
#### [For Developers] Install from source
3535

3636
You need to install `uv`. For details, please refer to [OFFICIAL DOCUMENT](https://docs.astral.sh/uv/).
3737

docs/assets/render_box2ds.png

170 KB
Loading

docs/assets/render_box3ds.png

752 KB
Loading

docs/assets/render_pointcloud.png

1.01 MB
Loading

docs/install.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Install via GitHub
1+
## [For Users] Install via GitHub
22

33
Note that the following command installs the latest `main` branch:
44

@@ -14,7 +14,7 @@ By specifying `@<TAG_OR_BRANCH>`, you can install the particular version of `t4-
1414
pip install git+https://github.com/tier4/t4-devkit.git@main
1515
```
1616

17-
## Install from source
17+
## [For Developers] Install from source
1818

1919
You need to install `uv`. For details, please refer to [OFFICIAL DOCUMENT](https://docs.astral.sh/uv/).
2020

docs/tutorials/render.md

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,37 +60,88 @@ When you specify `save_dir`, viewer will not be spawned on your screen.
6060

6161
## Rendering with `RerunViewer`
6262

63-
### Rendering boxes
64-
6563
If you want to visualize your components, such as boxes that your ML-model estimated, `RerunViewer` allows you to visualize these components.
6664
For details, please refer to the API references.
6765

6866
```python
6967
>>> from t4_devkit.viewer import RerunViewer
7068
# You need to specify `cameras` if you want to 2D spaces
71-
>>> viewer = RerunViewer(app_id, cameras=<CAMERA_NAMES:[str;N]>)
69+
>>> viewer = RerunViewer("foo", cameras=<CAMERA_NAMES:[str;N]>)
70+
71+
# Timestamp in seconds
72+
>>> seconds: int | float = ...
73+
```
74+
75+
### Rendering 3D boxes
76+
77+
```python
7278
# Rendering 3D boxes
79+
>>> from t4_devkit.dataclass import Box3D
80+
>>> box3ds = [Box3D(...)...]
7381
>>> viewer.render_box3ds(seconds, box3ds)
74-
# Rendering 2D boxes
75-
>>> viewer.render_box2ds(seconds, box2ds)
7682
```
7783

7884
It allows you to render boxes by specifying elements of boxes directly.
7985

8086
```python
8187
# Rendering 3D boxes
88+
>>> centers = [[i, i, i] for i in range(10)]
89+
>>> rotations = [[1, 0, 0, 0] for _ in range(10)]
90+
>>> sizes = [[1, 1, 1] for _ in range(10)]
91+
>>> class_ids = [0 for _ in range(10)]
8292
>>> viewer.render_box3ds(seconds, centers, rotations, sizes, class_ids)
93+
```
94+
95+
![Render Box3Ds](../assets/render_box3ds.png)
96+
97+
### Rendering 2D boxes
98+
99+
For 2D spaces, you need to specify camera names in the viewer constructor, and render images by specifying camera names:
100+
101+
```python
102+
# RerunViewer(<APP_ID:str>, cameras=<CAMERA_NAMES:[str;N]>)
103+
>>> viewer = RerunViewer("foo", cameras=["camera1"])
104+
105+
>>> import numpy as np
106+
>>> image = np.zeros((100, 100, 3), dtype=np.uint8)
107+
>>> viewer.render_image(seconds, "camera1", image)
108+
```
109+
110+
```python
83111
# Rendering 2D boxes
84-
>>> viewer.render_box2ds(seconds, rois, class_ids)
112+
>>> from t4_devkit.dataclass import Box2D
113+
>>> box2ds = [Box2D(...)...]
114+
>>> viewer.render_box2ds(seconds, "camera1", box2ds)
85115
```
86116

87-
### Rendering lanelet map
117+
It allows you to render boxes by specifying elements of boxes directly:
88118

89-
![Render Lanelet Map](../assets/render_map.png)
119+
```python
120+
# Rendering 2D boxes
121+
>>> rois = [[0, 0, 10 * i, 10 * i] for i in range(10)]
122+
>>> viewer.render_box2ds(seconds, "camera1", rois, class_ids)
123+
```
90124

91-
You can also render lanelet map by specifying `lanelet_path`:
125+
![Render Box2Ds](../assets/render_box2ds.png)
126+
127+
### Rendering point cloud
128+
129+
```python
130+
from t4_devkit.dataclass import LidarPointCloud
131+
# Point cloud channel name
132+
>>> lidar_channel = "LIDAR_TOP"
133+
# Load point cloud from file
134+
>>> pointcloud = LidarPointCloud.from_file(<PATH_TO_POINTCLOUD.pcd.bin>)
135+
>>> viewer.render_pointcloud(seconds, lidar_channel, pointcloud)
136+
```
137+
138+
![Render Point Cloud](../assets/render_pointcloud.png)
139+
140+
### Rendering lanelet map
92141

93142
```python
94143
# Rendering lanelet map
95-
>>> viewer.render_map(lanelet_path)
144+
>>> viewer.render_map(<PATH_TO_LANELET.osm>)
96145
```
146+
147+
![Render Lanelet Map](../assets/render_map.png)

t4_devkit/viewer/record/box.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
import numpy as np
66
import rerun as rr
77
import rerun.components as rrc
8-
from attrs import converters, define, field
8+
from attrs import converters, define, field, validators
99

10+
from t4_devkit.common.converter import to_quaternion
11+
from t4_devkit.dataclass import Future
1012
from t4_devkit.typing import Roi, Vector3
11-
from t4_devkit.typing.aliases import RoiLike
1213

1314
if TYPE_CHECKING:
14-
from t4_devkit.dataclass import Box2D, Box3D, Future
15-
from t4_devkit.typing import RotationLike, Vector3Like
15+
from t4_devkit.dataclass import Box2D, Box3D
16+
from t4_devkit.typing import RoiLike, RotationLike, Vector3Like
1617

1718

1819
__all__ = ["BatchBox3D", "BatchBox2D"]
@@ -35,12 +36,16 @@ class Record:
3536
"""Inner class to represent a record of 3D box instance for rendering."""
3637

3738
center: Vector3 = field(converter=Vector3)
38-
rotation: rr.Quaternion
39+
rotation: rr.Quaternion = field(validator=validators.instance_of(rr.Quaternion))
3940
size: Vector3 = field(converter=Vector3)
40-
class_id: int
41-
uuid: int | None = field(default=None)
41+
class_id: int = field(validator=validators.instance_of(int))
42+
uuid: str | None = field(
43+
default=None, validator=validators.optional(validators.instance_of(str))
44+
)
4245
velocity: Vector3 | None = field(default=None, converter=converters.optional(Vector3))
43-
future: Future | None = field(default=None)
46+
future: Future | None = field(
47+
default=None, validator=validators.optional(validators.instance_of(Future))
48+
)
4449

4550
@overload
4651
def append(self, box: Box3D) -> None:
@@ -105,6 +110,7 @@ def _append_with_elements(
105110
uuid: str | None = None,
106111
future: Future | None = None,
107112
) -> None:
113+
rotation = to_quaternion(rotation)
108114
rotation_xyzw = np.roll(rotation.q, shift=-1)
109115

110116
width, length, height = size
@@ -203,8 +209,10 @@ class Record:
203209
"""Inner class to represent a record of 2D box instance for rendering."""
204210

205211
roi: Roi = field(converter=Roi)
206-
class_id: int
207-
uuid: str | None = field(default=None)
212+
class_id: int = field(validator=validators.instance_of(int))
213+
uuid: str | None = field(
214+
default=None, validator=validators.optional(validators.instance_of(str))
215+
)
208216

209217
@overload
210218
def append(self, box: Box2D) -> None:

t4_devkit/viewer/viewer.py

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
import rerun.blueprint as rrb
1010
from typing_extensions import Self
1111

12+
from t4_devkit.common.converter import to_quaternion
1213
from t4_devkit.common.timestamp import us2sec
1314
from t4_devkit.lanelet import LaneletParser
1415
from t4_devkit.schema import SensorModality
15-
from t4_devkit.typing import Quaternion, Roi, Vector3
1616

1717
from .color import distance_color
1818
from .geography import calculate_geodetic_point
@@ -27,7 +27,7 @@
2727
if TYPE_CHECKING:
2828
from t4_devkit.dataclass import Box2D, Box3D, Future, PointCloudLike
2929
from t4_devkit.schema import CalibratedSensor, EgoPose, Sensor
30-
from t4_devkit.typing import CamIntrinsicLike, NDArrayU8
30+
from t4_devkit.typing import CamIntrinsicLike, NDArrayU8, RoiLike, RotationLike, Vector3Like
3131

3232
__all__ = ["RerunViewer", "format_entity"]
3333

@@ -214,23 +214,23 @@ def render_box3ds(self, seconds: float, boxes: Sequence[Box3D]) -> None:
214214
def render_box3ds(
215215
self,
216216
seconds: float,
217-
centers: Sequence[Vector3],
218-
rotations: Sequence[Quaternion],
219-
sizes: Sequence[Vector3],
217+
centers: Sequence[Vector3Like],
218+
rotations: Sequence[RotationLike],
219+
sizes: Sequence[Vector3Like],
220220
class_ids: Sequence[int],
221-
velocities: Sequence[Vector3] | None = None,
221+
velocities: Sequence[Vector3Like] | None = None,
222222
uuids: Sequence[str] | None = None,
223223
futures: Sequence[Future] | None = None,
224224
) -> None:
225225
"""Render 3D boxes with its elements.
226226
227227
Args:
228228
seconds (float): Timestamp in [sec].
229-
centers (Sequence[Vector3]): Sequence of 3D positions in the order of (x, y, z).
230-
rotations (Sequence[Quaternion]): Sequence of quaternions.
231-
sizes (Sequence[Vector3]): Sequence of box sizes in the order of (width, length, height).
229+
centers (Sequence[Vector3Like]): Sequence of 3D positions in the order of (x, y, z).
230+
rotations (Sequence[RotationLike]): Sequence of rotations.
231+
sizes (Sequence[Vector3Like]): Sequence of box sizes in the order of (width, length, height).
232232
class_ids (Sequence[int]): Sequence of class IDs.
233-
velocities (Sequence[Vector3] | None, optional): Sequence of velocities.
233+
velocities (Sequence[Vector3Like] | None, optional): Sequence of velocities.
234234
uuids (Sequence[str] | None, optional): Sequence of unique identifiers.
235235
futures (Sequence[Future] | None, optional): Sequence future trajectories.
236236
"""
@@ -276,11 +276,11 @@ def _render_box3ds_with_boxes(self, seconds: float, boxes: Sequence[Box3D]) -> N
276276
def _render_box3ds_with_elements(
277277
self,
278278
seconds: float,
279-
centers: Sequence[Vector3],
280-
rotations: Sequence[Quaternion],
281-
sizes: Sequence[Vector3],
279+
centers: Sequence[Vector3Like],
280+
rotations: Sequence[RotationLike],
281+
sizes: Sequence[Vector3Like],
282282
class_ids: Sequence[int],
283-
velocities: Sequence[Vector3] | None = None,
283+
velocities: Sequence[Vector3Like] | None = None,
284284
uuids: Sequence[str] | None | None = None,
285285
futures: Sequence[Future] | None = None,
286286
) -> None:
@@ -346,7 +346,7 @@ def render_box2ds(
346346
self,
347347
seconds: float,
348348
camera: str,
349-
rois: Sequence[Roi],
349+
rois: Sequence[RoiLike],
350350
class_ids: Sequence[int],
351351
uuids: Sequence[str] | None = None,
352352
) -> None:
@@ -355,7 +355,7 @@ def render_box2ds(
355355
Args:
356356
seconds (float): Timestamp in [sec].
357357
camera (str): Camera name.
358-
rois (Sequence[Roi]): Sequence of ROIs in the order of (xmin, ymin, xmax, ymax).
358+
rois (Sequence[RoiLike]): Sequence of ROIs in the order of (xmin, ymin, xmax, ymax).
359359
class_ids (Sequence[int]): Sequence of class IDs.
360360
uuids (Sequence[str] | None, optional): Sequence of unique identifiers.
361361
"""
@@ -391,7 +391,7 @@ def _render_box2ds_with_elements(
391391
self,
392392
seconds: float,
393393
camera: str,
394-
rois: Sequence[Roi],
394+
rois: Sequence[RoiLike],
395395
class_ids: Sequence[int],
396396
uuids: Sequence[str] | None = None,
397397
) -> None:
@@ -496,18 +496,18 @@ def render_ego(self, ego_pose: EgoPose) -> None:
496496
def render_ego(
497497
self,
498498
seconds: float,
499-
translation: Vector3,
500-
rotation: Quaternion,
501-
geocoordinate: Vector3 | None = None,
499+
translation: Vector3Like,
500+
rotation: RotationLike,
501+
geocoordinate: Vector3Like | None = None,
502502
) -> None:
503503
"""Render an ego pose.
504504
505505
Args:
506506
seconds (float): Timestamp in [sec].
507-
translation (Vector3): 3D position in the map coordinate system
507+
translation (Vector3Like): 3D position in the map coordinate system
508508
, in the order of (x, y, z) in [m].
509-
rotation (Quaternion): Rotation in the map coordinate system.
510-
geocoordinate (Vector3 | None, optional): Coordinates in the WGS 84
509+
rotation (RotationLike): Rotation in the map coordinate system.
510+
geocoordinate (Vector3Like | None, optional): Coordinates in the WGS 84
511511
reference ellipsoid (latitude, longitude, altitude) in degrees and meters.
512512
"""
513513
pass
@@ -530,18 +530,17 @@ def _render_ego_with_schema(self, ego_pose: EgoPose) -> None:
530530
def _render_ego_without_schema(
531531
self,
532532
seconds: float,
533-
translation: Vector3,
534-
rotation: Quaternion,
535-
geocoordinate: Vector3 | None = None,
533+
translation: Vector3Like,
534+
rotation: RotationLike,
535+
geocoordinate: Vector3Like | None = None,
536536
) -> None:
537537
rr.set_time_seconds(self.timeline, seconds)
538538

539-
rotation_xyzw = np.roll(rotation.q, shift=-1)
540539
rr.log(
541540
self.ego_entity,
542541
rr.Transform3D(
543542
translation=translation,
544-
rotation=rr.Quaternion(xyzw=rotation_xyzw),
543+
rotation=_to_rerun_quaternion(rotation),
545544
relation=rr.TransformRelation.ParentFromChild,
546545
),
547546
)
@@ -578,17 +577,17 @@ def render_calibration(
578577
self,
579578
channel: str,
580579
modality: str | SensorModality,
581-
translation: Vector3,
582-
rotation: Quaternion,
580+
translation: Vector3Like,
581+
rotation: RotationLike,
583582
camera_intrinsic: CamIntrinsicLike | None = None,
584583
) -> None:
585584
"""Render a sensor calibration.
586585
587586
Args:
588587
channel (str): Name of the sensor channel.
589588
modality (str | SensorModality): Sensor modality.
590-
translation (Vector3): Sensor translation in ego centric coords.
591-
rotation (Quaternion): Sensor rotation in ego centric coords.
589+
translation (Vector3Like): Sensor translation in ego centric coords.
590+
rotation (RotationLike): Sensor rotation in ego centric coords.
592591
camera_intrinsic (CamIntrinsicLike | None, optional): Camera intrinsic matrix.
593592
Defaults to None.
594593
"""
@@ -618,25 +617,23 @@ def _render_calibration_without_schema(
618617
self,
619618
channel: str,
620619
modality: str | SensorModality,
621-
translation: Vector3,
622-
rotation: Quaternion,
620+
translation: Vector3Like,
621+
rotation: RotationLike,
623622
camera_intrinsic: CamIntrinsicLike | None = None,
624623
) -> None:
625624
"""Render a sensor calibration.
626625
627626
Args:
628627
channel (str): Name of the sensor channel.
629628
modality (str | SensorModality): Sensor modality.
630-
translation (Vector3): Sensor translation in ego centric coords.
631-
rotation (Quaternion): Sensor rotation in ego centric coords.
629+
translation (Vector3Like): Sensor translation in ego centric coords.
630+
rotation (RotationLike): Sensor rotation in ego centric coords.
632631
camera_intrinsic (CamIntrinsicLike | None, optional): Camera intrinsic matrix.
633632
Defaults to None.
634633
"""
635-
rotation_xyzw = np.roll(rotation.q, shift=-1)
636-
637634
rr.log(
638635
format_entity(self.ego_entity, channel),
639-
rr.Transform3D(translation=translation, rotation=rr.Quaternion(xyzw=rotation_xyzw)),
636+
rr.Transform3D(translation=translation, rotation=_to_rerun_quaternion(rotation)),
640637
static=True,
641638
)
642639

@@ -661,3 +658,8 @@ def render_map(self, filepath: str) -> None:
661658
render_ways(parser, root_entity)
662659

663660
render_geographic_borders(parser, f"{self.geocoordinate_entity}/vector_map")
661+
662+
663+
def _to_rerun_quaternion(rotation: RotationLike) -> rr.Quaternion:
664+
rotation_xyzw = np.roll(to_quaternion(rotation).q, shift=-1)
665+
return rr.Quaternion(xyzw=rotation_xyzw)

0 commit comments

Comments
 (0)