supervisely 6.73.273__py3-none-any.whl → 6.73.275__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/convert/__init__.py +4 -1
- supervisely/convert/base_converter.py +2 -9
- supervisely/convert/pointcloud/nuscenes_conv/__init__.py +0 -0
- supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +227 -0
- supervisely/convert/pointcloud/pointcloud_converter.py +52 -1
- supervisely/convert/pointcloud/sly/sly_pointcloud_converter.py +8 -21
- supervisely/convert/pointcloud/sly/sly_pointcloud_helper.py +4 -2
- supervisely/convert/pointcloud_episodes/lyft/lyft_converter.py +19 -20
- supervisely/convert/pointcloud_episodes/nuscenes_conv/__init__.py +0 -0
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +305 -0
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +265 -0
- supervisely/convert/pointcloud_episodes/pointcloud_episodes_converter.py +82 -27
- supervisely/convert/pointcloud_episodes/sly/sly_pointcloud_episodes_converter.py +9 -8
- supervisely/project/project.py +9 -10
- {supervisely-6.73.273.dist-info → supervisely-6.73.275.dist-info}/METADATA +1 -1
- {supervisely-6.73.273.dist-info → supervisely-6.73.275.dist-info}/RECORD +20 -15
- {supervisely-6.73.273.dist-info → supervisely-6.73.275.dist-info}/LICENSE +0 -0
- {supervisely-6.73.273.dist-info → supervisely-6.73.275.dist-info}/WHEEL +0 -0
- {supervisely-6.73.273.dist-info → supervisely-6.73.275.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.273.dist-info → supervisely-6.73.275.dist-info}/top_level.txt +0 -0
supervisely/convert/__init__.py
CHANGED
|
@@ -38,7 +38,7 @@ from supervisely.convert.pointcloud.las.las_converter import LasConverter
|
|
|
38
38
|
from supervisely.convert.pointcloud.ply.ply_converter import PlyConverter
|
|
39
39
|
from supervisely.convert.pointcloud.bag.bag_converter import BagConverter
|
|
40
40
|
from supervisely.convert.pointcloud.lyft.lyft_converter import LyftConverter
|
|
41
|
-
|
|
41
|
+
from supervisely.convert.pointcloud.nuscenes_conv.nuscenes_converter import NuscenesConverter
|
|
42
42
|
|
|
43
43
|
# Pointcloud Episodes
|
|
44
44
|
from supervisely.convert.pointcloud_episodes.sly.sly_pointcloud_episodes_converter import (
|
|
@@ -46,6 +46,9 @@ from supervisely.convert.pointcloud_episodes.sly.sly_pointcloud_episodes_convert
|
|
|
46
46
|
)
|
|
47
47
|
from supervisely.convert.pointcloud_episodes.bag.bag_converter import BagEpisodesConverter
|
|
48
48
|
from supervisely.convert.pointcloud_episodes.lyft.lyft_converter import LyftEpisodesConverter
|
|
49
|
+
from supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_converter import (
|
|
50
|
+
NuscenesEpisodesConverter,
|
|
51
|
+
)
|
|
49
52
|
|
|
50
53
|
# Video
|
|
51
54
|
from supervisely.convert.video.mot.mot_converter import MOTConverter
|
|
@@ -55,6 +55,7 @@ class AvailablePointcloudConverters:
|
|
|
55
55
|
PLY = "ply"
|
|
56
56
|
BAG = "rosbag"
|
|
57
57
|
LYFT = "lyft"
|
|
58
|
+
NUSCENES = "nuscenes"
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
class AvailablePointcloudEpisodesConverters:
|
|
@@ -301,23 +302,15 @@ class BaseConverter:
|
|
|
301
302
|
return found_formats[0]
|
|
302
303
|
|
|
303
304
|
def _collect_items_if_format_not_detected(self):
|
|
304
|
-
from supervisely.convert.pointcloud_episodes.pointcloud_episodes_converter import (
|
|
305
|
-
PointcloudEpisodeConverter,
|
|
306
|
-
)
|
|
307
|
-
|
|
308
305
|
only_modality_items = True
|
|
309
306
|
unsupported_exts = set()
|
|
310
307
|
items = []
|
|
311
|
-
is_episode = isinstance(self, PointcloudEpisodeConverter)
|
|
312
308
|
for root, _, files in os.walk(self._input_data):
|
|
313
309
|
for file in files:
|
|
314
310
|
full_path = os.path.join(root, file)
|
|
315
311
|
ext = get_file_ext(full_path)
|
|
316
312
|
if ext.lower() in self.allowed_exts: # pylint: disable=no-member
|
|
317
|
-
|
|
318
|
-
items.append(self.Item(full_path, len(items))) # pylint: disable=no-member
|
|
319
|
-
else:
|
|
320
|
-
items.append(self.Item(full_path)) # pylint: disable=no-member
|
|
313
|
+
items.append(self.Item(full_path)) # pylint: disable=no-member
|
|
321
314
|
continue
|
|
322
315
|
only_modality_items = False
|
|
323
316
|
if ext.lower() in self.unsupported_exts:
|
|
File without changes
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Dict, Optional
|
|
3
|
+
|
|
4
|
+
import supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_helper as helpers
|
|
5
|
+
import supervisely.io.fs as fs
|
|
6
|
+
from supervisely import PointcloudAnnotation, PointcloudObject
|
|
7
|
+
from supervisely._utils import is_development
|
|
8
|
+
from supervisely.annotation.obj_class import ObjClass
|
|
9
|
+
from supervisely.annotation.tag_meta import TagMeta, TagValueType
|
|
10
|
+
from supervisely.api.api import Api, ApiField
|
|
11
|
+
from supervisely.convert.base_converter import AvailablePointcloudConverters
|
|
12
|
+
from supervisely.convert.pointcloud.pointcloud_converter import PointcloudConverter
|
|
13
|
+
from supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_converter import (
|
|
14
|
+
NuscenesEpisodesConverter,
|
|
15
|
+
)
|
|
16
|
+
from supervisely.geometry.cuboid_3d import Cuboid3d
|
|
17
|
+
from supervisely.pointcloud_annotation.pointcloud_figure import PointcloudFigure
|
|
18
|
+
from supervisely.pointcloud_annotation.pointcloud_object_collection import (
|
|
19
|
+
PointcloudObjectCollection,
|
|
20
|
+
)
|
|
21
|
+
from supervisely.pointcloud_annotation.pointcloud_tag import PointcloudTag
|
|
22
|
+
from supervisely.pointcloud_annotation.pointcloud_tag_collection import (
|
|
23
|
+
PointcloudTagCollection,
|
|
24
|
+
)
|
|
25
|
+
from supervisely.project.project_meta import ProjectMeta
|
|
26
|
+
from supervisely.sly_logger import logger
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NuscenesConverter(NuscenesEpisodesConverter, PointcloudConverter):
|
|
30
|
+
"""Converter for NuScenes pointcloud format."""
|
|
31
|
+
|
|
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
|
+
def to_supervisely(
|
|
46
|
+
self,
|
|
47
|
+
scene_sample,
|
|
48
|
+
meta: ProjectMeta,
|
|
49
|
+
renamed_classes: dict = {},
|
|
50
|
+
renamed_tags: dict = {},
|
|
51
|
+
) -> PointcloudAnnotation:
|
|
52
|
+
bevbox_objs = [obj.convert_nuscenes_to_BEVBox3D() for obj in scene_sample.anns]
|
|
53
|
+
geoms = [obj.to_supervisely() for obj in scene_sample.anns]
|
|
54
|
+
attrs = [obj.attributes for obj in scene_sample.anns]
|
|
55
|
+
|
|
56
|
+
figures = []
|
|
57
|
+
objs = []
|
|
58
|
+
for label, geom, attributes in zip(bevbox_objs, geoms, attrs):
|
|
59
|
+
class_name = renamed_classes.get(label.label_class, label.label_class)
|
|
60
|
+
tag_col = None
|
|
61
|
+
if len(attributes) > 0 and all([tag_name is not None for tag_name in attributes]):
|
|
62
|
+
tag_meta_names = [renamed_tags.get(name, name) for name in attributes]
|
|
63
|
+
tag_metas = [meta.get_tag_meta(tag_meta_name) for tag_meta_name in tag_meta_names]
|
|
64
|
+
tag_col = PointcloudTagCollection([PointcloudTag(meta, None) for meta in tag_metas])
|
|
65
|
+
pcobj = PointcloudObject(meta.get_obj_class(class_name), tag_col)
|
|
66
|
+
figures.append(PointcloudFigure(pcobj, geom))
|
|
67
|
+
objs.append(pcobj)
|
|
68
|
+
return PointcloudAnnotation(PointcloudObjectCollection(objs), figures)
|
|
69
|
+
|
|
70
|
+
def upload_dataset(self, api: Api, dataset_id: int, batch_size: int = 1, log_progress=True):
|
|
71
|
+
nuscenes = self._nuscenes
|
|
72
|
+
|
|
73
|
+
tag_metas = [TagMeta(attr["name"], TagValueType.NONE) for attr in nuscenes.attribute]
|
|
74
|
+
obj_classes = []
|
|
75
|
+
for category in nuscenes.category:
|
|
76
|
+
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()
|
|
87
|
+
obj_classes.append(ObjClass(category["name"], Cuboid3d, color, description=description))
|
|
88
|
+
|
|
89
|
+
self._meta = ProjectMeta(obj_classes, tag_metas)
|
|
90
|
+
meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
|
|
91
|
+
|
|
92
|
+
dataset_info = api.dataset.get_info_by_id(dataset_id)
|
|
93
|
+
scene_name_to_dataset = {}
|
|
94
|
+
|
|
95
|
+
scene_names = [scene["name"] for scene in nuscenes.scene]
|
|
96
|
+
scene_cnt = len(scene_names)
|
|
97
|
+
total_sample_cnt = sum([scene["nbr_samples"] for scene in nuscenes.scene])
|
|
98
|
+
|
|
99
|
+
multiple_scenes = len(scene_names) > 1
|
|
100
|
+
if multiple_scenes:
|
|
101
|
+
logger.info(f"Found {scene_cnt} scenes ({total_sample_cnt} samples) in the input data.")
|
|
102
|
+
# * Create a nested dataset for each scene
|
|
103
|
+
for name in scene_names:
|
|
104
|
+
ds = api.dataset.create(
|
|
105
|
+
dataset_info.project_id,
|
|
106
|
+
name,
|
|
107
|
+
change_name_if_conflict=True,
|
|
108
|
+
parent_id=dataset_id,
|
|
109
|
+
)
|
|
110
|
+
scene_name_to_dataset[name] = ds
|
|
111
|
+
else:
|
|
112
|
+
scene_name_to_dataset[scene_names[0]] = dataset_info
|
|
113
|
+
|
|
114
|
+
if log_progress:
|
|
115
|
+
progress, progress_cb = self.get_progress(total_sample_cnt, "Converting pointclouds...")
|
|
116
|
+
else:
|
|
117
|
+
progress_cb = None
|
|
118
|
+
|
|
119
|
+
for scene in nuscenes.scene:
|
|
120
|
+
current_dataset_id = scene_name_to_dataset[scene["name"]].id
|
|
121
|
+
|
|
122
|
+
log = nuscenes.get("log", scene["log_token"])
|
|
123
|
+
sample_token = scene["first_sample_token"]
|
|
124
|
+
|
|
125
|
+
# * Extract scene's samples
|
|
126
|
+
scene_samples = []
|
|
127
|
+
for i in range(scene["nbr_samples"]):
|
|
128
|
+
sample = nuscenes.get("sample", sample_token)
|
|
129
|
+
lidar_path, boxes, _ = nuscenes.get_sample_data(sample["data"]["LIDAR_TOP"])
|
|
130
|
+
if not os.path.exists(lidar_path):
|
|
131
|
+
logger.warning(f'Scene "{scene["name"]}" has no LIDAR data.')
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
timestamp = sample["timestamp"]
|
|
135
|
+
anns = []
|
|
136
|
+
for box, name, inst_token in helpers.Sample.generate_boxes(nuscenes, boxes):
|
|
137
|
+
current_instance_token = inst_token["token"]
|
|
138
|
+
parent_token = inst_token["prev"]
|
|
139
|
+
|
|
140
|
+
# get category, attributes and visibility
|
|
141
|
+
ann = nuscenes.get("sample_annotation", current_instance_token)
|
|
142
|
+
category = ann["category_name"]
|
|
143
|
+
attributes = [
|
|
144
|
+
nuscenes.get("attribute", attr)["name"] for attr in ann["attribute_tokens"]
|
|
145
|
+
]
|
|
146
|
+
visibility = nuscenes.get("visibility", ann["visibility_token"])["level"]
|
|
147
|
+
|
|
148
|
+
anns.append(
|
|
149
|
+
helpers.AnnotationObject(
|
|
150
|
+
name,
|
|
151
|
+
box,
|
|
152
|
+
current_instance_token,
|
|
153
|
+
parent_token,
|
|
154
|
+
category,
|
|
155
|
+
attributes,
|
|
156
|
+
visibility,
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# get camera data
|
|
161
|
+
sample_data = nuscenes.get("sample_data", sample["data"]["LIDAR_TOP"])
|
|
162
|
+
cal_sensor = nuscenes.get(
|
|
163
|
+
"calibrated_sensor", sample_data["calibrated_sensor_token"]
|
|
164
|
+
)
|
|
165
|
+
ego_pose = nuscenes.get("ego_pose", sample_data["ego_pose_token"])
|
|
166
|
+
|
|
167
|
+
camera_data = [
|
|
168
|
+
helpers.CamData(nuscenes, sensor, token, cal_sensor, ego_pose)
|
|
169
|
+
for sensor, token in sample["data"].items()
|
|
170
|
+
if sensor.startswith("CAM")
|
|
171
|
+
]
|
|
172
|
+
scene_samples.append(helpers.Sample(timestamp, lidar_path, anns, camera_data))
|
|
173
|
+
sample_token = sample["next"]
|
|
174
|
+
|
|
175
|
+
# * Convert and upload pointclouds w/ annotations
|
|
176
|
+
for idx, sample in enumerate(scene_samples):
|
|
177
|
+
pcd_ann = self.to_supervisely(sample, meta, renamed_classes, renamed_tags)
|
|
178
|
+
|
|
179
|
+
pcd_path = sample.convert_lidar_to_supervisely()
|
|
180
|
+
pcd_name = fs.get_file_name(pcd_path)
|
|
181
|
+
pcd_meta = {
|
|
182
|
+
"frame": idx,
|
|
183
|
+
"vehicle": log["vehicle"],
|
|
184
|
+
"date": log["date_captured"],
|
|
185
|
+
"location": log["location"],
|
|
186
|
+
"description": scene["description"],
|
|
187
|
+
}
|
|
188
|
+
info = api.pointcloud.upload_path(current_dataset_id, pcd_name, pcd_path, pcd_meta)
|
|
189
|
+
fs.silent_remove(pcd_path)
|
|
190
|
+
|
|
191
|
+
pcd_id = info.id
|
|
192
|
+
# * Upload pointcloud annotation
|
|
193
|
+
try:
|
|
194
|
+
api.pointcloud.annotation.append(pcd_id, pcd_ann)
|
|
195
|
+
except Exception as e:
|
|
196
|
+
error_msg = getattr(getattr(e, "response", e), "text", str(e))
|
|
197
|
+
logger.warning(
|
|
198
|
+
f"Failed to upload annotation for scene: {scene['name']}. Message: {error_msg}"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# * Upload related images
|
|
202
|
+
image_jsons = []
|
|
203
|
+
camera_names = []
|
|
204
|
+
for img_path, rimage_info in [
|
|
205
|
+
data.get_info(sample.timestamp) for data in sample.cam_data
|
|
206
|
+
]:
|
|
207
|
+
img = api.pointcloud.upload_related_image(img_path)
|
|
208
|
+
image_jsons.append(
|
|
209
|
+
{
|
|
210
|
+
ApiField.ENTITY_ID: pcd_id,
|
|
211
|
+
ApiField.NAME: rimage_info[ApiField.NAME],
|
|
212
|
+
ApiField.HASH: img,
|
|
213
|
+
ApiField.META: rimage_info[ApiField.META],
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
camera_names.append(rimage_info[ApiField.META]["deviceId"])
|
|
217
|
+
if len(image_jsons) > 0:
|
|
218
|
+
api.pointcloud.add_related_images(image_jsons, camera_names)
|
|
219
|
+
|
|
220
|
+
if log_progress:
|
|
221
|
+
progress_cb(1)
|
|
222
|
+
|
|
223
|
+
logger.info(f"Dataset ID:{current_dataset_id} has been successfully uploaded.")
|
|
224
|
+
|
|
225
|
+
if log_progress:
|
|
226
|
+
if is_development():
|
|
227
|
+
progress.close()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import imghdr
|
|
2
|
+
import os
|
|
3
|
+
from typing import List, Optional, Set, Tuple
|
|
2
4
|
|
|
5
|
+
import supervisely.convert.pointcloud.sly.sly_pointcloud_helper as helpers
|
|
3
6
|
from supervisely import (
|
|
4
7
|
Api,
|
|
5
8
|
PointcloudAnnotation,
|
|
@@ -10,8 +13,10 @@ from supervisely import (
|
|
|
10
13
|
)
|
|
11
14
|
from supervisely.api.module_api import ApiField
|
|
12
15
|
from supervisely.convert.base_converter import BaseConverter
|
|
16
|
+
from supervisely.io.fs import get_file_ext, get_file_name
|
|
13
17
|
from supervisely.io.json import load_json_file
|
|
14
18
|
from supervisely.pointcloud.pointcloud import ALLOWED_POINTCLOUD_EXTENSIONS
|
|
19
|
+
from supervisely.pointcloud.pointcloud import validate_ext as validate_pcd_ext
|
|
15
20
|
|
|
16
21
|
|
|
17
22
|
class PointcloudConverter(BaseConverter):
|
|
@@ -134,3 +139,49 @@ class PointcloudConverter(BaseConverter):
|
|
|
134
139
|
if is_development():
|
|
135
140
|
progress.close()
|
|
136
141
|
logger.info(f"Dataset ID:{dataset_id} has been successfully uploaded.")
|
|
142
|
+
|
|
143
|
+
def _collect_items_if_format_not_detected(self) -> Tuple[List[Item], bool, Set[str]]:
|
|
144
|
+
only_modality_items = True
|
|
145
|
+
unsupported_exts = set()
|
|
146
|
+
pcd_list, rimg_dict, rimg_ann_dict = [], {}, {}
|
|
147
|
+
used_img_ext = set()
|
|
148
|
+
for root, _, files in os.walk(self._input_data):
|
|
149
|
+
for file in files:
|
|
150
|
+
full_path = os.path.join(root, file)
|
|
151
|
+
if file in ["key_id_map.json", "meta.json"]:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
ext = get_file_ext(full_path)
|
|
155
|
+
if ext == ".json":
|
|
156
|
+
dir_name = os.path.basename(root)
|
|
157
|
+
parent_dir_name = os.path.basename(os.path.dirname(root))
|
|
158
|
+
if any(
|
|
159
|
+
p.replace("_", " ") in ["images", "related images", "photo context"]
|
|
160
|
+
for p in [dir_name, parent_dir_name]
|
|
161
|
+
) or dir_name.endswith("_pcd"):
|
|
162
|
+
rimg_ann_dict[file] = full_path
|
|
163
|
+
elif imghdr.what(full_path):
|
|
164
|
+
rimg_dict[file] = full_path
|
|
165
|
+
if ext not in used_img_ext:
|
|
166
|
+
used_img_ext.add(ext)
|
|
167
|
+
elif ext.lower() in self.allowed_exts:
|
|
168
|
+
try:
|
|
169
|
+
validate_pcd_ext(ext)
|
|
170
|
+
pcd_list.append(full_path)
|
|
171
|
+
except:
|
|
172
|
+
pass
|
|
173
|
+
else:
|
|
174
|
+
only_modality_items = False
|
|
175
|
+
unsupported_exts.add(ext)
|
|
176
|
+
|
|
177
|
+
# create Items
|
|
178
|
+
items = []
|
|
179
|
+
for pcd_path in pcd_list:
|
|
180
|
+
item = self.Item(pcd_path)
|
|
181
|
+
rimg, rimg_ann = helpers.find_related_items(
|
|
182
|
+
item.name, used_img_ext, rimg_dict, rimg_ann_dict
|
|
183
|
+
)
|
|
184
|
+
if rimg is not None and rimg_ann is not None:
|
|
185
|
+
item.set_related_images((rimg, rimg_ann))
|
|
186
|
+
items.append(item)
|
|
187
|
+
return items, only_modality_items, unsupported_exts
|
|
@@ -6,7 +6,7 @@ import supervisely.convert.pointcloud.sly.sly_pointcloud_helper as helpers
|
|
|
6
6
|
from supervisely import PointcloudAnnotation, ProjectMeta, logger
|
|
7
7
|
from supervisely.convert.base_converter import AvailablePointcloudConverters
|
|
8
8
|
from supervisely.convert.pointcloud.pointcloud_converter import PointcloudConverter
|
|
9
|
-
from supervisely.io.fs import
|
|
9
|
+
from supervisely.io.fs import get_file_ext, get_file_name
|
|
10
10
|
from supervisely.io.json import load_json_file
|
|
11
11
|
from supervisely.pointcloud.pointcloud import validate_ext as validate_pcd_ext
|
|
12
12
|
|
|
@@ -46,7 +46,6 @@ class SLYPointcloudConverter(PointcloudConverter):
|
|
|
46
46
|
return False
|
|
47
47
|
|
|
48
48
|
def validate_format(self) -> bool:
|
|
49
|
-
detected_ann_cnt = 0
|
|
50
49
|
pcd_list, ann_dict, rimg_dict, rimg_ann_dict = [], {}, {}, {}
|
|
51
50
|
used_img_ext = []
|
|
52
51
|
for root, _, files in os.walk(self._input_data):
|
|
@@ -60,9 +59,7 @@ class SLYPointcloudConverter(PointcloudConverter):
|
|
|
60
59
|
continue
|
|
61
60
|
|
|
62
61
|
ext = get_file_ext(full_path)
|
|
63
|
-
if
|
|
64
|
-
continue
|
|
65
|
-
elif ext in self.ann_ext:
|
|
62
|
+
if ext in self.ann_ext:
|
|
66
63
|
dir_name = os.path.basename(root)
|
|
67
64
|
parent_dir_name = os.path.basename(os.path.dirname(root))
|
|
68
65
|
if any(
|
|
@@ -83,16 +80,11 @@ class SLYPointcloudConverter(PointcloudConverter):
|
|
|
83
80
|
except:
|
|
84
81
|
continue
|
|
85
82
|
|
|
86
|
-
if self._meta is not None:
|
|
87
|
-
meta = self._meta
|
|
88
|
-
else:
|
|
89
|
-
meta = ProjectMeta()
|
|
90
|
-
|
|
91
83
|
# create Items
|
|
92
84
|
self._items = []
|
|
85
|
+
sly_ann_detected = False
|
|
93
86
|
for pcd_path in pcd_list:
|
|
94
87
|
name_noext = get_file_name(pcd_path)
|
|
95
|
-
ann_or_rimg_detected = False
|
|
96
88
|
item = self.Item(pcd_path)
|
|
97
89
|
ann_name = f"{item.name}.json"
|
|
98
90
|
if ann_name not in ann_dict:
|
|
@@ -100,23 +92,18 @@ class SLYPointcloudConverter(PointcloudConverter):
|
|
|
100
92
|
if ann_name in ann_dict:
|
|
101
93
|
ann_path = ann_dict[ann_name]
|
|
102
94
|
if self._meta is None:
|
|
103
|
-
|
|
104
|
-
is_valid = self.validate_ann_file(ann_path,
|
|
95
|
+
self._meta = self.generate_meta_from_annotation(ann_path, self._meta)
|
|
96
|
+
is_valid = self.validate_ann_file(ann_path, self._meta)
|
|
105
97
|
if is_valid:
|
|
106
98
|
item.ann_data = ann_path
|
|
107
|
-
|
|
99
|
+
sly_ann_detected = True
|
|
108
100
|
rimg, rimg_ann = helpers.find_related_items(
|
|
109
101
|
item.name, used_img_ext, rimg_dict, rimg_ann_dict
|
|
110
102
|
)
|
|
111
103
|
if rimg is not None and rimg_ann is not None:
|
|
112
104
|
item.set_related_images((rimg, rimg_ann))
|
|
113
|
-
ann_or_rimg_detected = True
|
|
114
|
-
|
|
115
|
-
if ann_or_rimg_detected:
|
|
116
|
-
detected_ann_cnt += 1
|
|
117
105
|
self._items.append(item)
|
|
118
|
-
|
|
119
|
-
return detected_ann_cnt > 0
|
|
106
|
+
return sly_ann_detected
|
|
120
107
|
|
|
121
108
|
def to_supervisely(
|
|
122
109
|
self,
|
|
@@ -140,5 +127,5 @@ class SLYPointcloudConverter(PointcloudConverter):
|
|
|
140
127
|
ann_json = helpers.rename_in_json(ann_json, renamed_classes, renamed_tags)
|
|
141
128
|
return PointcloudAnnotation.from_json(ann_json, meta)
|
|
142
129
|
except Exception as e:
|
|
143
|
-
logger.
|
|
130
|
+
logger.warning(f"Failed to convert annotation: {repr(e)}")
|
|
144
131
|
return item.create_empty_annotation()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Dict, List
|
|
1
|
+
from typing import Dict, List, Union
|
|
2
2
|
|
|
3
3
|
from supervisely import (
|
|
4
4
|
AnyGeometry,
|
|
@@ -16,7 +16,9 @@ from supervisely.io.json import load_json_file
|
|
|
16
16
|
SLY_ANN_KEYS = ["figures", "objects", "tags"]
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def get_meta_from_annotation(ann_path: str, meta: ProjectMeta) -> ProjectMeta:
|
|
19
|
+
def get_meta_from_annotation(ann_path: str, meta: Union[ProjectMeta, None]) -> ProjectMeta:
|
|
20
|
+
if meta is None:
|
|
21
|
+
meta = ProjectMeta()
|
|
20
22
|
ann_json = load_json_file(ann_path)
|
|
21
23
|
if "annotation" in ann_json:
|
|
22
24
|
ann_json = ann_json["annotation"]
|
|
@@ -30,8 +30,6 @@ from supervisely import (
|
|
|
30
30
|
PointcloudEpisodeFrame,
|
|
31
31
|
TagMeta,
|
|
32
32
|
TagValueType,
|
|
33
|
-
VideoTagCollection,
|
|
34
|
-
VideoTag,
|
|
35
33
|
)
|
|
36
34
|
from supervisely.io import fs
|
|
37
35
|
from supervisely.convert.pointcloud.lyft import lyft_helper
|
|
@@ -40,6 +38,8 @@ from datetime import datetime
|
|
|
40
38
|
from supervisely.geometry.cuboid_3d import Cuboid3d
|
|
41
39
|
from collections import defaultdict
|
|
42
40
|
|
|
41
|
+
# from supervisely.annotation.tag_meta import TagTargetType as TagTT
|
|
42
|
+
|
|
43
43
|
|
|
44
44
|
class LyftEpisodesConverter(LyftConverter, PointcloudEpisodeConverter):
|
|
45
45
|
"""Converter for LYFT pointcloud episodes format."""
|
|
@@ -78,7 +78,7 @@ class LyftEpisodesConverter(LyftConverter, PointcloudEpisodeConverter):
|
|
|
78
78
|
for scene_name, items in scene_name_to_items.items():
|
|
79
79
|
token_to_obj = {}
|
|
80
80
|
frames = []
|
|
81
|
-
tags = [] # todo tags that belong to the
|
|
81
|
+
tags = [] # todo tags that belong to the scene if any
|
|
82
82
|
# * Iterate over each sample in the scene
|
|
83
83
|
for i, item in enumerate(items):
|
|
84
84
|
ann = item.ann_data
|
|
@@ -93,23 +93,23 @@ class LyftEpisodesConverter(LyftConverter, PointcloudEpisodeConverter):
|
|
|
93
93
|
obj_class_name = renamed_classes.get(class_name, class_name)
|
|
94
94
|
obj_class = meta.get_obj_class(obj_class_name)
|
|
95
95
|
|
|
96
|
-
#
|
|
96
|
+
# * Get tags for the object
|
|
97
97
|
# tag_names = [
|
|
98
|
-
#
|
|
99
|
-
#
|
|
98
|
+
# lyft.get("attribute", attr_token).get("name", None)
|
|
99
|
+
# for attr_token in instance_token["attribute_tokens"]
|
|
100
100
|
# ]
|
|
101
101
|
# if len(tag_names) > 0 and all(
|
|
102
|
-
#
|
|
102
|
+
# [tag_name is not None for tag_name in tag_names]
|
|
103
103
|
# ):
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
obj_tags = None
|
|
104
|
+
# tags = [TagMeta(tag_name, TagValueType.NONE) for tag_name in tag_names]
|
|
105
|
+
# tag_meta_names = [renamed_tags.get(name, name) for name in tag_names]
|
|
106
|
+
# tag_metas = [
|
|
107
|
+
# meta.get_tag_meta(tag_meta_name) for tag_meta_name in tag_meta_names
|
|
108
|
+
# ]
|
|
109
|
+
# obj_tags = PointcloudEpisodeTagCollection(
|
|
110
|
+
# [PointcloudEpisodeTag(tag_meta, None) for tag_meta in tag_metas]
|
|
111
|
+
# )
|
|
112
|
+
obj_tags = None # todo remove after fixing tags
|
|
113
113
|
pcd_ep_obj = PointcloudEpisodeObject(obj_class, obj_tags)
|
|
114
114
|
# * Assign the object to the starting token
|
|
115
115
|
token_to_obj[instance_token["token"]] = pcd_ep_obj
|
|
@@ -137,9 +137,10 @@ class LyftEpisodesConverter(LyftConverter, PointcloudEpisodeConverter):
|
|
|
137
137
|
def upload_dataset(self, api: Api, dataset_id: int, batch_size: int = 1, log_progress=True):
|
|
138
138
|
unique_names = {name for item in self._items for name in item.ann_data["names"]}
|
|
139
139
|
tag_names = {tag["name"] for tag in self._lyft.attribute}
|
|
140
|
+
target_type = None # TagTT.GLOBAL # todo remove after fixing tags
|
|
140
141
|
self._meta = ProjectMeta(
|
|
141
142
|
[ObjClass(name, Cuboid3d) for name in unique_names],
|
|
142
|
-
[TagMeta(tag, TagValueType.NONE) for tag in tag_names],
|
|
143
|
+
[TagMeta(tag, TagValueType.NONE, target_type=target_type) for tag in tag_names],
|
|
143
144
|
)
|
|
144
145
|
meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
|
|
145
146
|
|
|
@@ -234,9 +235,7 @@ class LyftEpisodesConverter(LyftConverter, PointcloudEpisodeConverter):
|
|
|
234
235
|
)
|
|
235
236
|
except Exception as e:
|
|
236
237
|
error_msg = getattr(getattr(e, "response", e), "text", str(e))
|
|
237
|
-
logger.warn(
|
|
238
|
-
f"Failed to upload annotation for scene: {scene}. Message: {error_msg}"
|
|
239
|
-
)
|
|
238
|
+
logger.warn(f"Failed to upload annotation for scene: {scene}. Message: {error_msg}")
|
|
240
239
|
logger.info(f"Dataset ID:{current_dataset_id} has been successfully uploaded.")
|
|
241
240
|
|
|
242
241
|
if log_progress:
|
|
File without changes
|