supervisely 6.73.452__py3-none-any.whl → 6.73.513__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. supervisely/__init__.py +25 -1
  2. supervisely/annotation/annotation.py +8 -2
  3. supervisely/annotation/json_geometries_map.py +13 -12
  4. supervisely/api/annotation_api.py +6 -3
  5. supervisely/api/api.py +2 -0
  6. supervisely/api/app_api.py +10 -1
  7. supervisely/api/dataset_api.py +74 -12
  8. supervisely/api/entities_collection_api.py +10 -0
  9. supervisely/api/entity_annotation/figure_api.py +28 -0
  10. supervisely/api/entity_annotation/object_api.py +3 -3
  11. supervisely/api/entity_annotation/tag_api.py +63 -12
  12. supervisely/api/guides_api.py +210 -0
  13. supervisely/api/image_api.py +4 -0
  14. supervisely/api/labeling_job_api.py +83 -1
  15. supervisely/api/labeling_queue_api.py +33 -7
  16. supervisely/api/module_api.py +5 -0
  17. supervisely/api/project_api.py +71 -26
  18. supervisely/api/storage_api.py +3 -1
  19. supervisely/api/task_api.py +13 -2
  20. supervisely/api/team_api.py +4 -3
  21. supervisely/api/video/video_annotation_api.py +119 -3
  22. supervisely/api/video/video_api.py +65 -14
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +23 -7
  25. supervisely/app/development/development.py +18 -2
  26. supervisely/app/fastapi/__init__.py +1 -0
  27. supervisely/app/fastapi/custom_static_files.py +1 -1
  28. supervisely/app/fastapi/multi_user.py +105 -0
  29. supervisely/app/fastapi/subapp.py +88 -42
  30. supervisely/app/fastapi/websocket.py +77 -9
  31. supervisely/app/singleton.py +21 -0
  32. supervisely/app/v1/app_service.py +18 -2
  33. supervisely/app/v1/constants.py +7 -1
  34. supervisely/app/widgets/__init__.py +6 -0
  35. supervisely/app/widgets/activity_feed/__init__.py +0 -0
  36. supervisely/app/widgets/activity_feed/activity_feed.py +239 -0
  37. supervisely/app/widgets/activity_feed/style.css +78 -0
  38. supervisely/app/widgets/activity_feed/template.html +22 -0
  39. supervisely/app/widgets/card/card.py +20 -0
  40. supervisely/app/widgets/classes_list_selector/classes_list_selector.py +121 -9
  41. supervisely/app/widgets/classes_list_selector/template.html +60 -93
  42. supervisely/app/widgets/classes_mapping/classes_mapping.py +13 -12
  43. supervisely/app/widgets/classes_table/classes_table.py +1 -0
  44. supervisely/app/widgets/deploy_model/deploy_model.py +56 -35
  45. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +1 -1
  46. supervisely/app/widgets/experiment_selector/experiment_selector.py +8 -0
  47. supervisely/app/widgets/fast_table/fast_table.py +184 -60
  48. supervisely/app/widgets/fast_table/template.html +1 -1
  49. supervisely/app/widgets/heatmap/__init__.py +0 -0
  50. supervisely/app/widgets/heatmap/heatmap.py +564 -0
  51. supervisely/app/widgets/heatmap/script.js +533 -0
  52. supervisely/app/widgets/heatmap/style.css +233 -0
  53. supervisely/app/widgets/heatmap/template.html +21 -0
  54. supervisely/app/widgets/modal/__init__.py +0 -0
  55. supervisely/app/widgets/modal/modal.py +198 -0
  56. supervisely/app/widgets/modal/template.html +10 -0
  57. supervisely/app/widgets/object_class_view/object_class_view.py +3 -0
  58. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  59. supervisely/app/widgets/radio_tabs/template.html +1 -0
  60. supervisely/app/widgets/select/select.py +6 -3
  61. supervisely/app/widgets/select_class/__init__.py +0 -0
  62. supervisely/app/widgets/select_class/select_class.py +363 -0
  63. supervisely/app/widgets/select_class/template.html +50 -0
  64. supervisely/app/widgets/select_cuda/select_cuda.py +22 -0
  65. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +65 -7
  66. supervisely/app/widgets/select_tag/__init__.py +0 -0
  67. supervisely/app/widgets/select_tag/select_tag.py +352 -0
  68. supervisely/app/widgets/select_tag/template.html +64 -0
  69. supervisely/app/widgets/select_team/select_team.py +37 -4
  70. supervisely/app/widgets/select_team/template.html +4 -5
  71. supervisely/app/widgets/select_user/__init__.py +0 -0
  72. supervisely/app/widgets/select_user/select_user.py +270 -0
  73. supervisely/app/widgets/select_user/template.html +13 -0
  74. supervisely/app/widgets/select_workspace/select_workspace.py +59 -10
  75. supervisely/app/widgets/select_workspace/template.html +9 -12
  76. supervisely/app/widgets/table/table.py +68 -13
  77. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  78. supervisely/aug/aug.py +6 -2
  79. supervisely/convert/base_converter.py +1 -0
  80. supervisely/convert/converter.py +2 -2
  81. supervisely/convert/image/image_converter.py +3 -1
  82. supervisely/convert/image/image_helper.py +48 -4
  83. supervisely/convert/image/label_studio/label_studio_converter.py +2 -0
  84. supervisely/convert/image/medical2d/medical2d_helper.py +2 -24
  85. supervisely/convert/image/multispectral/multispectral_converter.py +6 -0
  86. supervisely/convert/image/pascal_voc/pascal_voc_converter.py +8 -5
  87. supervisely/convert/image/pascal_voc/pascal_voc_helper.py +7 -0
  88. supervisely/convert/pointcloud/kitti_3d/kitti_3d_converter.py +33 -3
  89. supervisely/convert/pointcloud/kitti_3d/kitti_3d_helper.py +12 -5
  90. supervisely/convert/pointcloud/las/las_converter.py +13 -1
  91. supervisely/convert/pointcloud/las/las_helper.py +110 -11
  92. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +27 -16
  93. supervisely/convert/pointcloud/pointcloud_converter.py +91 -3
  94. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +58 -22
  95. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +21 -47
  96. supervisely/convert/video/__init__.py +1 -0
  97. supervisely/convert/video/multi_view/__init__.py +0 -0
  98. supervisely/convert/video/multi_view/multi_view.py +543 -0
  99. supervisely/convert/video/sly/sly_video_converter.py +359 -3
  100. supervisely/convert/video/video_converter.py +22 -2
  101. supervisely/convert/volume/dicom/dicom_converter.py +13 -5
  102. supervisely/convert/volume/dicom/dicom_helper.py +30 -18
  103. supervisely/geometry/constants.py +1 -0
  104. supervisely/geometry/geometry.py +4 -0
  105. supervisely/geometry/helpers.py +5 -1
  106. supervisely/geometry/oriented_bbox.py +676 -0
  107. supervisely/geometry/rectangle.py +2 -1
  108. supervisely/io/env.py +76 -1
  109. supervisely/io/fs.py +21 -0
  110. supervisely/nn/benchmark/base_evaluator.py +104 -11
  111. supervisely/nn/benchmark/instance_segmentation/evaluator.py +1 -8
  112. supervisely/nn/benchmark/object_detection/evaluator.py +20 -4
  113. supervisely/nn/benchmark/object_detection/vis_metrics/pr_curve.py +10 -5
  114. supervisely/nn/benchmark/semantic_segmentation/evaluator.py +34 -16
  115. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/confusion_matrix.py +1 -1
  116. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/frequently_confused.py +1 -1
  117. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/overview.py +1 -1
  118. supervisely/nn/benchmark/visualization/evaluation_result.py +66 -4
  119. supervisely/nn/inference/cache.py +43 -18
  120. supervisely/nn/inference/gui/serving_gui_template.py +5 -2
  121. supervisely/nn/inference/inference.py +795 -199
  122. supervisely/nn/inference/inference_request.py +42 -9
  123. supervisely/nn/inference/predict_app/gui/classes_selector.py +83 -12
  124. supervisely/nn/inference/predict_app/gui/gui.py +676 -488
  125. supervisely/nn/inference/predict_app/gui/input_selector.py +205 -26
  126. supervisely/nn/inference/predict_app/gui/model_selector.py +2 -4
  127. supervisely/nn/inference/predict_app/gui/output_selector.py +46 -6
  128. supervisely/nn/inference/predict_app/gui/settings_selector.py +756 -59
  129. supervisely/nn/inference/predict_app/gui/tags_selector.py +1 -1
  130. supervisely/nn/inference/predict_app/gui/utils.py +236 -119
  131. supervisely/nn/inference/predict_app/predict_app.py +2 -2
  132. supervisely/nn/inference/session.py +43 -35
  133. supervisely/nn/inference/tracking/bbox_tracking.py +113 -34
  134. supervisely/nn/inference/tracking/tracker_interface.py +7 -2
  135. supervisely/nn/inference/uploader.py +139 -12
  136. supervisely/nn/live_training/__init__.py +7 -0
  137. supervisely/nn/live_training/api_server.py +111 -0
  138. supervisely/nn/live_training/artifacts_utils.py +243 -0
  139. supervisely/nn/live_training/checkpoint_utils.py +229 -0
  140. supervisely/nn/live_training/dynamic_sampler.py +44 -0
  141. supervisely/nn/live_training/helpers.py +14 -0
  142. supervisely/nn/live_training/incremental_dataset.py +146 -0
  143. supervisely/nn/live_training/live_training.py +497 -0
  144. supervisely/nn/live_training/loss_plateau_detector.py +111 -0
  145. supervisely/nn/live_training/request_queue.py +52 -0
  146. supervisely/nn/model/model_api.py +9 -0
  147. supervisely/nn/prediction_dto.py +12 -1
  148. supervisely/nn/tracker/base_tracker.py +11 -1
  149. supervisely/nn/tracker/botsort/botsort_config.yaml +0 -1
  150. supervisely/nn/tracker/botsort/tracker/mc_bot_sort.py +7 -4
  151. supervisely/nn/tracker/botsort_tracker.py +94 -65
  152. supervisely/nn/tracker/visualize.py +87 -90
  153. supervisely/nn/training/gui/classes_selector.py +16 -1
  154. supervisely/nn/training/train_app.py +28 -29
  155. supervisely/project/data_version.py +115 -51
  156. supervisely/project/download.py +1 -1
  157. supervisely/project/pointcloud_episode_project.py +37 -8
  158. supervisely/project/pointcloud_project.py +30 -2
  159. supervisely/project/project.py +14 -2
  160. supervisely/project/project_meta.py +27 -1
  161. supervisely/project/project_settings.py +32 -18
  162. supervisely/project/versioning/__init__.py +1 -0
  163. supervisely/project/versioning/common.py +20 -0
  164. supervisely/project/versioning/schema_fields.py +35 -0
  165. supervisely/project/versioning/video_schema.py +221 -0
  166. supervisely/project/versioning/volume_schema.py +87 -0
  167. supervisely/project/video_project.py +717 -15
  168. supervisely/project/volume_project.py +623 -5
  169. supervisely/template/experiment/experiment.html.jinja +4 -4
  170. supervisely/template/experiment/experiment_generator.py +14 -21
  171. supervisely/template/live_training/__init__.py +0 -0
  172. supervisely/template/live_training/header.html.jinja +96 -0
  173. supervisely/template/live_training/live_training.html.jinja +51 -0
  174. supervisely/template/live_training/live_training_generator.py +464 -0
  175. supervisely/template/live_training/sly-style.css +402 -0
  176. supervisely/template/live_training/template.html.jinja +18 -0
  177. supervisely/versions.json +28 -26
  178. supervisely/video/sampling.py +39 -20
  179. supervisely/video/video.py +40 -11
  180. supervisely/video_annotation/video_object.py +29 -4
  181. supervisely/volume/stl_converter.py +2 -0
  182. supervisely/worker_api/agent_rpc.py +24 -1
  183. supervisely/worker_api/rpc_servicer.py +31 -7
  184. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/METADATA +56 -39
  185. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/RECORD +189 -142
  186. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/WHEEL +1 -1
  187. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/entry_points.txt +0 -0
  188. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info/licenses}/LICENSE +0 -0
  189. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,8 @@ import os
3
3
  from typing import Dict, List, Optional, Set, Tuple
4
4
  from uuid import UUID
5
5
 
6
+ import numpy as np
7
+
6
8
  from supervisely import (
7
9
  Api,
8
10
  PointcloudAnnotation,
@@ -20,9 +22,11 @@ from supervisely.pointcloud.pointcloud import validate_ext as validate_pcd_ext
20
22
  from supervisely.pointcloud_annotation.constants import OBJECT_KEY
21
23
  from supervisely.video_annotation.key_id_map import KeyIdMap
22
24
 
25
+ CONVERTIBLE_EXTENSIONS = [".bin"]
26
+
23
27
 
24
28
  class PointcloudConverter(BaseConverter):
25
- allowed_exts = ALLOWED_POINTCLOUD_EXTENSIONS
29
+ allowed_exts = ALLOWED_POINTCLOUD_EXTENSIONS + CONVERTIBLE_EXTENSIONS
26
30
  modality = "pointclouds"
27
31
 
28
32
  class Item(BaseConverter.BaseItem):
@@ -252,8 +256,10 @@ class PointcloudConverter(BaseConverter):
252
256
  rimg_dict[dir_name].append(full_path)
253
257
  elif ext.lower() in self.allowed_exts:
254
258
  try:
255
- validate_pcd_ext(ext)
256
- pcd_list.append(full_path)
259
+ path = self._convert_to_pcd_if_needed(full_path, ext)
260
+ if not path:
261
+ continue
262
+ pcd_list.append(path)
257
263
  except:
258
264
  pass
259
265
  else:
@@ -277,3 +283,85 @@ class PointcloudConverter(BaseConverter):
277
283
  item.set_related_images((rimg_path, rimg_ann_path, rimg_fig_path))
278
284
  items.append(item)
279
285
  return items, only_modality_items, unsupported_exts
286
+
287
+ def _convert_to_pcd_if_needed(self, pcd_path: str, ext: str) -> Optional[str]:
288
+ """Convert point cloud to .pcd format if it is in another supported format."""
289
+ if ext == ".pcd":
290
+ return pcd_path
291
+ elif ext == ".bin":
292
+ if not self._validate_bin_pointcloud(pcd_path):
293
+ logger.warning(
294
+ f"The .bin pointcloud file '{pcd_path}' is not valid. Skipping conversion to .pcd."
295
+ )
296
+ return None
297
+ pcd_output_path = pcd_path.replace(".bin", ".pcd")
298
+ self._convert_bin_to_pcd(pcd_path, pcd_output_path)
299
+ return pcd_output_path
300
+
301
+ def _validate_bin_pointcloud(self, bin_file: str) -> bool:
302
+ try:
303
+ data = np.fromfile(bin_file, dtype=np.float32)
304
+ if data.size == 0:
305
+ return False
306
+
307
+ if data.size % 5 == 0:
308
+ points = data.reshape(-1, 5)
309
+ return bool(np.isfinite(points).all())
310
+
311
+ if data.size % 4 == 0:
312
+ points = data.reshape(-1, 4)
313
+ return bool(np.isfinite(points).all())
314
+
315
+ return False
316
+ except Exception:
317
+ return False
318
+
319
+ def _convert_bin_to_pcd(self, bin_file: str, pcd_file: str) -> None:
320
+ """Convert .bin point cloud file to .pcd format using open3d library (supports 4 or 5 floats per point)."""
321
+ try:
322
+ import open3d as o3d
323
+ except ImportError:
324
+ raise ImportError(
325
+ "open3d is not installed. Cannot convert .bin point cloud to .pcd format. "
326
+ "Please install open3d package to enable this feature."
327
+ )
328
+
329
+ try:
330
+ raw = np.fromfile(bin_file, dtype=np.float32)
331
+ if raw.size % 5 == 0:
332
+ arr = raw.reshape(-1, 5)
333
+ points = arr[:, 0:3]
334
+ intensity = arr[:, 3]
335
+ elif raw.size % 4 == 0:
336
+ arr = raw.reshape(-1, 4)
337
+ points = arr[:, 0:3]
338
+ intensity = arr[:, 3]
339
+ else:
340
+ raise ValueError("The number of float32 values is not a multiple of 4 or 5.")
341
+ except ValueError as e:
342
+ raise Exception(
343
+ f"Incorrect data in the BIN pointcloud file: {bin_file}. "
344
+ f"The number of float32 values must be a multiple of 4 (x,y,z,intensity) "
345
+ f"or 5 (x,y,z,intensity,ring_index). Details: {e} "
346
+ "Please ensure that the binary file contains a valid number of elements to be "
347
+ "successfully reshaped into a (N, 4) or (N, 5) array.\n"
348
+ )
349
+ # normalize intensity to [0, 1] for Open3D colors
350
+ intensity = np.nan_to_num(intensity, nan=0.0, posinf=0.0, neginf=0.0)
351
+ if intensity.size > 0:
352
+ i_min = float(np.min(intensity))
353
+ i_max = float(np.max(intensity))
354
+ if i_max > i_min:
355
+ norm_intensity = (intensity - i_min) / (i_max - i_min)
356
+ else:
357
+ norm_intensity = np.zeros_like(intensity)
358
+ else:
359
+ norm_intensity = intensity
360
+ intensity_fake_rgb = np.zeros((norm_intensity.shape[0], 3))
361
+ intensity_fake_rgb[:, 0] = norm_intensity
362
+ pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
363
+ pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
364
+ o3d.io.write_point_cloud(pcd_file, pc)
365
+ pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
366
+ pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
367
+ o3d.io.write_point_cloud(pcd_file, pc)
@@ -1,4 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
4
+ import uuid
2
5
  from os import path as osp
3
6
  from pathlib import Path
4
7
  from typing import Dict, List, Optional
@@ -37,19 +40,14 @@ from supervisely.pointcloud_annotation.pointcloud_figure import PointcloudFigure
37
40
  from supervisely.project.project_meta import ProjectMeta
38
41
  from supervisely.sly_logger import logger
39
42
  from supervisely.tiny_timer import TinyTimer
43
+ from supervisely.video_annotation.key_id_map import KeyIdMap
40
44
 
41
45
 
42
46
  class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
43
47
  """Converter for NuScenes pointcloud episodes format."""
44
48
 
45
- def __init__(
46
- self,
47
- input_data: str,
48
- labeling_interface: str,
49
- upload_as_links: bool,
50
- remote_files_map: Optional[Dict[str, str]] = None,
51
- ):
52
- super().__init__(input_data, labeling_interface, upload_as_links, remote_files_map)
49
+ _nuscenes: "NuScenes" = None # type: ignore
50
+ _custom_data: Dict = {}
53
51
 
54
52
  def __str__(self) -> str:
55
53
  return AvailablePointcloudConverters.NUSCENES
@@ -61,8 +59,10 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
61
59
  logger.warning("Please, run 'pip install nuscenes-devkit' to import NuScenes data.")
62
60
  return False
63
61
 
64
- def _contains_tables(p):
65
- return all(fs.file_exists(Path(p) / f"{name}.json") for name in helpers.TABLE_NAMES)
62
+ table_json_filenames = [f"{name}.json" for name in helpers.TABLE_NAMES]
63
+
64
+ def _contains_tables(dir_path: str) -> bool:
65
+ return all(fs.file_exists(osp.join(dir_path, table)) for table in table_json_filenames)
66
66
 
67
67
  def _filter_fn(path):
68
68
  has_tables = False
@@ -89,11 +89,13 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
89
89
  logger.debug(f"Failed to initialize NuScenes: {e}")
90
90
  return False
91
91
 
92
+ self._custom_data["nuscenes_version"] = version
93
+ self._custom_data["dataroot"] = input_path
92
94
  return True
93
95
 
94
96
  def to_supervisely(
95
97
  self,
96
- scene_samples: List[helpers.Sample],
98
+ scene_samples: Dict[str, helpers.Sample],
97
99
  meta: ProjectMeta,
98
100
  renamed_classes: dict = {},
99
101
  renamed_tags: dict = {},
@@ -101,9 +103,13 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
101
103
  token_to_obj = {}
102
104
  frames = []
103
105
  tags = []
104
- for sample_i, sample in enumerate(scene_samples):
106
+ frame_idx_to_scene_sample_token = {}
107
+ if "frame_token_map" not in self._custom_data:
108
+ self._custom_data["frame_token_map"] = {}
109
+ for sample_i, (token, sample) in enumerate(scene_samples.items()):
105
110
  figures = []
106
111
  for obj in sample.anns:
112
+ ann_token = uuid.UUID(obj.token)
107
113
  instance_token = obj.instance_token
108
114
  class_name = obj.category
109
115
  parent_obj_token = obj.parent_token
@@ -113,7 +119,9 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
113
119
  obj_class_name = renamed_classes.get(class_name, class_name)
114
120
  obj_class = meta.get_obj_class(obj_class_name)
115
121
  obj_tags = None # ! TODO: fix tags
116
- pcd_ep_obj = PointcloudEpisodeObject(obj_class, obj_tags)
122
+ pcd_ep_obj = PointcloudEpisodeObject(
123
+ obj_class, obj_tags, uuid.UUID(instance_token)
124
+ )
117
125
  # * Assign the object to the starting token
118
126
  token_to_obj[instance_token] = pcd_ep_obj
119
127
  parent_object = pcd_ep_obj
@@ -122,29 +130,41 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
122
130
  token_to_obj[instance_token] = token_to_obj[parent_obj_token]
123
131
  parent_object = token_to_obj[parent_obj_token]
124
132
  geom = obj.to_supervisely()
125
- pcd_figure = PointcloudFigure(parent_object, geom, sample_i)
133
+ pcd_figure = PointcloudFigure(parent_object, geom, sample_i, ann_token)
126
134
  figures.append(pcd_figure)
135
+ frame_idx_to_scene_sample_token[sample_i] = token
127
136
  frame = PointcloudEpisodeFrame(sample_i, figures)
128
137
  frames.append(frame)
129
138
  tag_collection = PointcloudEpisodeTagCollection(tags) if len(tags) > 0 else None
139
+ self._custom_data["frame_token_map"][self._current_ds_id] = frame_idx_to_scene_sample_token
140
+ key_uuid = uuid.UUID(token)
130
141
  return PointcloudEpisodeAnnotation(
131
142
  len(frames),
132
143
  PointcloudEpisodeObjectCollection(list(set(token_to_obj.values()))),
133
144
  PointcloudEpisodeFrameCollection(frames),
134
145
  tag_collection,
146
+ key=key_uuid,
135
147
  )
136
148
 
137
149
  def upload_dataset(self, api: Api, dataset_id: int, batch_size: int = 1, log_progress=True):
138
- nuscenes = self._nuscenes
150
+ from nuscenes import NuScenes # pylint: disable=import-error
151
+
152
+ nuscenes: NuScenes = self._nuscenes
153
+ key_id_map = KeyIdMap()
139
154
 
140
155
  tag_metas = [TagMeta(attr["name"], TagValueType.NONE) for attr in nuscenes.attribute]
141
- obj_classes = []
156
+ obj_classes = {}
142
157
  for category in nuscenes.category:
143
158
  color = nuscenes.colormap[category["name"]]
144
159
  description = helpers.trim_description(category.get("description", ""))
145
- obj_classes.append(ObjClass(category["name"], Cuboid3d, color, description=description))
160
+ token = category["token"]
161
+ obj_classes[token] = ObjClass(
162
+ category["name"], Cuboid3d, color, description=description
163
+ )
164
+
165
+ self._custom_data["classes_token_map"] = {k: v.name for k, v in obj_classes.items()}
146
166
 
147
- self._meta = ProjectMeta(obj_classes, tag_metas)
167
+ self._meta = ProjectMeta(list(obj_classes.values()), tag_metas)
148
168
  meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
149
169
 
150
170
  dataset_info = api.dataset.get_info_by_id(dataset_id)
@@ -178,12 +198,13 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
178
198
 
179
199
  for scene in nuscenes.scene:
180
200
  current_dataset_id = scene_name_to_dataset[scene["name"]].id
201
+ self._current_ds_id = current_dataset_id
181
202
 
182
203
  log = nuscenes.get("log", scene["log_token"])
183
204
  sample_token = scene["first_sample_token"]
184
205
 
185
206
  # * Extract scene's samples
186
- scene_samples: List[helpers.Sample] = []
207
+ scene_samples: Dict[str, helpers.Sample] = {}
187
208
  for i in range(scene["nbr_samples"]):
188
209
  sample = nuscenes.get("sample", sample_token)
189
210
  lidar_path, boxes, _ = nuscenes.get_sample_data(sample["data"]["LIDAR_TOP"])
@@ -203,10 +224,12 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
203
224
  nuscenes.get("attribute", attr)["name"] for attr in ann["attribute_tokens"]
204
225
  ]
205
226
  visibility = nuscenes.get("visibility", ann["visibility_token"])["level"]
227
+ ann_token = ann["token"]
206
228
 
207
229
  ann = helpers.AnnotationObject(
208
230
  name=name,
209
231
  bbox=box,
232
+ token=ann_token,
210
233
  instance_token=current_instance_token,
211
234
  parent_token=parent_token,
212
235
  category=category,
@@ -227,12 +250,15 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
227
250
  for sensor, token in sample["data"].items()
228
251
  if sensor.startswith("CAM")
229
252
  ]
230
- scene_samples.append(helpers.Sample(timestamp, lidar_path, anns, camera_data))
253
+ sample_token = sample["token"]
254
+ scene_samples[sample_token] = helpers.Sample(
255
+ timestamp, lidar_path, anns, camera_data
256
+ )
231
257
  sample_token = sample["next"]
232
258
 
233
259
  # * Convert and upload pointclouds
234
260
  frame_to_pointcloud_ids = {}
235
- for idx, sample in enumerate(scene_samples):
261
+ for idx, sample in enumerate(scene_samples.values()):
236
262
  pcd_path = sample.convert_lidar_to_supervisely()
237
263
 
238
264
  pcd_name = fs.get_file_name_with_ext(pcd_path)
@@ -275,9 +301,10 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
275
301
 
276
302
  # * Convert and upload annotations
277
303
  pcd_ann = self.to_supervisely(scene_samples, meta, renamed_classes, renamed_tags)
304
+
278
305
  try:
279
306
  api.pointcloud_episode.annotation.append(
280
- current_dataset_id, pcd_ann, frame_to_pointcloud_ids
307
+ current_dataset_id, pcd_ann, frame_to_pointcloud_ids, key_id_map=key_id_map
281
308
  )
282
309
  logger.info(f"Dataset ID:{current_dataset_id} has been successfully uploaded.")
283
310
  except Exception as e:
@@ -285,6 +312,15 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
285
312
  logger.warning(
286
313
  f"Failed to upload annotation for scene: {scene['name']}. Message: {error_msg}"
287
314
  )
315
+ key_id_map = key_id_map.to_dict()
316
+ key_id_map.pop("tags")
317
+ key_id_map.pop("videos")
318
+ self._custom_data["key_id_map"] = key_id_map
319
+
320
+ project_id = dataset_info.project_id
321
+ current_custom_data = api.project.get_custom_data(project_id)
322
+ current_custom_data.update(self._custom_data)
323
+ api.project.update_custom_data(project_id, current_custom_data)
288
324
 
289
325
  if log_progress:
290
326
  if is_development():
@@ -1,3 +1,4 @@
1
+ from dataclasses import dataclass, field
1
2
  from datetime import datetime
2
3
  from os import path as osp
3
4
  from pathlib import Path
@@ -52,6 +53,7 @@ def trim_description(description: str, max_length: int = 255) -> str:
52
53
  return description
53
54
 
54
55
 
56
+ @dataclass
55
57
  class AnnotationObject:
56
58
  """
57
59
  A class to represent an annotation object in the NuScenes dataset.
@@ -60,6 +62,8 @@ class AnnotationObject:
60
62
  :type name: str
61
63
  :param bbox: The bounding box coordinates in NuScenes format
62
64
  :type bbox: np.ndarray
65
+ :param token: The unique token identifying the annotation object
66
+ :type token: str
63
67
  :param instance_token: The instance token associated with the annotation object
64
68
  :type instance_token: str
65
69
  :param parent_token: The token of instance preceding the current object instance
@@ -71,25 +75,14 @@ class AnnotationObject:
71
75
  :param visibility: The visibility level of the annotation object
72
76
  :type visibility: str
73
77
  """
74
-
75
- def __init__(
76
- self,
77
- name: str,
78
- bbox: np.ndarray,
79
- instance_token: str,
80
- parent_token: str,
81
- category: str,
82
- attributes: List[str],
83
- visibility: str,
84
- ):
85
- self.name = name
86
- self.bbox = bbox
87
- self.instance_token = instance_token
88
- self.parent_token = parent_token
89
-
90
- self.category = category
91
- self.attributes = attributes
92
- self.visibility = visibility
78
+ name: str
79
+ bbox: np.ndarray
80
+ token: str
81
+ instance_token: str
82
+ parent_token: str
83
+ category: str
84
+ attributes: List[str]
85
+ visibility: str
93
86
 
94
87
  def to_supervisely(self) -> Cuboid3d:
95
88
  box = self.convert_nuscenes_to_BEVBox3D()
@@ -213,38 +206,19 @@ class CamData:
213
206
  return (sly_path_img, img_info)
214
207
 
215
208
 
209
+ @dataclass
216
210
  class Sample:
217
211
  """
218
212
  A class to represent a sample from the NuScenes dataset.
219
213
  """
220
-
221
- def __init__(
222
- self,
223
- timestamp: float,
224
- lidar_path: str,
225
- anns: List[AnnotationObject],
226
- cam_data: List[CamData],
227
- ):
228
- self._timestamp = datetime.utcfromtimestamp(timestamp / 1e6).isoformat()
229
- self._lidar_path = lidar_path
230
- self._anns = anns
231
- self._cam_data = cam_data
232
-
233
- @property
234
- def timestamp(self) -> str:
235
- return self._timestamp
236
-
237
- @property
238
- def lidar_path(self) -> str:
239
- return self._lidar_path
240
-
241
- @property
242
- def anns(self) -> List[AnnotationObject]:
243
- return self._anns
244
-
245
- @property
246
- def cam_data(self) -> List[CamData]:
247
- return self._cam_data
214
+ timestamp_us: float
215
+ lidar_path: str
216
+ anns: List[AnnotationObject]
217
+ cam_data: List[CamData]
218
+ timestamp: str = field(init=False)
219
+
220
+ def __post_init__(self):
221
+ self.timestamp = datetime.utcfromtimestamp(self.timestamp_us / 1e6).isoformat()
248
222
 
249
223
  @staticmethod
250
224
  def generate_boxes(nuscenes, boxes: List) -> Generator:
@@ -1,3 +1,4 @@
1
1
  # Video
2
2
  from supervisely.convert.video.mot.mot_converter import MOTConverter
3
3
  from supervisely.convert.video.sly.sly_video_converter import SLYVideoConverter
4
+ from supervisely.convert.video.multi_view.multi_view import MultiViewVideoConverter
File without changes