1313
1414from t4_devkit .common .geometry import view_points
1515from t4_devkit .common .timestamp import microseconds2seconds , seconds2microseconds
16- from t4_devkit .dataclass import LidarPointCloud , RadarPointCloud
16+ from t4_devkit .dataclass import LidarPointCloud , RadarPointCloud , SegmentationPointCloud
1717from t4_devkit .schema import SensorModality
1818from t4_devkit .viewer import (
1919 PointCloudColorMode ,
@@ -56,9 +56,17 @@ def __init__(self, t4: Tier4) -> None:
5656 self ._label2id : dict [str , int ] = {
5757 category .name : category .index for category in self ._t4 .category
5858 }
59+ self ._sample_data_to_lidarseg_filename : dict [str , str ] | None = (
60+ {lidarseg .sample_data_token : lidarseg .filename for lidarseg in self ._t4 .lidarseg }
61+ if self ._t4 .lidarseg
62+ else None
63+ )
5964
6065 self ._executor = concurrent .futures .ThreadPoolExecutor ()
6166
67+ def _has_lidarseg (self ) -> bool :
68+ return self ._sample_data_to_lidarseg_filename is not None
69+
6270 def _init_viewer (
6371 self ,
6472 app_id : str ,
@@ -122,17 +130,24 @@ def render_scene(
122130 app_id = f"scene@{ self ._t4 .dataset_id } "
123131 viewer = self ._init_viewer (app_id , render_ann = True , save_dir = save_dir )
124132
125- self ._render_map (viewer )
133+ self ._try_render_map (viewer )
126134
127135 scene : Scene = self ._t4 .scene [0 ]
128136 first_sample : Sample = self ._t4 .get ("sample" , scene .first_sample_token )
129137 max_timestamp_us = first_sample .timestamp + seconds2microseconds (max_time_seconds )
130138
131- concurrent .futures .wait (
139+ pointcloud_color_mode = (
140+ PointCloudColorMode .SEGMENTATION
141+ if self ._has_lidarseg ()
142+ else PointCloudColorMode .DISTANCE
143+ )
144+
145+ futures = (
132146 self ._render_lidar_and_ego (
133147 viewer = viewer ,
134148 first_lidar_tokens = first_lidar_tokens ,
135149 max_timestamp_us = max_timestamp_us ,
150+ color_mode = pointcloud_color_mode ,
136151 )
137152 + self ._render_radars (
138153 viewer = viewer ,
@@ -146,23 +161,23 @@ def render_scene(
146161 )
147162 + [
148163 self ._executor .submit (
149- self ._render_annotation3ds (
150- viewer = viewer ,
151- first_sample_token = scene .first_sample_token ,
152- max_timestamp_us = max_timestamp_us ,
153- future_seconds = future_seconds ,
154- )
164+ self ._render_annotation3ds ,
165+ viewer = viewer ,
166+ first_sample_token = scene .first_sample_token ,
167+ max_timestamp_us = max_timestamp_us ,
168+ future_seconds = future_seconds ,
155169 ),
156170 self ._executor .submit (
157- self ._render_annotation2ds (
158- viewer = viewer ,
159- first_sample_token = scene .first_sample_token ,
160- max_timestamp_us = max_timestamp_us ,
161- )
171+ self ._render_annotation2ds ,
172+ viewer = viewer ,
173+ first_sample_token = scene .first_sample_token ,
174+ max_timestamp_us = max_timestamp_us ,
162175 ),
163176 ]
164177 )
165178
179+ _handle_futures (futures )
180+
166181 def render_instance (
167182 self ,
168183 instance_token : str | Sequence [str ],
@@ -221,13 +236,20 @@ def render_instance(
221236 app_id = f"instance@{ self ._t4 .dataset_id } "
222237 viewer = self ._init_viewer (app_id , render_ann = True , save_dir = save_dir )
223238
224- self ._render_map (viewer )
239+ self ._try_render_map (viewer )
240+
241+ pointcloud_color_mode = (
242+ PointCloudColorMode .SEGMENTATION
243+ if self ._has_lidarseg ()
244+ else PointCloudColorMode .DISTANCE
245+ )
225246
226- concurrent . futures . wait (
247+ futures = (
227248 self ._render_lidar_and_ego (
228249 viewer = viewer ,
229250 first_lidar_tokens = first_lidar_tokens ,
230251 max_timestamp_us = max_timestamp_us ,
252+ color_mode = pointcloud_color_mode ,
231253 )
232254 + self ._render_radars (
233255 viewer = viewer ,
@@ -241,25 +263,25 @@ def render_instance(
241263 )
242264 + [
243265 self ._executor .submit (
244- self ._render_annotation3ds (
245- viewer = viewer ,
246- first_sample_token = first_sample .token ,
247- max_timestamp_us = max_timestamp_us ,
248- future_seconds = future_seconds ,
249- instance_tokens = instance_tokens ,
250- )
266+ self ._render_annotation3ds ,
267+ viewer = viewer ,
268+ first_sample_token = first_sample .token ,
269+ max_timestamp_us = max_timestamp_us ,
270+ future_seconds = future_seconds ,
271+ instance_tokens = instance_tokens ,
251272 ),
252273 self ._executor .submit (
253- self ._render_annotation2ds (
254- viewer = viewer ,
255- first_sample_token = first_sample .token ,
256- max_timestamp_us = max_timestamp_us ,
257- instance_tokens = instance_tokens ,
258- )
274+ self ._render_annotation2ds ,
275+ viewer = viewer ,
276+ first_sample_token = first_sample .token ,
277+ max_timestamp_us = max_timestamp_us ,
278+ instance_tokens = instance_tokens ,
259279 ),
260- ],
280+ ]
261281 )
262282
283+ _handle_futures (futures )
284+
263285 def render_pointcloud (
264286 self ,
265287 * ,
@@ -282,7 +304,7 @@ def render_pointcloud(
282304 app_id = f"pointcloud@{ self ._t4 .dataset_id } "
283305 viewer = self ._init_viewer (app_id , render_ann = False , save_dir = save_dir )
284306
285- self ._render_map (viewer )
307+ self ._try_render_map (viewer )
286308
287309 # search first lidar sample data token
288310 first_lidar_token : str | None = None
@@ -299,22 +321,24 @@ def render_pointcloud(
299321 max_time_seconds
300322 )
301323
302- concurrent .futures .wait (
303- self ._render_lidar_and_ego (
304- viewer = viewer ,
305- first_lidar_tokens = [first_lidar_token ],
306- max_timestamp_us = max_timestamp_us ,
307- )
308- + self ._render_points_on_cameras (
309- first_point_sample_data_token = first_lidar_token ,
310- max_timestamp_us = max_timestamp_us ,
311- min_dist = 1.0 ,
312- ignore_distortion = ignore_distortion ,
313- ),
324+ # TODO: support rendering segmentation pointcloud on camera
325+ futures = self ._render_lidar_and_ego (
326+ viewer = viewer ,
327+ first_lidar_tokens = [first_lidar_token ],
328+ max_timestamp_us = max_timestamp_us ,
329+ ) + self ._render_points_on_cameras (
330+ first_point_sample_data_token = first_lidar_token ,
331+ max_timestamp_us = max_timestamp_us ,
332+ min_dist = 1.0 ,
333+ ignore_distortion = ignore_distortion ,
314334 )
315335
316- def _render_map (self , viewer : RerunViewer ) -> None :
336+ _handle_futures (futures )
337+
338+ def _try_render_map (self , viewer : RerunViewer ) -> None :
317339 lanelet_path = osp .join (self ._t4 .map_dir , "lanelet2_map.osm" )
340+ if not osp .exists (lanelet_path ):
341+ return
318342 viewer .render_map (lanelet_path )
319343
320344 def _render_sensor_calibration (self , viewer : RerunViewer , sample_data_token : str ) -> None :
@@ -337,6 +361,7 @@ def _render_lidar_and_ego(
337361 viewer : RerunViewer ,
338362 first_lidar_tokens : list [str ],
339363 max_timestamp_us : float ,
364+ * ,
340365 color_mode : PointCloudColorMode = PointCloudColorMode .DISTANCE ,
341366 ) -> list [Future ]:
342367 def _render_single_lidar (first_lidar_token : str ) -> None :
@@ -345,25 +370,39 @@ def _render_single_lidar(first_lidar_token: str) -> None:
345370 current_lidar_token = first_lidar_token
346371 while current_lidar_token != "" :
347372 sample_data : SampleData = self ._t4 .get ("sample_data" , current_lidar_token )
373+ current_lidar_token = sample_data .next
348374
349375 if max_timestamp_us < sample_data .timestamp :
350376 break
351377
352378 ego_pose : EgoPose = self ._t4 .get ("ego_pose" , sample_data .ego_pose_token )
353379 viewer .render_ego (ego_pose = ego_pose )
354380
355- pointcloud = LidarPointCloud .from_file (
356- osp .join (self ._t4 .data_root , sample_data .filename )
357- )
381+ # render segmentation pointcloud if available, otherwise render raw pointcloud
382+ if color_mode == PointCloudColorMode .SEGMENTATION :
383+ if not (
384+ self ._has_lidarseg ()
385+ and sample_data .token in self ._sample_data_to_lidarseg_filename
386+ ):
387+ continue
388+
389+ label_filename = self ._sample_data_to_lidarseg_filename [sample_data .token ]
390+ pointcloud = SegmentationPointCloud .from_file (
391+ point_filepath = osp .join (self ._t4 .data_root , sample_data .filename ),
392+ label_filepath = osp .join (self ._t4 .data_root , label_filename ),
393+ )
394+ else :
395+ pointcloud = LidarPointCloud .from_file (
396+ osp .join (self ._t4 .data_root , sample_data .filename )
397+ )
398+
358399 viewer .render_pointcloud (
359400 seconds = microseconds2seconds (sample_data .timestamp ),
360401 channel = sample_data .channel ,
361402 pointcloud = pointcloud ,
362403 color_mode = color_mode ,
363404 )
364405
365- current_lidar_token = sample_data .next
366-
367406 return [self ._executor .submit (_render_single_lidar , token ) for token in first_lidar_tokens ]
368407
369408 def _render_radars (
@@ -699,3 +738,19 @@ def _append_mask(
699738 camera_masks [camera ]["class_ids" ] = [class_id ]
700739 camera_masks [camera ]["uuids" ] = [uuid ]
701740 return camera_masks
741+
742+
743+ def _handle_futures (futures : list [Future ]) -> None :
744+ """Wait for all futures and raise exception if any.
745+
746+ Args:
747+ futures (list[Future]): List of futures.
748+ """
749+ if not futures :
750+ return
751+
752+ concurrent .futures .wait (futures )
753+ for future in futures :
754+ if not future .done ():
755+ continue
756+ future .result ()
0 commit comments