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.
- supervisely/__init__.py +136 -1
- supervisely/_utils.py +81 -0
- supervisely/annotation/json_geometries_map.py +2 -0
- supervisely/annotation/label.py +80 -3
- supervisely/api/annotation_api.py +9 -9
- supervisely/api/api.py +67 -43
- supervisely/api/app_api.py +72 -5
- supervisely/api/dataset_api.py +108 -33
- supervisely/api/entity_annotation/figure_api.py +113 -49
- supervisely/api/image_api.py +82 -0
- supervisely/api/module_api.py +10 -0
- supervisely/api/nn/deploy_api.py +15 -9
- supervisely/api/nn/ecosystem_models_api.py +201 -0
- supervisely/api/nn/neural_network_api.py +12 -3
- supervisely/api/pointcloud/pointcloud_api.py +38 -0
- supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
- supervisely/api/project_api.py +213 -6
- supervisely/api/task_api.py +11 -1
- supervisely/api/video/video_annotation_api.py +4 -2
- supervisely/api/video/video_api.py +79 -1
- supervisely/api/video/video_figure_api.py +24 -11
- supervisely/api/volume/volume_api.py +38 -0
- supervisely/app/__init__.py +1 -1
- supervisely/app/content.py +14 -6
- supervisely/app/fastapi/__init__.py +1 -0
- supervisely/app/fastapi/custom_static_files.py +1 -1
- supervisely/app/fastapi/multi_user.py +88 -0
- supervisely/app/fastapi/subapp.py +175 -42
- supervisely/app/fastapi/templating.py +1 -1
- supervisely/app/fastapi/websocket.py +77 -9
- supervisely/app/singleton.py +21 -0
- supervisely/app/v1/app_service.py +18 -2
- supervisely/app/v1/constants.py +7 -1
- supervisely/app/widgets/__init__.py +11 -1
- supervisely/app/widgets/agent_selector/template.html +1 -0
- supervisely/app/widgets/card/card.py +20 -0
- supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
- supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
- supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
- supervisely/app/widgets/dialog/dialog.py +12 -0
- supervisely/app/widgets/dialog/template.html +2 -1
- supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
- supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
- supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
- supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
- supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
- supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
- supervisely/app/widgets/fast_table/fast_table.py +713 -126
- supervisely/app/widgets/fast_table/script.js +492 -95
- supervisely/app/widgets/fast_table/style.css +54 -0
- supervisely/app/widgets/fast_table/template.html +45 -5
- supervisely/app/widgets/heatmap/__init__.py +0 -0
- supervisely/app/widgets/heatmap/heatmap.py +523 -0
- supervisely/app/widgets/heatmap/script.js +378 -0
- supervisely/app/widgets/heatmap/style.css +227 -0
- supervisely/app/widgets/heatmap/template.html +21 -0
- supervisely/app/widgets/input_tag/input_tag.py +102 -15
- supervisely/app/widgets/input_tag_list/__init__.py +0 -0
- supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
- supervisely/app/widgets/input_tag_list/template.html +70 -0
- supervisely/app/widgets/radio_table/radio_table.py +10 -2
- supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
- supervisely/app/widgets/radio_tabs/template.html +1 -0
- supervisely/app/widgets/select/select.py +6 -4
- supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
- supervisely/app/widgets/table/table.py +68 -13
- supervisely/app/widgets/tabs/tabs.py +22 -6
- supervisely/app/widgets/tabs/template.html +5 -1
- supervisely/app/widgets/transfer/style.css +3 -0
- supervisely/app/widgets/transfer/template.html +3 -1
- supervisely/app/widgets/transfer/transfer.py +48 -45
- supervisely/app/widgets/tree_select/tree_select.py +2 -0
- supervisely/convert/image/csv/csv_converter.py +24 -15
- supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
- supervisely/convert/video/video_converter.py +2 -2
- supervisely/geometry/polyline_3d.py +110 -0
- supervisely/io/env.py +161 -1
- supervisely/nn/artifacts/__init__.py +1 -1
- supervisely/nn/artifacts/artifacts.py +10 -2
- supervisely/nn/artifacts/detectron2.py +1 -0
- supervisely/nn/artifacts/hrda.py +1 -0
- supervisely/nn/artifacts/mmclassification.py +20 -0
- supervisely/nn/artifacts/mmdetection.py +5 -3
- supervisely/nn/artifacts/mmsegmentation.py +1 -0
- supervisely/nn/artifacts/ritm.py +1 -0
- supervisely/nn/artifacts/rtdetr.py +1 -0
- supervisely/nn/artifacts/unet.py +1 -0
- supervisely/nn/artifacts/utils.py +3 -0
- supervisely/nn/artifacts/yolov5.py +2 -0
- supervisely/nn/artifacts/yolov8.py +1 -0
- supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
- supervisely/nn/experiments.py +9 -0
- supervisely/nn/inference/cache.py +37 -17
- supervisely/nn/inference/gui/serving_gui_template.py +39 -13
- supervisely/nn/inference/inference.py +953 -211
- supervisely/nn/inference/inference_request.py +15 -8
- supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
- supervisely/nn/inference/object_detection/object_detection.py +1 -0
- supervisely/nn/inference/predict_app/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
- supervisely/nn/inference/predict_app/gui/gui.py +915 -0
- supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
- supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
- supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
- supervisely/nn/inference/predict_app/gui/preview.py +93 -0
- supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
- supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
- supervisely/nn/inference/predict_app/gui/utils.py +399 -0
- supervisely/nn/inference/predict_app/predict_app.py +176 -0
- supervisely/nn/inference/session.py +47 -39
- supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
- supervisely/nn/inference/tracking/point_tracking.py +5 -1
- supervisely/nn/inference/tracking/tracker_interface.py +4 -0
- supervisely/nn/inference/uploader.py +9 -5
- supervisely/nn/model/model_api.py +44 -22
- supervisely/nn/model/prediction.py +15 -1
- supervisely/nn/model/prediction_session.py +70 -14
- supervisely/nn/prediction_dto.py +7 -0
- supervisely/nn/tracker/__init__.py +6 -8
- supervisely/nn/tracker/base_tracker.py +54 -0
- supervisely/nn/tracker/botsort/__init__.py +1 -0
- supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
- supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
- supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
- supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
- supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
- supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
- supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
- supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
- supervisely/nn/tracker/botsort_tracker.py +273 -0
- supervisely/nn/tracker/calculate_metrics.py +264 -0
- supervisely/nn/tracker/utils.py +273 -0
- supervisely/nn/tracker/visualize.py +520 -0
- supervisely/nn/training/gui/gui.py +152 -49
- supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
- supervisely/nn/training/gui/model_selector.py +8 -6
- supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
- supervisely/nn/training/gui/training_artifacts.py +3 -1
- supervisely/nn/training/train_app.py +225 -46
- supervisely/project/pointcloud_episode_project.py +12 -8
- supervisely/project/pointcloud_project.py +12 -8
- supervisely/project/project.py +221 -75
- supervisely/template/experiment/experiment.html.jinja +105 -55
- supervisely/template/experiment/experiment_generator.py +258 -112
- supervisely/template/experiment/header.html.jinja +31 -13
- supervisely/template/experiment/sly-style.css +7 -2
- supervisely/versions.json +3 -1
- supervisely/video/sampling.py +42 -20
- supervisely/video/video.py +41 -12
- supervisely/video_annotation/video_figure.py +38 -4
- supervisely/volume/stl_converter.py +2 -0
- supervisely/worker_api/agent_rpc.py +24 -1
- supervisely/worker_api/rpc_servicer.py +31 -7
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
- supervisely_lib/__init__.py +6 -1
- supervisely/app/widgets/experiment_selector/style.css +0 -27
- supervisely/app/widgets/experiment_selector/template.html +0 -61
- supervisely/nn/tracker/bot_sort/__init__.py +0 -21
- supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
- supervisely/nn/tracker/bot_sort/matching.py +0 -127
- supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
- supervisely/nn/tracker/deep_sort/__init__.py +0 -6
- supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
- supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
- supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
- supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
- supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
- supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
- supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
- supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
- supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
- supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
- supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
- supervisely/nn/tracker/tracker.py +0 -285
- supervisely/nn/tracker/utils/kalman_filter.py +0 -492
- supervisely/nn/tracking/__init__.py +0 -1
- supervisely/nn/tracking/boxmot.py +0 -114
- supervisely/nn/tracking/tracking.py +0 -24
- /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
- {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__(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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.
|
|
@@ -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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
231
|
+
logger.warning(f"FYI: Image [{image_path}] doesn't have extension.")
|
|
223
232
|
elif extension.lower() not in SUPPORTED_IMG_EXTS:
|
|
224
|
-
logger.
|
|
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.
|
|
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.
|
|
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.
|
|
351
|
+
logger.warning(f"Dataset ID:'{dataset_id}' has been uploaded.")
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import os
|
|
2
|
-
|
|
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 =
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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.
|
|
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()
|