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
|
@@ -1,7 +1,10 @@
|
|
|
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
|
-
from typing import Dict, Optional
|
|
7
|
+
from typing import Dict, List, Optional
|
|
5
8
|
|
|
6
9
|
import supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_helper as helpers
|
|
7
10
|
import supervisely.io.fs as fs
|
|
@@ -9,6 +12,7 @@ from supervisely._utils import is_development
|
|
|
9
12
|
from supervisely.annotation.obj_class import ObjClass
|
|
10
13
|
from supervisely.annotation.tag_meta import TagMeta, TagValueType
|
|
11
14
|
from supervisely.api.api import Api, ApiField
|
|
15
|
+
from supervisely.api.dataset_api import DatasetInfo
|
|
12
16
|
from supervisely.convert.base_converter import AvailablePointcloudConverters
|
|
13
17
|
from supervisely.convert.pointcloud_episodes.pointcloud_episodes_converter import (
|
|
14
18
|
PointcloudEpisodeConverter,
|
|
@@ -36,20 +40,14 @@ from supervisely.pointcloud_annotation.pointcloud_figure import PointcloudFigure
|
|
|
36
40
|
from supervisely.project.project_meta import ProjectMeta
|
|
37
41
|
from supervisely.sly_logger import logger
|
|
38
42
|
from supervisely.tiny_timer import TinyTimer
|
|
43
|
+
from supervisely.video_annotation.key_id_map import KeyIdMap
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
42
47
|
"""Converter for NuScenes pointcloud episodes format."""
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
input_data: str,
|
|
47
|
-
labeling_interface: str,
|
|
48
|
-
upload_as_links: bool,
|
|
49
|
-
remote_files_map: Optional[Dict[str, str]] = None,
|
|
50
|
-
):
|
|
51
|
-
super().__init__(input_data, labeling_interface, upload_as_links, remote_files_map)
|
|
52
|
-
self._nuscenes = None
|
|
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
|
-
|
|
65
|
-
|
|
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
|
|
@@ -83,18 +83,19 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
83
83
|
version = osp.basename(ann_dir)
|
|
84
84
|
try:
|
|
85
85
|
t = TinyTimer()
|
|
86
|
-
|
|
87
|
-
self._nuscenes: NuScenes = nuscenes
|
|
86
|
+
self._nuscenes: NuScenes = NuScenes(version=version, dataroot=input_path, verbose=False)
|
|
88
87
|
logger.debug(f"NuScenes initialization took {t.get_sec():.3f} sec")
|
|
89
88
|
except Exception as e:
|
|
90
89
|
logger.debug(f"Failed to initialize NuScenes: {e}")
|
|
91
90
|
return False
|
|
92
91
|
|
|
92
|
+
self._custom_data["nuscenes_version"] = version
|
|
93
|
+
self._custom_data["dataroot"] = input_path
|
|
93
94
|
return True
|
|
94
95
|
|
|
95
96
|
def to_supervisely(
|
|
96
97
|
self,
|
|
97
|
-
scene_samples,
|
|
98
|
+
scene_samples: Dict[str, helpers.Sample],
|
|
98
99
|
meta: ProjectMeta,
|
|
99
100
|
renamed_classes: dict = {},
|
|
100
101
|
renamed_tags: dict = {},
|
|
@@ -102,9 +103,13 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
102
103
|
token_to_obj = {}
|
|
103
104
|
frames = []
|
|
104
105
|
tags = []
|
|
105
|
-
|
|
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()):
|
|
106
110
|
figures = []
|
|
107
111
|
for obj in sample.anns:
|
|
112
|
+
ann_token = uuid.UUID(obj.token)
|
|
108
113
|
instance_token = obj.instance_token
|
|
109
114
|
class_name = obj.category
|
|
110
115
|
parent_obj_token = obj.parent_token
|
|
@@ -114,7 +119,9 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
114
119
|
obj_class_name = renamed_classes.get(class_name, class_name)
|
|
115
120
|
obj_class = meta.get_obj_class(obj_class_name)
|
|
116
121
|
obj_tags = None # ! TODO: fix tags
|
|
117
|
-
pcd_ep_obj = PointcloudEpisodeObject(
|
|
122
|
+
pcd_ep_obj = PointcloudEpisodeObject(
|
|
123
|
+
obj_class, obj_tags, uuid.UUID(instance_token)
|
|
124
|
+
)
|
|
118
125
|
# * Assign the object to the starting token
|
|
119
126
|
token_to_obj[instance_token] = pcd_ep_obj
|
|
120
127
|
parent_object = pcd_ep_obj
|
|
@@ -123,42 +130,45 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
123
130
|
token_to_obj[instance_token] = token_to_obj[parent_obj_token]
|
|
124
131
|
parent_object = token_to_obj[parent_obj_token]
|
|
125
132
|
geom = obj.to_supervisely()
|
|
126
|
-
pcd_figure = PointcloudFigure(parent_object, geom, sample_i)
|
|
133
|
+
pcd_figure = PointcloudFigure(parent_object, geom, sample_i, ann_token)
|
|
127
134
|
figures.append(pcd_figure)
|
|
135
|
+
frame_idx_to_scene_sample_token[sample_i] = token
|
|
128
136
|
frame = PointcloudEpisodeFrame(sample_i, figures)
|
|
129
137
|
frames.append(frame)
|
|
130
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)
|
|
131
141
|
return PointcloudEpisodeAnnotation(
|
|
132
142
|
len(frames),
|
|
133
143
|
PointcloudEpisodeObjectCollection(list(set(token_to_obj.values()))),
|
|
134
144
|
PointcloudEpisodeFrameCollection(frames),
|
|
135
145
|
tag_collection,
|
|
146
|
+
key=key_uuid,
|
|
136
147
|
)
|
|
137
148
|
|
|
138
149
|
def upload_dataset(self, api: Api, dataset_id: int, batch_size: int = 1, log_progress=True):
|
|
139
|
-
nuscenes =
|
|
150
|
+
from nuscenes import NuScenes # pylint: disable=import-error
|
|
151
|
+
|
|
152
|
+
nuscenes: NuScenes = self._nuscenes
|
|
153
|
+
key_id_map = KeyIdMap()
|
|
140
154
|
|
|
141
155
|
tag_metas = [TagMeta(attr["name"], TagValueType.NONE) for attr in nuscenes.attribute]
|
|
142
|
-
obj_classes =
|
|
156
|
+
obj_classes = {}
|
|
143
157
|
for category in nuscenes.category:
|
|
144
158
|
color = nuscenes.colormap[category["name"]]
|
|
145
|
-
description = category
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
description = trimmed_description.strip()
|
|
155
|
-
obj_classes.append(ObjClass(category["name"], Cuboid3d, color, description=description))
|
|
156
|
-
|
|
157
|
-
self._meta = ProjectMeta(obj_classes, tag_metas)
|
|
159
|
+
description = helpers.trim_description(category.get("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()}
|
|
166
|
+
|
|
167
|
+
self._meta = ProjectMeta(list(obj_classes.values()), tag_metas)
|
|
158
168
|
meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
|
|
159
169
|
|
|
160
170
|
dataset_info = api.dataset.get_info_by_id(dataset_id)
|
|
161
|
-
scene_name_to_dataset = {}
|
|
171
|
+
scene_name_to_dataset: Dict[str, DatasetInfo] = {}
|
|
162
172
|
|
|
163
173
|
scene_names = [scene["name"] for scene in nuscenes.scene]
|
|
164
174
|
scene_cnt = len(scene_names)
|
|
@@ -188,12 +198,13 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
188
198
|
|
|
189
199
|
for scene in nuscenes.scene:
|
|
190
200
|
current_dataset_id = scene_name_to_dataset[scene["name"]].id
|
|
201
|
+
self._current_ds_id = current_dataset_id
|
|
191
202
|
|
|
192
203
|
log = nuscenes.get("log", scene["log_token"])
|
|
193
204
|
sample_token = scene["first_sample_token"]
|
|
194
205
|
|
|
195
206
|
# * Extract scene's samples
|
|
196
|
-
scene_samples =
|
|
207
|
+
scene_samples: Dict[str, helpers.Sample] = {}
|
|
197
208
|
for i in range(scene["nbr_samples"]):
|
|
198
209
|
sample = nuscenes.get("sample", sample_token)
|
|
199
210
|
lidar_path, boxes, _ = nuscenes.get_sample_data(sample["data"]["LIDAR_TOP"])
|
|
@@ -207,25 +218,25 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
207
218
|
current_instance_token = inst_token["token"]
|
|
208
219
|
parent_token = inst_token["prev"]
|
|
209
220
|
|
|
210
|
-
# get category, attributes and visibility
|
|
211
221
|
ann = nuscenes.get("sample_annotation", current_instance_token)
|
|
212
222
|
category = ann["category_name"]
|
|
213
223
|
attributes = [
|
|
214
224
|
nuscenes.get("attribute", attr)["name"] for attr in ann["attribute_tokens"]
|
|
215
225
|
]
|
|
216
226
|
visibility = nuscenes.get("visibility", ann["visibility_token"])["level"]
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
ann_token = ann["token"]
|
|
228
|
+
|
|
229
|
+
ann = helpers.AnnotationObject(
|
|
230
|
+
name=name,
|
|
231
|
+
bbox=box,
|
|
232
|
+
token=ann_token,
|
|
233
|
+
instance_token=current_instance_token,
|
|
234
|
+
parent_token=parent_token,
|
|
235
|
+
category=category,
|
|
236
|
+
attributes=attributes,
|
|
237
|
+
visibility=visibility,
|
|
228
238
|
)
|
|
239
|
+
anns.append(ann)
|
|
229
240
|
|
|
230
241
|
# get camera data
|
|
231
242
|
sample_data = nuscenes.get("sample_data", sample["data"]["LIDAR_TOP"])
|
|
@@ -239,15 +250,18 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
239
250
|
for sensor, token in sample["data"].items()
|
|
240
251
|
if sensor.startswith("CAM")
|
|
241
252
|
]
|
|
242
|
-
|
|
253
|
+
sample_token = sample["token"]
|
|
254
|
+
scene_samples[sample_token] = helpers.Sample(
|
|
255
|
+
timestamp, lidar_path, anns, camera_data
|
|
256
|
+
)
|
|
243
257
|
sample_token = sample["next"]
|
|
244
258
|
|
|
245
259
|
# * Convert and upload pointclouds
|
|
246
260
|
frame_to_pointcloud_ids = {}
|
|
247
|
-
for idx, sample in enumerate(scene_samples):
|
|
261
|
+
for idx, sample in enumerate(scene_samples.values()):
|
|
248
262
|
pcd_path = sample.convert_lidar_to_supervisely()
|
|
249
263
|
|
|
250
|
-
pcd_name = fs.
|
|
264
|
+
pcd_name = fs.get_file_name_with_ext(pcd_path)
|
|
251
265
|
pcd_meta = {
|
|
252
266
|
"frame": idx,
|
|
253
267
|
"vehicle": log["vehicle"],
|
|
@@ -287,9 +301,10 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
287
301
|
|
|
288
302
|
# * Convert and upload annotations
|
|
289
303
|
pcd_ann = self.to_supervisely(scene_samples, meta, renamed_classes, renamed_tags)
|
|
304
|
+
|
|
290
305
|
try:
|
|
291
306
|
api.pointcloud_episode.annotation.append(
|
|
292
|
-
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
|
|
293
308
|
)
|
|
294
309
|
logger.info(f"Dataset ID:{current_dataset_id} has been successfully uploaded.")
|
|
295
310
|
except Exception as e:
|
|
@@ -297,6 +312,15 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
297
312
|
logger.warning(
|
|
298
313
|
f"Failed to upload annotation for scene: {scene['name']}. Message: {error_msg}"
|
|
299
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)
|
|
300
324
|
|
|
301
325
|
if log_progress:
|
|
302
326
|
if is_development():
|
|
@@ -1,7 +1,8 @@
|
|
|
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
|
|
4
|
-
from typing import List
|
|
5
|
+
from typing import Dict, Generator, List, Tuple
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
|
|
@@ -40,113 +41,50 @@ TABLE_NAMES = [
|
|
|
40
41
|
]
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@staticmethod
|
|
55
|
-
def generate_boxes(nuscenes, boxes):
|
|
56
|
-
"""
|
|
57
|
-
Generate ground truth boxes for a given set of boxes.
|
|
58
|
-
|
|
59
|
-
Yields:
|
|
60
|
-
tuple: A tuple containing:
|
|
61
|
-
- gt_box (np.ndarray): A numpy array representing the ground truth box with concatenated location,
|
|
62
|
-
dimensions, and rotation.
|
|
63
|
-
- name (str): The name of the object.
|
|
64
|
-
- instance_token (str): The instance token associated with the box.
|
|
65
|
-
"""
|
|
66
|
-
locs = np.array([b.center for b in boxes]).reshape(-1, 3)
|
|
67
|
-
dims = np.array([b.wlh for b in boxes]).reshape(-1, 3)
|
|
68
|
-
rots = np.array([b.orientation.yaw_pitch_roll[0] for b in boxes]).reshape(-1, 1)
|
|
69
|
-
|
|
70
|
-
gt_boxes = np.concatenate([locs, dims, -rots - np.pi / 2], axis=1)
|
|
71
|
-
names = np.array([b.name for b in boxes])
|
|
72
|
-
instance_tokens = [nuscenes.get("sample_annotation", box.token) for box in boxes]
|
|
73
|
-
|
|
74
|
-
yield from zip(gt_boxes, names, instance_tokens)
|
|
75
|
-
|
|
76
|
-
def convert_lidar_to_supervisely(self):
|
|
77
|
-
"""
|
|
78
|
-
Converts a LiDAR point cloud file to the Supervisely format and saves it as a .pcd file.
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
str: The file path of the saved .pcd file.
|
|
82
|
-
"""
|
|
83
|
-
import open3d as o3d # pylint: disable=import-error
|
|
84
|
-
|
|
85
|
-
bin_file = Path(self.lidar_path)
|
|
86
|
-
save_path = str(bin_file.with_suffix(".pcd"))
|
|
87
|
-
|
|
88
|
-
b = np.fromfile(bin_file, dtype=np.float32).reshape(-1, 5)
|
|
89
|
-
points = b[:, 0:3]
|
|
90
|
-
intensity = b[:, 3]
|
|
91
|
-
ring_index = b[:, 4]
|
|
92
|
-
intensity_fake_rgb = np.zeros((intensity.shape[0], 3))
|
|
93
|
-
intensity_fake_rgb[:, 0] = (
|
|
94
|
-
intensity # red The intensity measures the reflectivity of the objects
|
|
95
|
-
)
|
|
96
|
-
intensity_fake_rgb[:, 1] = (
|
|
97
|
-
ring_index # green ring index is the index of the laser ranging from 0 to 31
|
|
98
|
-
)
|
|
99
|
-
try:
|
|
100
|
-
pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
|
|
101
|
-
pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
|
|
102
|
-
o3d.io.write_point_cloud(save_path, pc)
|
|
103
|
-
except Exception as e:
|
|
104
|
-
logger.warning(f"Error converting lidar to supervisely format: {e}")
|
|
105
|
-
return save_path
|
|
44
|
+
def trim_description(description: str, max_length: int = 255) -> str:
|
|
45
|
+
if len(description) > max_length:
|
|
46
|
+
sentences = description.split(".")
|
|
47
|
+
trimmed_description = ""
|
|
48
|
+
for sentence in sentences:
|
|
49
|
+
if len(trimmed_description) + len(sentence) + 1 > max_length:
|
|
50
|
+
break
|
|
51
|
+
trimmed_description += sentence + "."
|
|
52
|
+
description = trimmed_description.strip()
|
|
53
|
+
return description
|
|
106
54
|
|
|
107
55
|
|
|
56
|
+
@dataclass
|
|
108
57
|
class AnnotationObject:
|
|
109
58
|
"""
|
|
110
59
|
A class to represent an annotation object in the NuScenes dataset.
|
|
111
60
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
instance_token
|
|
119
|
-
|
|
120
|
-
parent_token
|
|
121
|
-
|
|
122
|
-
category
|
|
123
|
-
|
|
124
|
-
attributes
|
|
125
|
-
|
|
126
|
-
visibility
|
|
127
|
-
|
|
61
|
+
:param name: The name of the annotation object
|
|
62
|
+
:type name: str
|
|
63
|
+
:param bbox: The bounding box coordinates in NuScenes format
|
|
64
|
+
:type bbox: np.ndarray
|
|
65
|
+
:param token: The unique token identifying the annotation object
|
|
66
|
+
:type token: str
|
|
67
|
+
:param instance_token: The instance token associated with the annotation object
|
|
68
|
+
:type instance_token: str
|
|
69
|
+
:param parent_token: The token of instance preceding the current object instance
|
|
70
|
+
:type parent_token: str
|
|
71
|
+
:param category: The class name of the annotation object
|
|
72
|
+
:type category: str
|
|
73
|
+
:param attributes: The attribute names associated with the annotation object
|
|
74
|
+
:type attributes: List[str]
|
|
75
|
+
:param visibility: The visibility level of the annotation object
|
|
76
|
+
:type visibility: str
|
|
128
77
|
"""
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
):
|
|
140
|
-
self.name = name
|
|
141
|
-
self.bbox = bbox
|
|
142
|
-
self.instance_token = instance_token
|
|
143
|
-
self.parent_token = parent_token
|
|
144
|
-
|
|
145
|
-
self.category = category
|
|
146
|
-
self.attributes = attributes
|
|
147
|
-
self.visibility = visibility
|
|
148
|
-
|
|
149
|
-
def to_supervisely(self):
|
|
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
|
|
86
|
+
|
|
87
|
+
def to_supervisely(self) -> Cuboid3d:
|
|
150
88
|
box = self.convert_nuscenes_to_BEVBox3D()
|
|
151
89
|
|
|
152
90
|
bbox = box.to_xyzwhlr()
|
|
@@ -176,29 +114,33 @@ class AnnotationObject:
|
|
|
176
114
|
|
|
177
115
|
class CamData:
|
|
178
116
|
"""
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
The intrinsic matrix (3x3) representing the camera's intrinsic parameters.
|
|
117
|
+
This class handles camera sensor data from the nuScenes dataset, including coordinate system
|
|
118
|
+
transformations from lidar to camera space and extraction of camera calibration parameters.
|
|
119
|
+
|
|
120
|
+
:param nuscenes: The nuScenes dataset instance
|
|
121
|
+
:type nuscenes: NuScenes
|
|
122
|
+
:param sensor_name: The name of the camera sensor
|
|
123
|
+
:type sensor_name: str
|
|
124
|
+
:param sensor_token: The token identifying the specific sensor sample
|
|
125
|
+
:type sensor_token: str
|
|
126
|
+
:param cs_record: The calibrated sensor record for the lidar
|
|
127
|
+
:type cs_record: dict
|
|
128
|
+
:param ego_record: The ego pose record for the lidar
|
|
129
|
+
:type ego_record: dict
|
|
193
130
|
"""
|
|
194
131
|
|
|
195
|
-
def __init__(
|
|
132
|
+
def __init__(
|
|
133
|
+
self, nuscenes, sensor_name: str, sensor_token: str, cs_record: dict, ego_record: dict
|
|
134
|
+
):
|
|
135
|
+
from nuscenes import NuScenes # pylint: disable=import-error
|
|
196
136
|
from nuscenes.utils.data_classes import ( # pylint: disable=import-error
|
|
197
137
|
transform_matrix,
|
|
198
138
|
)
|
|
199
139
|
from pyquaternion import Quaternion # pylint: disable=import-error
|
|
200
140
|
|
|
201
|
-
|
|
141
|
+
nuscenes: NuScenes = nuscenes
|
|
142
|
+
|
|
143
|
+
img_path, _, _ = nuscenes.get_sample_data(sensor_token)
|
|
202
144
|
if not osp.exists(img_path):
|
|
203
145
|
return None
|
|
204
146
|
|
|
@@ -237,15 +179,14 @@ class CamData:
|
|
|
237
179
|
self.extrinsic = np.hstack((velo_to_cam_rot, velo_to_cam_trans.reshape(3, 1)))
|
|
238
180
|
self.intrinsic = np.asarray(cs_record_cam["camera_intrinsic"])
|
|
239
181
|
|
|
240
|
-
def get_info(self, timestamp):
|
|
182
|
+
def get_info(self, timestamp: str) -> Tuple[str, Dict]:
|
|
241
183
|
"""
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
timestamp (int): The timestamp associated with the image.
|
|
184
|
+
Generates image info based on the camera data.
|
|
246
185
|
|
|
247
|
-
|
|
248
|
-
|
|
186
|
+
:param timestamp: The timestamp associated with the image
|
|
187
|
+
:type timestamp: str
|
|
188
|
+
:return: A tuple containing the image path and a dictionary with image metadata.
|
|
189
|
+
:rtype: tuple
|
|
249
190
|
"""
|
|
250
191
|
sensors_to_skip = ["_intrinsic", "_extrinsic", "_imsize"]
|
|
251
192
|
if not any([self.name.endswith(s) for s in sensors_to_skip]):
|
|
@@ -263,3 +204,75 @@ class CamData:
|
|
|
263
204
|
},
|
|
264
205
|
}
|
|
265
206
|
return (sly_path_img, img_info)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@dataclass
|
|
210
|
+
class Sample:
|
|
211
|
+
"""
|
|
212
|
+
A class to represent a sample from the NuScenes dataset.
|
|
213
|
+
"""
|
|
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()
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def generate_boxes(nuscenes, boxes: List) -> Generator:
|
|
225
|
+
"""
|
|
226
|
+
Generate ground truth boxes for a given set of boxes.
|
|
227
|
+
|
|
228
|
+
:param nuscenes: The nuScenes dataset instance
|
|
229
|
+
:type nuscenes: NuScenes
|
|
230
|
+
:param boxes: A list of boxes to generate ground truth for
|
|
231
|
+
:type boxes: List
|
|
232
|
+
:return: A generator that yields tuples containing the ground truth box, name, and instance token.
|
|
233
|
+
:rtype: generator
|
|
234
|
+
"""
|
|
235
|
+
from nuscenes.utils.data_classes import Box # pylint: disable=import-error
|
|
236
|
+
|
|
237
|
+
boxes: List[Box] = boxes
|
|
238
|
+
|
|
239
|
+
locs = np.array([b.center for b in boxes]).reshape(-1, 3)
|
|
240
|
+
dims = np.array([b.wlh for b in boxes]).reshape(-1, 3)
|
|
241
|
+
rots = np.array([b.orientation.yaw_pitch_roll[0] for b in boxes]).reshape(-1, 1)
|
|
242
|
+
|
|
243
|
+
gt_boxes = np.concatenate([locs, dims, -rots - np.pi / 2], axis=1)
|
|
244
|
+
names = np.array([b.name for b in boxes])
|
|
245
|
+
instance_tokens = [nuscenes.get("sample_annotation", box.token) for box in boxes]
|
|
246
|
+
|
|
247
|
+
yield from zip(gt_boxes, names, instance_tokens)
|
|
248
|
+
|
|
249
|
+
def convert_lidar_to_supervisely(self) -> str:
|
|
250
|
+
"""
|
|
251
|
+
Converts a LiDAR point cloud file to the Supervisely format and saves it as a .pcd file.
|
|
252
|
+
|
|
253
|
+
:return: The file path of the saved .pcd file.
|
|
254
|
+
:rtype: str
|
|
255
|
+
"""
|
|
256
|
+
import open3d as o3d # pylint: disable=import-error
|
|
257
|
+
|
|
258
|
+
bin_file = Path(self.lidar_path)
|
|
259
|
+
save_path = str(bin_file.with_suffix(".pcd"))
|
|
260
|
+
|
|
261
|
+
b = np.fromfile(bin_file, dtype=np.float32).reshape(-1, 5)
|
|
262
|
+
points = b[:, 0:3]
|
|
263
|
+
intensity = b[:, 3]
|
|
264
|
+
ring_index = b[:, 4]
|
|
265
|
+
intensity_fake_rgb = np.zeros((intensity.shape[0], 3))
|
|
266
|
+
intensity_fake_rgb[:, 0] = (
|
|
267
|
+
intensity # red The intensity measures the reflectivity of the objects
|
|
268
|
+
)
|
|
269
|
+
intensity_fake_rgb[:, 1] = (
|
|
270
|
+
ring_index # green ring index is the index of the laser ranging from 0 to 31
|
|
271
|
+
)
|
|
272
|
+
try:
|
|
273
|
+
pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
|
|
274
|
+
pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
|
|
275
|
+
o3d.io.write_point_cloud(save_path, pc)
|
|
276
|
+
except Exception as e:
|
|
277
|
+
logger.warning(f"Error converting lidar to supervisely format: {e}")
|
|
278
|
+
return save_path
|
|
@@ -266,8 +266,8 @@ class VideoConverter(BaseConverter):
|
|
|
266
266
|
if codec_type not in ["video", "audio"]:
|
|
267
267
|
continue
|
|
268
268
|
codec_name = stream["codecName"]
|
|
269
|
-
if codec_type == "video" and codec_name
|
|
270
|
-
logger.info(f"Video codec is not h264, transcoding is required: {codec_name}")
|
|
269
|
+
if codec_type == "video" and codec_name not in ["h264", "h265", "hevc", "av1"]:
|
|
270
|
+
logger.info(f"Video codec is not h264/h265/hevc/av1, transcoding is required: {codec_name}")
|
|
271
271
|
need_video_transc = True
|
|
272
272
|
elif codec_type == "audio" and codec_name != "aac":
|
|
273
273
|
logger.info(f"Audio codec is not aac, transcoding is required: {codec_name}")
|