supervisely 6.73.410__py3-none-any.whl → 6.73.470__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.

Potentially problematic release.


This version of supervisely might be problematic. Click here for more details.

Files changed (190) hide show
  1. supervisely/__init__.py +136 -1
  2. supervisely/_utils.py +81 -0
  3. supervisely/annotation/json_geometries_map.py +2 -0
  4. supervisely/annotation/label.py +80 -3
  5. supervisely/api/annotation_api.py +9 -9
  6. supervisely/api/api.py +67 -43
  7. supervisely/api/app_api.py +72 -5
  8. supervisely/api/dataset_api.py +108 -33
  9. supervisely/api/entity_annotation/figure_api.py +113 -49
  10. supervisely/api/image_api.py +82 -0
  11. supervisely/api/module_api.py +10 -0
  12. supervisely/api/nn/deploy_api.py +15 -9
  13. supervisely/api/nn/ecosystem_models_api.py +201 -0
  14. supervisely/api/nn/neural_network_api.py +12 -3
  15. supervisely/api/pointcloud/pointcloud_api.py +38 -0
  16. supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
  17. supervisely/api/project_api.py +213 -6
  18. supervisely/api/task_api.py +11 -1
  19. supervisely/api/video/video_annotation_api.py +4 -2
  20. supervisely/api/video/video_api.py +79 -1
  21. supervisely/api/video/video_figure_api.py +24 -11
  22. supervisely/api/volume/volume_api.py +38 -0
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +14 -6
  25. supervisely/app/fastapi/__init__.py +1 -0
  26. supervisely/app/fastapi/custom_static_files.py +1 -1
  27. supervisely/app/fastapi/multi_user.py +88 -0
  28. supervisely/app/fastapi/subapp.py +175 -42
  29. supervisely/app/fastapi/templating.py +1 -1
  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 +11 -1
  35. supervisely/app/widgets/agent_selector/template.html +1 -0
  36. supervisely/app/widgets/card/card.py +20 -0
  37. supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
  38. supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
  39. supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
  40. supervisely/app/widgets/dialog/dialog.py +12 -0
  41. supervisely/app/widgets/dialog/template.html +2 -1
  42. supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
  43. supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
  44. supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
  45. supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
  46. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
  47. supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
  48. supervisely/app/widgets/fast_table/fast_table.py +713 -126
  49. supervisely/app/widgets/fast_table/script.js +492 -95
  50. supervisely/app/widgets/fast_table/style.css +54 -0
  51. supervisely/app/widgets/fast_table/template.html +45 -5
  52. supervisely/app/widgets/heatmap/__init__.py +0 -0
  53. supervisely/app/widgets/heatmap/heatmap.py +523 -0
  54. supervisely/app/widgets/heatmap/script.js +378 -0
  55. supervisely/app/widgets/heatmap/style.css +227 -0
  56. supervisely/app/widgets/heatmap/template.html +21 -0
  57. supervisely/app/widgets/input_tag/input_tag.py +102 -15
  58. supervisely/app/widgets/input_tag_list/__init__.py +0 -0
  59. supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
  60. supervisely/app/widgets/input_tag_list/template.html +70 -0
  61. supervisely/app/widgets/radio_table/radio_table.py +10 -2
  62. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  63. supervisely/app/widgets/radio_tabs/template.html +1 -0
  64. supervisely/app/widgets/select/select.py +6 -4
  65. supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
  66. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
  67. supervisely/app/widgets/table/table.py +68 -13
  68. supervisely/app/widgets/tabs/tabs.py +22 -6
  69. supervisely/app/widgets/tabs/template.html +5 -1
  70. supervisely/app/widgets/transfer/style.css +3 -0
  71. supervisely/app/widgets/transfer/template.html +3 -1
  72. supervisely/app/widgets/transfer/transfer.py +48 -45
  73. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  74. supervisely/convert/image/csv/csv_converter.py +24 -15
  75. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
  76. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
  77. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
  78. supervisely/convert/video/video_converter.py +2 -2
  79. supervisely/geometry/polyline_3d.py +110 -0
  80. supervisely/io/env.py +161 -1
  81. supervisely/nn/artifacts/__init__.py +1 -1
  82. supervisely/nn/artifacts/artifacts.py +10 -2
  83. supervisely/nn/artifacts/detectron2.py +1 -0
  84. supervisely/nn/artifacts/hrda.py +1 -0
  85. supervisely/nn/artifacts/mmclassification.py +20 -0
  86. supervisely/nn/artifacts/mmdetection.py +5 -3
  87. supervisely/nn/artifacts/mmsegmentation.py +1 -0
  88. supervisely/nn/artifacts/ritm.py +1 -0
  89. supervisely/nn/artifacts/rtdetr.py +1 -0
  90. supervisely/nn/artifacts/unet.py +1 -0
  91. supervisely/nn/artifacts/utils.py +3 -0
  92. supervisely/nn/artifacts/yolov5.py +2 -0
  93. supervisely/nn/artifacts/yolov8.py +1 -0
  94. supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
  95. supervisely/nn/experiments.py +9 -0
  96. supervisely/nn/inference/cache.py +37 -17
  97. supervisely/nn/inference/gui/serving_gui_template.py +39 -13
  98. supervisely/nn/inference/inference.py +953 -211
  99. supervisely/nn/inference/inference_request.py +15 -8
  100. supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
  101. supervisely/nn/inference/object_detection/object_detection.py +1 -0
  102. supervisely/nn/inference/predict_app/__init__.py +0 -0
  103. supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
  104. supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
  105. supervisely/nn/inference/predict_app/gui/gui.py +915 -0
  106. supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
  107. supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
  108. supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
  109. supervisely/nn/inference/predict_app/gui/preview.py +93 -0
  110. supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
  111. supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
  112. supervisely/nn/inference/predict_app/gui/utils.py +399 -0
  113. supervisely/nn/inference/predict_app/predict_app.py +176 -0
  114. supervisely/nn/inference/session.py +47 -39
  115. supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
  116. supervisely/nn/inference/tracking/point_tracking.py +5 -1
  117. supervisely/nn/inference/tracking/tracker_interface.py +4 -0
  118. supervisely/nn/inference/uploader.py +9 -5
  119. supervisely/nn/model/model_api.py +44 -22
  120. supervisely/nn/model/prediction.py +15 -1
  121. supervisely/nn/model/prediction_session.py +70 -14
  122. supervisely/nn/prediction_dto.py +7 -0
  123. supervisely/nn/tracker/__init__.py +6 -8
  124. supervisely/nn/tracker/base_tracker.py +54 -0
  125. supervisely/nn/tracker/botsort/__init__.py +1 -0
  126. supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
  127. supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
  128. supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
  129. supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
  130. supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
  131. supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
  132. supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
  133. supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
  134. supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
  135. supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
  136. supervisely/nn/tracker/botsort_tracker.py +273 -0
  137. supervisely/nn/tracker/calculate_metrics.py +264 -0
  138. supervisely/nn/tracker/utils.py +273 -0
  139. supervisely/nn/tracker/visualize.py +520 -0
  140. supervisely/nn/training/gui/gui.py +152 -49
  141. supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
  142. supervisely/nn/training/gui/model_selector.py +8 -6
  143. supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
  144. supervisely/nn/training/gui/training_artifacts.py +3 -1
  145. supervisely/nn/training/train_app.py +225 -46
  146. supervisely/project/pointcloud_episode_project.py +12 -8
  147. supervisely/project/pointcloud_project.py +12 -8
  148. supervisely/project/project.py +221 -75
  149. supervisely/template/experiment/experiment.html.jinja +105 -55
  150. supervisely/template/experiment/experiment_generator.py +258 -112
  151. supervisely/template/experiment/header.html.jinja +31 -13
  152. supervisely/template/experiment/sly-style.css +7 -2
  153. supervisely/versions.json +3 -1
  154. supervisely/video/sampling.py +42 -20
  155. supervisely/video/video.py +41 -12
  156. supervisely/video_annotation/video_figure.py +38 -4
  157. supervisely/volume/stl_converter.py +2 -0
  158. supervisely/worker_api/agent_rpc.py +24 -1
  159. supervisely/worker_api/rpc_servicer.py +31 -7
  160. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
  161. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
  162. supervisely_lib/__init__.py +6 -1
  163. supervisely/app/widgets/experiment_selector/style.css +0 -27
  164. supervisely/app/widgets/experiment_selector/template.html +0 -61
  165. supervisely/nn/tracker/bot_sort/__init__.py +0 -21
  166. supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
  167. supervisely/nn/tracker/bot_sort/matching.py +0 -127
  168. supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
  169. supervisely/nn/tracker/deep_sort/__init__.py +0 -6
  170. supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
  171. supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
  172. supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
  173. supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
  174. supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
  175. supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
  176. supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
  177. supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
  178. supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
  179. supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
  180. supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
  181. supervisely/nn/tracker/tracker.py +0 -285
  182. supervisely/nn/tracker/utils/kalman_filter.py +0 -492
  183. supervisely/nn/tracking/__init__.py +0 -1
  184. supervisely/nn/tracking/boxmot.py +0 -114
  185. supervisely/nn/tracking/tracking.py +0 -24
  186. /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
  187. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
  188. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
  189. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
  190. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/top_level.txt +0 -0
@@ -41,7 +41,9 @@ class Transfer(Widget):
41
41
  :param right_checked: A list of keys of the items in the right (target) list, which should be checked at widget
42
42
  initialization. Defaults to None.
43
43
  :type right_checked: List[str], optional
44
-
44
+ :param width: The width of the widget in pixels. The default and minimum width is 150 pixels.
45
+ :type width: int, optional
46
+
45
47
  :Methods:
46
48
  get_transferred_items(): returns the list of keys of the items, which are currently displayed in the
47
49
  right (target) list.
@@ -88,7 +90,7 @@ class Transfer(Widget):
88
90
  """
89
91
  class Routes:
90
92
  VALUE_CHANGED = "value_changed"
91
-
93
+
92
94
  class Item:
93
95
  """
94
96
  Class for representing items in the Transfer widget.
@@ -123,45 +125,49 @@ class Transfer(Widget):
123
125
  else:
124
126
  self.label = label
125
127
  self.disabled = disabled
126
-
128
+
127
129
  def to_json(self):
128
130
  return {"key": self.key, "label": self.label, "disabled": self.disabled}
129
-
130
- def __init__(self,
131
- items: Optional[Union[List[Item], List[str]]] = None,
132
- transferred_items: Optional[List[str]] = None,
133
- widget_id: Optional[str] = None,
134
- filterable: Optional[bool] = False,
135
- filter_placeholder: Optional[str] = None,
136
- titles: Optional[List[str]] = None,
137
- button_texts: Optional[List[str]] = None,
138
- left_checked: Optional[List[str]] = None,
139
- right_checked: Optional[List[str]] = None):
140
-
131
+
132
+ def __init__(
133
+ self,
134
+ items: Optional[Union[List[Item], List[str]]] = None,
135
+ transferred_items: Optional[List[str]] = None,
136
+ widget_id: Optional[str] = None,
137
+ filterable: Optional[bool] = False,
138
+ filter_placeholder: Optional[str] = None,
139
+ titles: Optional[List[str]] = None,
140
+ button_texts: Optional[List[str]] = None,
141
+ left_checked: Optional[List[str]] = None,
142
+ right_checked: Optional[List[str]] = None,
143
+ width: int = 150,
144
+ ):
145
+
141
146
  self._changes_handled = False
142
147
  self._items = []
143
148
  self._transferred_items = []
144
-
149
+
145
150
  if items:
146
151
  self._items = self.__checked_items(items)
147
-
152
+
148
153
  if transferred_items:
149
154
  self._transferred_items = self.__checked_transferred_items(transferred_items)
150
-
155
+
151
156
  # If wrong items are specified, items won't be checked.
152
157
  self._left_checked = left_checked
153
158
  self._right_checked = right_checked
154
-
159
+
155
160
  self._filterable = filterable
156
161
  self._filter_placeholder = filter_placeholder
157
-
162
+
163
+ self._width = max(width, 150)
164
+
158
165
  self._titles = titles if titles is not None else ["Source", "Target"]
159
-
166
+
160
167
  self._button_texts = button_texts
161
-
168
+
162
169
  super().__init__(widget_id=widget_id, file_path=__file__)
163
-
164
-
170
+
165
171
  def __checked_items(self, items: Optional[Union[List[Item], List[str]]]) -> List[Transfer.Item]:
166
172
  """
167
173
  If the list of items is specified as a list of strings, they will be converted to Transfer.Item objects. List of
@@ -183,17 +189,17 @@ class Transfer(Widget):
183
189
  if len(set(items)) != len(items):
184
190
  # If the keys of the items are not unique, an error will be raised.
185
191
  raise ValueError("The keys of the items should be unique.")
186
-
192
+
187
193
  checked_items = [Transfer.Item(key=item) for item in items]
188
194
  else:
189
195
  # If items are specified as Transfer.Item objects, they will be checked for uniqueness.
190
196
  if len({item.key for item in items}) != len(items):
191
197
  # If the keys of the items are not unique, an error will be raised.
192
198
  raise ValueError("The keys of the items should be unique.")
193
-
199
+
194
200
  checked_items = items
195
201
  return checked_items
196
-
202
+
197
203
  def __checked_transferred_items(self, transferred_items: List[str]) -> List[str]:
198
204
  """
199
205
  If the self._items is specified, the list of transferred items will be checked for the keys of the items. Since
@@ -223,7 +229,7 @@ class Transfer(Widget):
223
229
  "the keys of the items specified in the 'items' argument.")
224
230
  else:
225
231
  return transferred_items
226
-
232
+
227
233
  def get_json_data(self) -> Dict[str, Union[List[Dict[str, Union[str, bool]]], None]]:
228
234
  """
229
235
  Returns the data of the widget in JSON format.
@@ -243,7 +249,7 @@ class Transfer(Widget):
243
249
  res["items"] = [item.to_json() for item in self._items]
244
250
 
245
251
  return res
246
-
252
+
247
253
  def get_json_state(self) -> Dict[str, List[str]]:
248
254
  """
249
255
  Returns the state of the widget in JSON format.
@@ -256,9 +262,9 @@ class Transfer(Widget):
256
262
  """
257
263
 
258
264
  transferred_items = self._transferred_items
259
-
265
+
260
266
  return {"transferred_items": transferred_items}
261
-
267
+
262
268
  def get_transferred_items(self) -> List[str]:
263
269
  """
264
270
  Returns the list of transferred items.
@@ -268,8 +274,7 @@ class Transfer(Widget):
268
274
  """
269
275
 
270
276
  return StateJson()[self.widget_id]["transferred_items"]
271
-
272
-
277
+
273
278
  def get_untransferred_items(self) -> List[str]:
274
279
  """
275
280
  Returns the list of untransferred items.
@@ -279,8 +284,7 @@ class Transfer(Widget):
279
284
  """
280
285
 
281
286
  return [item.key for item in self._items if item.key not in self.get_transferred_items()]
282
-
283
-
287
+
284
288
  def value_changed(self, func: Callable) -> Callable:
285
289
  """
286
290
  Decorates a function which will be called when the the items in right list are changed (moved in or out of the list).
@@ -305,22 +309,21 @@ class Transfer(Widget):
305
309
  print(items.untransferred_items) # ["item3"]
306
310
  """
307
311
 
308
-
309
312
  route_path = self.get_route_path(Transfer.Routes.VALUE_CHANGED)
310
313
  server = self._sly_app.get_server()
311
314
  self._changes_handled = True
312
315
 
313
316
  @server.post(route_path)
314
317
  def _click():
315
-
318
+
316
319
  Items = namedtuple("Items", ["transferred_items", "untransferred_items"])
317
-
320
+
318
321
  res = Items(transferred_items=self.get_transferred_items(), untransferred_items=self.get_untransferred_items())
319
-
322
+
320
323
  func(res)
321
324
 
322
325
  return _click
323
-
326
+
324
327
  def set_items(self, items: Union[List[Transfer.Item], List[str]]):
325
328
  """
326
329
  Sets the list of items for the widget.
@@ -345,7 +348,7 @@ class Transfer(Widget):
345
348
 
346
349
  # As you can see, the list of items was replaced with the new one.
347
350
  """
348
-
351
+
349
352
  if items:
350
353
  self._items = self.__checked_items(items)
351
354
  else:
@@ -365,7 +368,7 @@ class Transfer(Widget):
365
368
  self._transferred_items = self.__checked_transferred_items(transferred_items)
366
369
  self.update_state()
367
370
  StateJson().send_changes()
368
-
371
+
369
372
  def add(self, items: Union[List[Item], List[str]]):
370
373
  """
371
374
  Adds new items to the current list of items.
@@ -391,14 +394,14 @@ class Transfer(Widget):
391
394
  """
392
395
 
393
396
  items = self.__checked_items(items)
394
-
397
+
395
398
  if any([item.key in [item.key for item in self._items] for item in items]):
396
399
  raise ValueError("The 'items' argument should not contain any items with the same key as the items in the current list.")
397
400
  else:
398
401
  self._items.extend(items)
399
402
  self.update_data()
400
403
  DataJson().send_changes()
401
-
404
+
402
405
  def remove(self, items_keys: List[str]):
403
406
  """
404
407
  Removes items from the current list of items.
@@ -416,7 +419,7 @@ class Transfer(Widget):
416
419
  self.update_state()
417
420
  DataJson().send_changes()
418
421
  StateJson().send_changes()
419
-
422
+
420
423
  def get_items_keys(self) -> List[str]:
421
424
  """
422
425
  Returns the list of keys of the items.
@@ -264,6 +264,8 @@ class TreeSelect(Widget):
264
264
 
265
265
  def _get_all_items(items: List[TreeSelect.Item]) -> List[TreeSelect.Item]:
266
266
  res = []
267
+ if not items:
268
+ return res
267
269
  for item in items:
268
270
  res.append(item)
269
271
  res.extend(_get_all_items(item.children))
@@ -24,6 +24,7 @@ from supervisely.io.fs import (
24
24
  get_file_name_with_ext,
25
25
  list_files_recursively,
26
26
  )
27
+ from supervisely.io.env import team_id
27
28
  from supervisely.io.json import load_json_file
28
29
  from supervisely.project.project_settings import LabelingInterface
29
30
 
@@ -78,16 +79,16 @@ class CSVConverter(ImageConverter):
78
79
  }
79
80
 
80
81
  def __init__(
81
- self,
82
- input_data: str,
83
- labeling_interface: Optional[Union[LabelingInterface, str]],
84
- upload_as_links: bool,
85
- remote_files_map: Optional[Dict[str, str]] = None,
82
+ self,
83
+ input_data: str,
84
+ labeling_interface: Optional[Union[LabelingInterface, str]],
85
+ upload_as_links: bool,
86
+ remote_files_map: Optional[Dict[str, str]] = None,
86
87
  ):
87
88
  super().__init__(input_data, labeling_interface, upload_as_links, remote_files_map)
88
89
 
90
+ self._supports_links = True
89
91
  self._csv_reader = None
90
- self._team_id = None
91
92
 
92
93
  def __str__(self):
93
94
  return AvailableImageConverters.CSV
@@ -121,6 +122,12 @@ class CSVConverter(ImageConverter):
121
122
 
122
123
  full_path = valid_files[0]
123
124
 
125
+ if self.upload_as_links and self._supports_links:
126
+ for local_path, remote_path in self._remote_files_map.items():
127
+ if local_path.endswith(full_path):
128
+ self._api.storage.download(self._team_id, remote_path, local_path)
129
+ break
130
+
124
131
  file_ext = get_file_ext(full_path)
125
132
  if file_ext in self.conversion_functions:
126
133
  csv_full_path = os.path.splitext(full_path)[0] + ".csv"
@@ -147,7 +154,7 @@ class CSVConverter(ImageConverter):
147
154
  team_files = False
148
155
  break
149
156
  if item_path is None:
150
- logger.warn(f"Failed to find image path in row: {row}. Skipping.")
157
+ logger.warning(f"Failed to find image path in row: {row}. Skipping.")
151
158
  continue
152
159
  ann_data = row.get("tag")
153
160
  item = CSVConverter.Item(
@@ -192,7 +199,7 @@ class CSVConverter(ImageConverter):
192
199
  ann_json = csv_helper.rename_in_json(ann_json, renamed_classes, renamed_tags)
193
200
  return Annotation.from_json(ann_json, meta)
194
201
  except Exception as e:
195
- logger.warn(f"Failed to convert annotation: {repr(e)}")
202
+ logger.warning(f"Failed to convert annotation: {repr(e)}")
196
203
  return item.create_empty_annotation()
197
204
 
198
205
  def process_remote_image(
@@ -209,19 +216,21 @@ class CSVConverter(ImageConverter):
209
216
  image_path = image_path.strip()
210
217
  if is_team_file:
211
218
  if not api.file.exists(team_id, image_path):
212
- logger.warn(f"File {image_path} not found in Team Files. Skipping...")
219
+ logger.warning(f"File {image_path} not found in Team Files. Skipping...")
213
220
  return None
214
221
  team_file_image_info = api.file.list(team_id, image_path)
215
222
  image_path = team_file_image_info[0]["fullStorageUrl"]
216
223
  if not image_path:
217
- logger.warn(f"Failed to get full storage URL for file '{image_path}'. Skipping...")
224
+ logger.warning(
225
+ f"Failed to get full storage URL for file '{image_path}'. Skipping..."
226
+ )
218
227
  return None
219
228
 
220
229
  extension = os.path.splitext(image_path)[1]
221
230
  if not extension:
222
- logger.warn(f"FYI: Image [{image_path}] doesn't have extension.")
231
+ logger.warning(f"FYI: Image [{image_path}] doesn't have extension.")
223
232
  elif extension.lower() not in SUPPORTED_IMG_EXTS:
224
- logger.warn(
233
+ logger.warning(
225
234
  f"Image [{image_path}] has unsupported extension [{extension}]. Skipping..."
226
235
  )
227
236
  return None
@@ -234,7 +243,7 @@ class CSVConverter(ImageConverter):
234
243
  force_metadata_for_links=force_metadata,
235
244
  )
236
245
  except Exception:
237
- logger.warn(f"Failed to upload image {image_name}. Skipping...")
246
+ logger.warning(f"Failed to link image {image_name}. Skipping...")
238
247
  return None
239
248
  if progress_cb is not None:
240
249
  progress_cb(1)
@@ -312,7 +321,7 @@ class CSVConverter(ImageConverter):
312
321
  success = False
313
322
  continue
314
323
  if item.name not in info.name:
315
- logger.warn(
324
+ logger.warning(
316
325
  f"Batched image with name '{item.name}' doesn't match uploaded image name '{info.name}'"
317
326
  )
318
327
  success = False
@@ -339,4 +348,4 @@ class CSVConverter(ImageConverter):
339
348
  if success:
340
349
  logger.info(f"Dataset ID:'{dataset_id}' has been successfully uploaded.")
341
350
  else:
342
- logger.warn(f"Dataset ID:'{dataset_id}' has been uploaded.")
351
+ logger.warning(f"Dataset ID:'{dataset_id}' has been uploaded.")
@@ -1,13 +1,15 @@
1
1
  import os
2
- from typing import Dict, Optional
2
+ import uuid
3
+ from typing import Dict, List, Optional
3
4
 
4
5
  import supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_helper as helpers
5
6
  import supervisely.io.fs as fs
6
- from supervisely import PointcloudAnnotation, PointcloudObject
7
+ from supervisely import KeyIdMap, PointcloudAnnotation, PointcloudObject
7
8
  from supervisely._utils import is_development
8
9
  from supervisely.annotation.obj_class import ObjClass
9
10
  from supervisely.annotation.tag_meta import TagMeta, TagValueType
10
11
  from supervisely.api.api import Api, ApiField
12
+ from supervisely.api.dataset_api import DatasetInfo
11
13
  from supervisely.convert.base_converter import AvailablePointcloudConverters
12
14
  from supervisely.convert.pointcloud.pointcloud_converter import PointcloudConverter
13
15
  from supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_converter import (
@@ -29,22 +31,9 @@ from supervisely.sly_logger import logger
29
31
  class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
30
32
  """Converter for NuScenes pointcloud format."""
31
33
 
32
- def __init__(
33
- self,
34
- input_data: str,
35
- labeling_interface: str,
36
- upload_as_links: bool,
37
- remote_files_map: Optional[Dict[str, str]] = None,
38
- ):
39
- super().__init__(input_data, labeling_interface, upload_as_links, remote_files_map)
40
- self._nuscenes = None
41
-
42
- def __str__(self) -> str:
43
- return AvailablePointcloudConverters.NUSCENES
44
-
45
34
  def to_supervisely(
46
35
  self,
47
- scene_sample,
36
+ scene_sample: helpers.Sample,
48
37
  meta: ProjectMeta,
49
38
  renamed_classes: dict = {},
50
39
  renamed_tags: dict = {},
@@ -68,29 +57,27 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
68
57
  return PointcloudAnnotation(PointcloudObjectCollection(objs), figures)
69
58
 
70
59
  def upload_dataset(self, api: Api, dataset_id: int, batch_size: int = 1, log_progress=True):
71
- nuscenes = self._nuscenes
60
+ from nuscenes.nuscenes import NuScenes # pylint: disable=import-error
61
+
62
+ nuscenes: NuScenes = self._nuscenes
63
+
64
+ key_id_map = KeyIdMap()
72
65
 
73
66
  tag_metas = [TagMeta(attr["name"], TagValueType.NONE) for attr in nuscenes.attribute]
74
67
  obj_classes = []
68
+ classes_token_map = {}
75
69
  for category in nuscenes.category:
76
70
  color = nuscenes.colormap[category["name"]]
77
- description = category["description"]
78
- if len(description) > 255:
79
- # * Trim description to fit into 255 characters limit
80
- sentences = description.split(".")
81
- trimmed_description = ""
82
- for sentence in sentences:
83
- if len(trimmed_description) + len(sentence) + 1 > 255:
84
- break
85
- trimmed_description += sentence + "."
86
- description = trimmed_description.strip()
71
+ description = helpers.trim_description(category["description"])
87
72
  obj_classes.append(ObjClass(category["name"], Cuboid3d, color, description=description))
73
+ classes_token_map[category["token"]] = category["name"]
74
+ self._custom_data["classes_token_map"] = classes_token_map
88
75
 
89
76
  self._meta = ProjectMeta(obj_classes, tag_metas)
90
77
  meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
91
78
 
92
79
  dataset_info = api.dataset.get_info_by_id(dataset_id)
93
- scene_name_to_dataset = {}
80
+ scene_name_to_dataset: Dict[str, DatasetInfo] = {}
94
81
 
95
82
  scene_names = [scene["name"] for scene in nuscenes.scene]
96
83
  scene_cnt = len(scene_names)
@@ -116,6 +103,7 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
116
103
  else:
117
104
  progress_cb = None
118
105
 
106
+ self._custom_data["frame_token_map"] = {}
119
107
  for scene in nuscenes.scene:
120
108
  current_dataset_id = scene_name_to_dataset[scene["name"]].id
121
109
 
@@ -123,9 +111,11 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
123
111
  sample_token = scene["first_sample_token"]
124
112
 
125
113
  # * Extract scene's samples
126
- scene_samples = []
114
+ scene_samples: List[helpers.Sample] = []
115
+ frame_token_map = {}
127
116
  for i in range(scene["nbr_samples"]):
128
117
  sample = nuscenes.get("sample", sample_token)
118
+ frame_token_map[sample["token"]] = i
129
119
  lidar_path, boxes, _ = nuscenes.get_sample_data(sample["data"]["LIDAR_TOP"])
130
120
  if not os.path.exists(lidar_path):
131
121
  logger.warning(f'Scene "{scene["name"]}" has no LIDAR data.')
@@ -145,17 +135,18 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
145
135
  ]
146
136
  visibility = nuscenes.get("visibility", ann["visibility_token"])["level"]
147
137
 
148
- anns.append(
149
- helpers.AnnotationObject(
150
- name,
151
- box,
152
- current_instance_token,
153
- parent_token,
154
- category,
155
- attributes,
156
- visibility,
157
- )
138
+ ann_uuid = uuid.UUID(ann["token"])
139
+ ann = helpers.AnnotationObject(
140
+ name=name,
141
+ bbox=box,
142
+ token=ann_uuid,
143
+ instance_token=current_instance_token,
144
+ parent_token=parent_token,
145
+ category=category,
146
+ attributes=attributes,
147
+ visibility=visibility,
158
148
  )
149
+ anns.append(ann)
159
150
 
160
151
  # get camera data
161
152
  sample_data = nuscenes.get("sample_data", sample["data"]["LIDAR_TOP"])
@@ -171,13 +162,14 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
171
162
  ]
172
163
  scene_samples.append(helpers.Sample(timestamp, lidar_path, anns, camera_data))
173
164
  sample_token = sample["next"]
165
+ self._custom_data["frame_token_map"][current_dataset_id] = frame_token_map
174
166
 
175
167
  # * Convert and upload pointclouds w/ annotations
176
168
  for idx, sample in enumerate(scene_samples):
177
169
  pcd_ann = self.to_supervisely(sample, meta, renamed_classes, renamed_tags)
178
170
 
179
171
  pcd_path = sample.convert_lidar_to_supervisely()
180
- pcd_name = fs.get_file_name(pcd_path)
172
+ pcd_name = fs.get_file_name_with_ext(pcd_path)
181
173
  pcd_meta = {
182
174
  "frame": idx,
183
175
  "vehicle": log["vehicle"],
@@ -191,7 +183,7 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
191
183
  pcd_id = info.id
192
184
  # * Upload pointcloud annotation
193
185
  try:
194
- api.pointcloud.annotation.append(pcd_id, pcd_ann)
186
+ api.pointcloud.annotation.append(pcd_id, pcd_ann, key_id_map)
195
187
  except Exception as e:
196
188
  error_msg = getattr(getattr(e, "response", e), "text", str(e))
197
189
  logger.warning(
@@ -222,6 +214,16 @@ class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
222
214
 
223
215
  logger.info(f"Dataset ID:{current_dataset_id} has been successfully uploaded.")
224
216
 
217
+ key_id_map = key_id_map.to_dict()
218
+ key_id_map.pop("tags")
219
+ key_id_map.pop("videos")
220
+ self._custom_data["key_id_map"] = key_id_map
221
+
222
+ project_id = dataset_info.project_id
223
+ current_custom_data = api.project.get_custom_data(project_id)
224
+ current_custom_data.update(self._custom_data)
225
+ api.project.update_custom_data(project_id, current_custom_data)
226
+
225
227
  if log_progress:
226
228
  if is_development():
227
229
  progress.close()