supervisely 6.73.378__py3-none-any.whl → 6.73.380__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.
- supervisely/__init__.py +1 -1
- supervisely/api/pointcloud/pointcloud_api.py +8 -0
- supervisely/convert/pointcloud/pointcloud_converter.py +103 -12
- supervisely/convert/pointcloud/sly/sly_pointcloud_converter.py +5 -1
- supervisely/convert/pointcloud_episodes/pointcloud_episodes_converter.py +122 -12
- supervisely/convert/pointcloud_episodes/sly/sly_pointcloud_episodes_converter.py +5 -1
- supervisely/project/pointcloud_episode_project.py +126 -4
- supervisely/project/pointcloud_project.py +160 -19
- {supervisely-6.73.378.dist-info → supervisely-6.73.380.dist-info}/METADATA +1 -1
- {supervisely-6.73.378.dist-info → supervisely-6.73.380.dist-info}/RECORD +14 -14
- {supervisely-6.73.378.dist-info → supervisely-6.73.380.dist-info}/LICENSE +0 -0
- {supervisely-6.73.378.dist-info → supervisely-6.73.380.dist-info}/WHEEL +0 -0
- {supervisely-6.73.378.dist-info → supervisely-6.73.380.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.378.dist-info → supervisely-6.73.380.dist-info}/top_level.txt +0 -0
supervisely/__init__.py
CHANGED
|
@@ -314,4 +314,4 @@ except Exception as e:
|
|
|
314
314
|
# If new changes in Supervisely Python SDK require upgrade of the Supervisely instance
|
|
315
315
|
# set a new value for the environment variable MINIMUM_INSTANCE_VERSION_FOR_SDK, otherwise
|
|
316
316
|
# users can face compatibility issues, if the instance version is lower than the SDK version.
|
|
317
|
-
os.environ["MINIMUM_INSTANCE_VERSION_FOR_SDK"] = "6.
|
|
317
|
+
os.environ["MINIMUM_INSTANCE_VERSION_FOR_SDK"] = "6.13.00"
|
|
@@ -421,6 +421,14 @@ class PointcloudApi(RemoveableBulkModuleApi):
|
|
|
421
421
|
convert_json_info_cb=lambda x: x,
|
|
422
422
|
)
|
|
423
423
|
|
|
424
|
+
def get_list_related_images_batch(self, dataset_id: int, ids: List[int]) -> List:
|
|
425
|
+
filters = [{"field": ApiField.ENTITY_ID, "operator": "in", "value": ids}]
|
|
426
|
+
return self.get_list_all_pages(
|
|
427
|
+
"point-clouds.images.list",
|
|
428
|
+
{ApiField.DATASET_ID: dataset_id, ApiField.FILTER: filters},
|
|
429
|
+
convert_json_info_cb=lambda x: x,
|
|
430
|
+
)
|
|
431
|
+
|
|
424
432
|
def download_related_image(self, id: int, path: str = None) -> Response:
|
|
425
433
|
"""
|
|
426
434
|
Download a related context image from Supervisely to local directory by image id.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import imghdr
|
|
2
2
|
import os
|
|
3
|
-
from typing import List, Optional, Set, Tuple
|
|
3
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
4
|
+
from uuid import UUID
|
|
4
5
|
|
|
5
6
|
import supervisely.convert.pointcloud.sly.sly_pointcloud_helper as helpers
|
|
6
7
|
from supervisely import (
|
|
@@ -17,6 +18,8 @@ from supervisely.io.fs import get_file_ext, get_file_name
|
|
|
17
18
|
from supervisely.io.json import load_json_file
|
|
18
19
|
from supervisely.pointcloud.pointcloud import ALLOWED_POINTCLOUD_EXTENSIONS
|
|
19
20
|
from supervisely.pointcloud.pointcloud import validate_ext as validate_pcd_ext
|
|
21
|
+
from supervisely.pointcloud_annotation.constants import OBJECT_KEY
|
|
22
|
+
from supervisely.video_annotation.key_id_map import KeyIdMap
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class PointcloudConverter(BaseConverter):
|
|
@@ -41,7 +44,14 @@ class PointcloudConverter(BaseConverter):
|
|
|
41
44
|
def create_empty_annotation(self) -> PointcloudAnnotation:
|
|
42
45
|
return PointcloudAnnotation()
|
|
43
46
|
|
|
44
|
-
def set_related_images(self, related_images: Tuple[str, str]) -> None:
|
|
47
|
+
def set_related_images(self, related_images: Tuple[str, str, Optional[str]]) -> None:
|
|
48
|
+
"""Adds related image to the item.
|
|
49
|
+
|
|
50
|
+
related_images tuple:
|
|
51
|
+
- path to image
|
|
52
|
+
- path to .json with image metadata
|
|
53
|
+
- path to .figures.json (can be None if no figures)
|
|
54
|
+
"""
|
|
45
55
|
self._related_images.append(related_images)
|
|
46
56
|
|
|
47
57
|
@property
|
|
@@ -98,21 +108,26 @@ class PointcloudConverter(BaseConverter):
|
|
|
98
108
|
item_paths,
|
|
99
109
|
)
|
|
100
110
|
pcd_ids = [pcd_info.id for pcd_info in pcd_infos]
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
pcl_to_rimg_figures: Dict[int, Dict[str, List[Dict]]] = {}
|
|
112
|
+
pcl_to_hash_to_id: Dict[int, Dict[str, int]] = {}
|
|
113
|
+
key_id_map = KeyIdMap()
|
|
114
|
+
for pcd_id, ann, item in zip(pcd_ids, anns, batch):
|
|
103
115
|
if ann is not None:
|
|
104
|
-
api.pointcloud.annotation.append(pcd_id, ann)
|
|
116
|
+
api.pointcloud.annotation.append(pcd_id, ann, key_id_map)
|
|
105
117
|
|
|
106
118
|
rimg_infos = []
|
|
107
119
|
camera_names = []
|
|
108
|
-
for img_ind,
|
|
120
|
+
for img_ind, rel_tuple in enumerate(item._related_images):
|
|
121
|
+
img_path = rel_tuple[0]
|
|
122
|
+
rimg_ann_path = rel_tuple[1]
|
|
123
|
+
fig_path = rel_tuple[2] if len(rel_tuple) > 2 else None
|
|
109
124
|
meta_json = load_json_file(rimg_ann_path)
|
|
110
125
|
try:
|
|
111
126
|
if ApiField.META not in meta_json:
|
|
112
127
|
raise ValueError("Related image meta not found in json file.")
|
|
113
128
|
if ApiField.NAME not in meta_json:
|
|
114
129
|
raise ValueError("Related image name not found in json file.")
|
|
115
|
-
|
|
130
|
+
img_hash = api.pointcloud.upload_related_image(img_path)
|
|
116
131
|
if "deviceId" not in meta_json[ApiField.META].keys():
|
|
117
132
|
camera_names.append(f"CAM_{str(img_ind).zfill(2)}")
|
|
118
133
|
else:
|
|
@@ -121,17 +136,87 @@ class PointcloudConverter(BaseConverter):
|
|
|
121
136
|
{
|
|
122
137
|
ApiField.ENTITY_ID: pcd_id,
|
|
123
138
|
ApiField.NAME: meta_json[ApiField.NAME],
|
|
124
|
-
ApiField.HASH:
|
|
139
|
+
ApiField.HASH: img_hash,
|
|
125
140
|
ApiField.META: meta_json[ApiField.META],
|
|
126
141
|
}
|
|
127
142
|
)
|
|
128
|
-
|
|
143
|
+
|
|
144
|
+
if fig_path is not None and os.path.isfile(fig_path):
|
|
145
|
+
try:
|
|
146
|
+
figs_json = load_json_file(fig_path)
|
|
147
|
+
pcl_to_rimg_figures.setdefault(pcd_id, {})[img_hash] = figs_json
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.debug(f"Failed to read figures json '{fig_path}': {repr(e)}")
|
|
150
|
+
|
|
129
151
|
except Exception as e:
|
|
130
152
|
logger.warn(
|
|
131
153
|
f"Failed to upload related image or add it to pointcloud: {repr(e)}"
|
|
132
154
|
)
|
|
133
155
|
continue
|
|
134
156
|
|
|
157
|
+
# add images for this point cloud
|
|
158
|
+
if len(rimg_infos) > 0:
|
|
159
|
+
try:
|
|
160
|
+
uploaded_rimgs = api.pointcloud.add_related_images(rimg_infos, camera_names)
|
|
161
|
+
# build mapping hash->id
|
|
162
|
+
for info, uploaded in zip(rimg_infos, uploaded_rimgs):
|
|
163
|
+
img_hash = info.get(ApiField.HASH)
|
|
164
|
+
img_id = (
|
|
165
|
+
uploaded.get(ApiField.ID)
|
|
166
|
+
if isinstance(uploaded, dict)
|
|
167
|
+
else getattr(uploaded, "id", None)
|
|
168
|
+
)
|
|
169
|
+
if img_hash is not None and img_id is not None:
|
|
170
|
+
pcl_to_hash_to_id.setdefault(pcd_id, {})[img_hash] = img_id
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.debug(f"Failed to add related images to pointcloud: {repr(e)}")
|
|
173
|
+
|
|
174
|
+
# ---- upload figures for processed batch ----
|
|
175
|
+
if len(pcl_to_rimg_figures) > 0:
|
|
176
|
+
try:
|
|
177
|
+
dataset_info = api.dataset.get_info_by_id(dataset_id)
|
|
178
|
+
project_id = dataset_info.project_id
|
|
179
|
+
|
|
180
|
+
figures_payload: List[Dict] = []
|
|
181
|
+
|
|
182
|
+
for pcl_id, hash_to_figs in pcl_to_rimg_figures.items():
|
|
183
|
+
hash_to_ids = pcl_to_hash_to_id.get(pcl_id, {})
|
|
184
|
+
if len(hash_to_ids) == 0:
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
for img_hash, figs_json in hash_to_figs.items():
|
|
188
|
+
if img_hash not in hash_to_ids:
|
|
189
|
+
continue
|
|
190
|
+
rimg_id = hash_to_ids[img_hash]
|
|
191
|
+
for fig in figs_json:
|
|
192
|
+
try:
|
|
193
|
+
fig[ApiField.ENTITY_ID] = rimg_id
|
|
194
|
+
fig[ApiField.DATASET_ID] = dataset_id
|
|
195
|
+
fig[ApiField.PROJECT_ID] = project_id
|
|
196
|
+
if OBJECT_KEY in fig:
|
|
197
|
+
fig[ApiField.OBJECT_ID] = key_id_map.get_object_id(
|
|
198
|
+
UUID(fig[OBJECT_KEY])
|
|
199
|
+
)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
logger.debug(
|
|
202
|
+
f"Failed to process figure json for img_hash={img_hash}: {repr(e)}"
|
|
203
|
+
)
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
figures_payload.extend(figs_json)
|
|
207
|
+
|
|
208
|
+
if len(figures_payload) > 0:
|
|
209
|
+
try:
|
|
210
|
+
api.image.figure.create_bulk(
|
|
211
|
+
figures_json=figures_payload, dataset_id=dataset_id
|
|
212
|
+
)
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.debug(
|
|
215
|
+
f"Failed to upload figures for related images: {repr(e)}"
|
|
216
|
+
)
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logger.debug(f"Unexpected error during related image figures upload: {repr(e)}")
|
|
219
|
+
|
|
135
220
|
if log_progress:
|
|
136
221
|
progress_cb(len(batch))
|
|
137
222
|
|
|
@@ -143,7 +228,7 @@ class PointcloudConverter(BaseConverter):
|
|
|
143
228
|
def _collect_items_if_format_not_detected(self) -> Tuple[List[Item], bool, Set[str]]:
|
|
144
229
|
only_modality_items = True
|
|
145
230
|
unsupported_exts = set()
|
|
146
|
-
pcd_list, rimg_dict, rimg_ann_dict = [], {}, {}
|
|
231
|
+
pcd_list, rimg_dict, rimg_ann_dict, rimg_fig_dict = [], {}, {}, {}
|
|
147
232
|
used_img_ext = set()
|
|
148
233
|
for root, _, files in os.walk(self._input_data):
|
|
149
234
|
for file in files:
|
|
@@ -170,6 +255,8 @@ class PointcloudConverter(BaseConverter):
|
|
|
170
255
|
pcd_list.append(full_path)
|
|
171
256
|
except:
|
|
172
257
|
pass
|
|
258
|
+
elif file.endswith(".figures.json"):
|
|
259
|
+
rimg_fig_dict[file] = full_path
|
|
173
260
|
else:
|
|
174
261
|
only_modality_items = False
|
|
175
262
|
unsupported_exts.add(ext)
|
|
@@ -179,9 +266,13 @@ class PointcloudConverter(BaseConverter):
|
|
|
179
266
|
for pcd_path in pcd_list:
|
|
180
267
|
item = self.Item(pcd_path)
|
|
181
268
|
rimg, rimg_ann = helpers.find_related_items(
|
|
182
|
-
item.name, used_img_ext, rimg_dict, rimg_ann_dict
|
|
269
|
+
item.name, list(used_img_ext), rimg_dict, rimg_ann_dict
|
|
183
270
|
)
|
|
184
271
|
if rimg is not None and rimg_ann is not None:
|
|
185
|
-
|
|
272
|
+
rimg_ext = get_file_ext(rimg)
|
|
273
|
+
rimg_fig_path = rimg_fig_dict.get(f"{get_file_name(rimg)}{rimg_ext}.figures.json")
|
|
274
|
+
if rimg_fig_path is None:
|
|
275
|
+
rimg_fig_path = rimg_fig_dict.get(f"{get_file_name(rimg)}.figures.json")
|
|
276
|
+
item.set_related_images((rimg, rimg_ann, rimg_fig_path))
|
|
186
277
|
items.append(item)
|
|
187
278
|
return items, only_modality_items, unsupported_exts
|
|
@@ -101,7 +101,11 @@ class SLYPointcloudConverter(PointcloudConverter):
|
|
|
101
101
|
item.name, used_img_ext, rimg_dict, rimg_ann_dict
|
|
102
102
|
)
|
|
103
103
|
if rimg is not None and rimg_ann is not None:
|
|
104
|
-
|
|
104
|
+
rimg_ext = get_file_ext(rimg)
|
|
105
|
+
rimg_fig_path = rimg_ann.replace(f"{rimg_ext}.json", f"{rimg_ext}.figures.json")
|
|
106
|
+
if not os.path.exists(rimg_fig_path):
|
|
107
|
+
rimg_fig_path = None
|
|
108
|
+
item.set_related_images((rimg, rimg_ann, rimg_fig_path))
|
|
105
109
|
self._items.append(item)
|
|
106
110
|
return sly_ann_detected
|
|
107
111
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import imghdr
|
|
2
2
|
import os
|
|
3
3
|
from typing import Dict, List, Optional, Set, Tuple, Union
|
|
4
|
+
from uuid import UUID
|
|
4
5
|
|
|
5
6
|
from supervisely import (
|
|
6
7
|
Api,
|
|
@@ -16,7 +17,9 @@ from supervisely.io.fs import get_file_ext, get_file_name
|
|
|
16
17
|
from supervisely.io.json import load_json_file
|
|
17
18
|
from supervisely.pointcloud.pointcloud import ALLOWED_POINTCLOUD_EXTENSIONS
|
|
18
19
|
from supervisely.pointcloud.pointcloud import validate_ext as validate_pcd_ext
|
|
20
|
+
from supervisely.pointcloud_annotation.constants import OBJECT_KEY
|
|
19
21
|
from supervisely.project.project_settings import LabelingInterface
|
|
22
|
+
from supervisely.video_annotation.key_id_map import KeyIdMap
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class PointcloudEpisodeConverter(BaseConverter):
|
|
@@ -47,7 +50,14 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
47
50
|
def create_empty_annotation(self) -> PointcloudEpisodeAnnotation:
|
|
48
51
|
return PointcloudEpisodeAnnotation()
|
|
49
52
|
|
|
50
|
-
def set_related_images(self, related_images:
|
|
53
|
+
def set_related_images(self, related_images: Tuple[str, str, Optional[str]]) -> None:
|
|
54
|
+
"""Adds related image to the item.
|
|
55
|
+
|
|
56
|
+
related_images tuple:
|
|
57
|
+
- path to image
|
|
58
|
+
- path to .json with image metadata
|
|
59
|
+
- path to .figures.json (can be None if no figures)
|
|
60
|
+
"""
|
|
51
61
|
self._related_images.append(related_images)
|
|
52
62
|
|
|
53
63
|
def __init__(
|
|
@@ -104,7 +114,10 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
104
114
|
else:
|
|
105
115
|
progress_cb = None
|
|
106
116
|
|
|
107
|
-
frame_to_pointcloud_ids = {}
|
|
117
|
+
frame_to_pointcloud_ids: Dict[int, int] = {}
|
|
118
|
+
pcl_to_rimg_figures: Dict[int, Dict[str, List[Dict]]] = {}
|
|
119
|
+
pcl_to_hash_to_id: Dict[int, Dict[str, int]] = {}
|
|
120
|
+
key_id_map = KeyIdMap()
|
|
108
121
|
for batch in batched(self._items, batch_size=batch_size):
|
|
109
122
|
item_names = []
|
|
110
123
|
item_paths = []
|
|
@@ -129,12 +142,21 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
129
142
|
rimg_infos = []
|
|
130
143
|
camera_names = []
|
|
131
144
|
if len(item._related_images) > 0:
|
|
132
|
-
img_paths
|
|
145
|
+
img_paths: List[str] = []
|
|
146
|
+
ann_paths: List[str] = []
|
|
147
|
+
fig_paths: List[Optional[str]] = []
|
|
148
|
+
|
|
149
|
+
for triple in item._related_images:
|
|
150
|
+
img_paths.append(triple[0])
|
|
151
|
+
ann_paths.append(triple[1])
|
|
152
|
+
fig_paths.append(triple[2] if len(triple) > 2 else None)
|
|
153
|
+
|
|
133
154
|
rimg_hashes = api.pointcloud_episode.upload_related_images(img_paths)
|
|
134
|
-
|
|
135
|
-
|
|
155
|
+
|
|
156
|
+
for img_ind, (img_hash, ann_path, fig_path) in enumerate(
|
|
157
|
+
zip(rimg_hashes, ann_paths, fig_paths)
|
|
136
158
|
):
|
|
137
|
-
meta_json = load_json_file(
|
|
159
|
+
meta_json = load_json_file(ann_path)
|
|
138
160
|
try:
|
|
139
161
|
if ApiField.META not in meta_json:
|
|
140
162
|
raise ValueError("Related image meta not found in json file.")
|
|
@@ -152,19 +174,102 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
152
174
|
ApiField.META: meta_json[ApiField.META],
|
|
153
175
|
}
|
|
154
176
|
)
|
|
155
|
-
|
|
177
|
+
|
|
178
|
+
if fig_path is not None:
|
|
179
|
+
try:
|
|
180
|
+
figs_json = load_json_file(fig_path)
|
|
181
|
+
pcl_to_rimg_figures.setdefault(pcd_id, {})[img_hash] = figs_json
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.debug(
|
|
184
|
+
f"Failed to read figures json '{fig_path}': {repr(e)}"
|
|
185
|
+
)
|
|
186
|
+
|
|
156
187
|
except Exception as e:
|
|
157
188
|
logger.warning(
|
|
158
|
-
f"Failed to
|
|
189
|
+
f"Failed to process related image meta '{ann_path}': {repr(e)}"
|
|
159
190
|
)
|
|
160
191
|
continue
|
|
161
192
|
|
|
193
|
+
uploaded_rimgs = api.pointcloud.add_related_images(rimg_infos, camera_names)
|
|
194
|
+
try:
|
|
195
|
+
# build mapping hash->id
|
|
196
|
+
for info, uploaded in zip(rimg_infos, uploaded_rimgs):
|
|
197
|
+
img_hash = info.get(ApiField.HASH)
|
|
198
|
+
img_id = (
|
|
199
|
+
uploaded.get(ApiField.ID)
|
|
200
|
+
if isinstance(uploaded, dict)
|
|
201
|
+
else getattr(uploaded, "id", None)
|
|
202
|
+
)
|
|
203
|
+
if img_hash is not None and img_id is not None:
|
|
204
|
+
pcl_to_hash_to_id.setdefault(pcd_id, {})[img_hash] = img_id
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.debug(
|
|
207
|
+
f"Failed to build hash->ID mapping for related images: {repr(e)}"
|
|
208
|
+
)
|
|
209
|
+
|
|
162
210
|
if log_progress:
|
|
163
211
|
progress_cb(len(batch))
|
|
164
212
|
|
|
165
213
|
if self.items_count > 0:
|
|
166
214
|
ann = self.to_supervisely(self._items[0], meta, renamed_classes, renamed_tags)
|
|
167
|
-
api.pointcloud_episode.annotation.append(
|
|
215
|
+
api.pointcloud_episode.annotation.append(
|
|
216
|
+
dataset_id, ann, frame_to_pointcloud_ids, key_id_map
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# ---- upload figures for processed batch ----
|
|
220
|
+
if len(pcl_to_rimg_figures) > 0:
|
|
221
|
+
if log_progress:
|
|
222
|
+
progress, progress_cb = self.get_progress(
|
|
223
|
+
self.items_count, "Uploading figures for related images..."
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
progress_cb = None
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
dataset_info = api.dataset.get_info_by_id(dataset_id)
|
|
230
|
+
project_id = dataset_info.project_id
|
|
231
|
+
|
|
232
|
+
figures_to_upload: List[Dict] = []
|
|
233
|
+
for pcl_id, hash_to_figs in pcl_to_rimg_figures.items():
|
|
234
|
+
hash_to_ids = pcl_to_hash_to_id.get(pcl_id, {})
|
|
235
|
+
if len(hash_to_ids) == 0:
|
|
236
|
+
continue
|
|
237
|
+
|
|
238
|
+
for img_hash, figs_json in hash_to_figs.items():
|
|
239
|
+
if img_hash not in hash_to_ids:
|
|
240
|
+
continue
|
|
241
|
+
img_id = hash_to_ids[img_hash]
|
|
242
|
+
|
|
243
|
+
for fig in figs_json:
|
|
244
|
+
try:
|
|
245
|
+
fig[ApiField.ENTITY_ID] = img_id
|
|
246
|
+
fig[ApiField.DATASET_ID] = dataset_id
|
|
247
|
+
fig[ApiField.PROJECT_ID] = project_id
|
|
248
|
+
if OBJECT_KEY in fig:
|
|
249
|
+
fig[ApiField.OBJECT_ID] = key_id_map.get_object_id(
|
|
250
|
+
UUID(fig[OBJECT_KEY])
|
|
251
|
+
)
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.debug(
|
|
254
|
+
f"Failed to process figure json for img_hash={img_hash}: {repr(e)}"
|
|
255
|
+
)
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
figures_to_upload.extend(figs_json)
|
|
259
|
+
|
|
260
|
+
if len(figures_to_upload) > 0:
|
|
261
|
+
try:
|
|
262
|
+
api.image.figure.create_bulk(
|
|
263
|
+
figures_json=figures_to_upload, dataset_id=dataset_id
|
|
264
|
+
)
|
|
265
|
+
except Exception as e:
|
|
266
|
+
logger.debug(f"Failed to upload figures for related images: {repr(e)}")
|
|
267
|
+
|
|
268
|
+
if log_progress:
|
|
269
|
+
progress_cb(len(pcl_to_rimg_figures))
|
|
270
|
+
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logger.debug(f"Unexpected error during figures upload: {repr(e)}")
|
|
168
273
|
|
|
169
274
|
if log_progress:
|
|
170
275
|
if is_development():
|
|
@@ -177,7 +282,7 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
177
282
|
pcd_dict = {}
|
|
178
283
|
frames_pcd_map = None
|
|
179
284
|
used_img_ext = set()
|
|
180
|
-
rimg_dict, rimg_json_dict = {}, {}
|
|
285
|
+
rimg_dict, rimg_json_dict, rimg_fig_dict = {}, {}, {}
|
|
181
286
|
for root, _, files in os.walk(self._input_data):
|
|
182
287
|
for file in files:
|
|
183
288
|
full_path = os.path.join(root, file)
|
|
@@ -187,7 +292,9 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
187
292
|
|
|
188
293
|
ext = get_file_ext(full_path)
|
|
189
294
|
recognized_ext = imghdr.what(full_path)
|
|
190
|
-
if
|
|
295
|
+
if file.endswith(".figures.json"):
|
|
296
|
+
rimg_fig_dict[file] = full_path
|
|
297
|
+
elif ext == ".json":
|
|
191
298
|
rimg_json_dict[file] = full_path
|
|
192
299
|
elif recognized_ext:
|
|
193
300
|
if ext.lower() == ".pcd":
|
|
@@ -226,9 +333,12 @@ class PointcloudEpisodeConverter(BaseConverter):
|
|
|
226
333
|
if rimg_name in rimg_dict:
|
|
227
334
|
rimg_path = rimg_dict[rimg_name]
|
|
228
335
|
rimg_ann_name = f"{rimg_name}.json"
|
|
336
|
+
rimg_fig_name = f"{rimg_name}.figures.json"
|
|
337
|
+
rimg_fig_path = rimg_fig_dict.get(rimg_fig_name)
|
|
338
|
+
|
|
229
339
|
if rimg_ann_name in rimg_json_dict:
|
|
230
340
|
rimg_ann_path = rimg_json_dict[rimg_ann_name]
|
|
231
|
-
item.set_related_images((rimg_path, rimg_ann_path))
|
|
341
|
+
item.set_related_images((rimg_path, rimg_ann_path, rimg_fig_path))
|
|
232
342
|
items.append(item)
|
|
233
343
|
else:
|
|
234
344
|
logger.warning(f"Pointcloud file {pcd_name} not found. Skipping frame.")
|
|
@@ -127,7 +127,11 @@ class SLYPointcloudEpisodesConverter(PointcloudEpisodeConverter):
|
|
|
127
127
|
rimg_ann_name = f"{rimg_name}.json"
|
|
128
128
|
if rimg_ann_name in rimg_json_dict:
|
|
129
129
|
rimg_ann_path = rimg_json_dict[rimg_ann_name]
|
|
130
|
-
|
|
130
|
+
rimg_fig_name = f"{rimg_name}.figures.json"
|
|
131
|
+
rimg_fig_path = os.path.join(os.path.dirname(rimg_ann_path), rimg_fig_name)
|
|
132
|
+
if not os.path.exists(rimg_fig_path):
|
|
133
|
+
rimg_fig_path = None
|
|
134
|
+
item.set_related_images((rimg_path, rimg_ann_path, rimg_fig_path))
|
|
131
135
|
self._items.append(item)
|
|
132
136
|
else:
|
|
133
137
|
logger.warning(f"Pointcloud file {pcd_name} not found. Skipping frame.")
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
import os
|
|
7
7
|
import random
|
|
8
8
|
from typing import Callable, Dict, List, NamedTuple, Optional, Tuple, Union
|
|
9
|
+
from uuid import UUID
|
|
9
10
|
|
|
10
11
|
from tqdm import tqdm
|
|
11
12
|
|
|
@@ -17,6 +18,7 @@ from supervisely.api.pointcloud.pointcloud_api import PointcloudInfo
|
|
|
17
18
|
from supervisely.collection.key_indexed_collection import KeyIndexedCollection
|
|
18
19
|
from supervisely.io.fs import dir_exists, get_file_name, list_files, mkdir, touch
|
|
19
20
|
from supervisely.io.json import dump_json_file, load_json_file
|
|
21
|
+
from supervisely.pointcloud_annotation.constants import OBJECT_KEY
|
|
20
22
|
from supervisely.pointcloud_annotation.pointcloud_episode_annotation import (
|
|
21
23
|
PointcloudEpisodeAnnotation,
|
|
22
24
|
)
|
|
@@ -354,6 +356,7 @@ class PointcloudEpisodeProject(PointcloudProject):
|
|
|
354
356
|
def _list_items_for_splits(project) -> List[EpisodeItemInfo]:
|
|
355
357
|
items = []
|
|
356
358
|
for dataset in project.datasets:
|
|
359
|
+
dataset: PointcloudEpisodeDataset
|
|
357
360
|
for item_name in dataset:
|
|
358
361
|
items.append(
|
|
359
362
|
EpisodeItemInfo(
|
|
@@ -745,7 +748,7 @@ def download_pointcloud_episode_project(
|
|
|
745
748
|
datasets_infos = api.dataset.get_list(project_id)
|
|
746
749
|
|
|
747
750
|
for dataset in datasets_infos:
|
|
748
|
-
dataset_fs = project_fs.create_dataset(dataset.name)
|
|
751
|
+
dataset_fs: PointcloudEpisodeDataset = project_fs.create_dataset(dataset.name)
|
|
749
752
|
pointclouds = api.pointcloud_episode.get_list(dataset.id)
|
|
750
753
|
|
|
751
754
|
# Download annotation to project_path/dataset_path/annotation.json
|
|
@@ -774,6 +777,30 @@ def download_pointcloud_episode_project(
|
|
|
774
777
|
pc_to_frame = {v: k for k, v in frame_to_pc_map.items()}
|
|
775
778
|
item_to_ann = {name: pc_to_frame[name] for name in pointcloud_names}
|
|
776
779
|
|
|
780
|
+
batch_rimg_figures = {}
|
|
781
|
+
if download_related_images:
|
|
782
|
+
try:
|
|
783
|
+
rimgs = api.pointcloud_episode.get_list_related_images_batch(
|
|
784
|
+
dataset.id, pointcloud_ids
|
|
785
|
+
)
|
|
786
|
+
if len(rimgs) > 0:
|
|
787
|
+
rimg_ids = [rimg[ApiField.ID] for rimg in rimgs]
|
|
788
|
+
batch_rimg_figures = api.image.figure.download(
|
|
789
|
+
dataset_id=dataset.id, image_ids=rimg_ids
|
|
790
|
+
)
|
|
791
|
+
else:
|
|
792
|
+
batch_rimg_figures = []
|
|
793
|
+
except Exception as e:
|
|
794
|
+
logger.info(
|
|
795
|
+
"INFO FOR DEBUGGING",
|
|
796
|
+
extra={
|
|
797
|
+
"project_id": project_id,
|
|
798
|
+
"dataset_id": dataset.id,
|
|
799
|
+
"pointcloud_ids": pointcloud_ids,
|
|
800
|
+
},
|
|
801
|
+
)
|
|
802
|
+
raise e
|
|
803
|
+
|
|
777
804
|
for pointcloud_id, pointcloud_name, pointcloud_info in zip(
|
|
778
805
|
pointcloud_ids, pointcloud_names, batch
|
|
779
806
|
):
|
|
@@ -830,6 +857,7 @@ def download_pointcloud_episode_project(
|
|
|
830
857
|
|
|
831
858
|
path_img = os.path.join(related_images_path, name)
|
|
832
859
|
path_json = os.path.join(related_images_path, name + ".json")
|
|
860
|
+
path_figures = os.path.join(related_images_path, name + ".figures.json")
|
|
833
861
|
|
|
834
862
|
try:
|
|
835
863
|
api.pointcloud_episode.download_related_image(rimage_id, path_img)
|
|
@@ -846,9 +874,37 @@ def download_pointcloud_episode_project(
|
|
|
846
874
|
},
|
|
847
875
|
)
|
|
848
876
|
raise e
|
|
849
|
-
|
|
850
877
|
dump_json_file(rimage_info, path_json)
|
|
851
878
|
|
|
879
|
+
try:
|
|
880
|
+
if rimage_id in batch_rimg_figures:
|
|
881
|
+
rimg_figures = batch_rimg_figures[rimage_id]
|
|
882
|
+
rimg_figures_json = []
|
|
883
|
+
for fig in rimg_figures:
|
|
884
|
+
fig_json = fig.to_json()
|
|
885
|
+
if ApiField.OBJECT_ID in fig_json:
|
|
886
|
+
fig_json[OBJECT_KEY] = str(
|
|
887
|
+
key_id_map.get_object_key(fig_json[ApiField.OBJECT_ID])
|
|
888
|
+
)
|
|
889
|
+
fig_json.pop(ApiField.OBJECT_ID, None)
|
|
890
|
+
rimg_figures_json.append(fig_json)
|
|
891
|
+
else:
|
|
892
|
+
raise RuntimeError(f"Figure {fig} has no object id")
|
|
893
|
+
dump_json_file(rimg_figures_json, path_figures)
|
|
894
|
+
except Exception as e:
|
|
895
|
+
logger.info(
|
|
896
|
+
"INFO FOR DEBUGGING",
|
|
897
|
+
extra={
|
|
898
|
+
"project_id": project_id,
|
|
899
|
+
"dataset_id": dataset.id,
|
|
900
|
+
"pointcloud_id": pointcloud_id,
|
|
901
|
+
"pointcloud_name": pointcloud_name,
|
|
902
|
+
"rimage_id": rimage_id,
|
|
903
|
+
"path_figures": path_figures,
|
|
904
|
+
},
|
|
905
|
+
)
|
|
906
|
+
raise e
|
|
907
|
+
|
|
852
908
|
pointcloud_info = pointcloud_info._asdict() if download_pointclouds_info else None
|
|
853
909
|
try:
|
|
854
910
|
dataset_fs.add_item_file(
|
|
@@ -901,6 +957,7 @@ def upload_pointcloud_episode_project(
|
|
|
901
957
|
|
|
902
958
|
key_id_map = KeyIdMap()
|
|
903
959
|
for dataset_fs in project_fs.datasets:
|
|
960
|
+
dataset_fs: PointcloudEpisodeDataset
|
|
904
961
|
ann_json_path = dataset_fs.get_ann_path()
|
|
905
962
|
|
|
906
963
|
if os.path.isfile(ann_json_path):
|
|
@@ -974,8 +1031,9 @@ def upload_pointcloud_episode_project(
|
|
|
974
1031
|
|
|
975
1032
|
# STEP 3 — upload photo context
|
|
976
1033
|
img_infos = {"img_paths": [], "img_metas": []}
|
|
977
|
-
|
|
978
1034
|
# STEP 3.1 — upload images
|
|
1035
|
+
pcl_to_rimg_figures: Dict[int, Dict[str, List[Dict]]] = {}
|
|
1036
|
+
pcl_to_hash_to_id: Dict[int, Dict[str, int]] = {}
|
|
979
1037
|
for pcl_info in pcl_infos:
|
|
980
1038
|
related_items = dataset_fs.get_related_images(pcl_info.name)
|
|
981
1039
|
images_paths_for_frame = [img_path for img_path, _ in related_items]
|
|
@@ -1014,6 +1072,7 @@ def upload_pointcloud_episode_project(
|
|
|
1014
1072
|
img_hash = next(images_hashes_iterator)
|
|
1015
1073
|
if "deviceId" not in meta_json[ApiField.META].keys():
|
|
1016
1074
|
meta_json[ApiField.META]["deviceId"] = f"CAM_{str(img_ind).zfill(2)}"
|
|
1075
|
+
|
|
1017
1076
|
img_infos["img_metas"].append(
|
|
1018
1077
|
{
|
|
1019
1078
|
ApiField.ENTITY_ID: pcl_info.id,
|
|
@@ -1022,10 +1081,38 @@ def upload_pointcloud_episode_project(
|
|
|
1022
1081
|
ApiField.META: meta_json[ApiField.META],
|
|
1023
1082
|
}
|
|
1024
1083
|
)
|
|
1084
|
+
img_name = meta_json[ApiField.NAME]
|
|
1085
|
+
related_images_dir = dataset_fs.get_related_images_path(pcl_info.name)
|
|
1086
|
+
fig_json_path = os.path.join(related_images_dir, img_name + ".figures.json")
|
|
1087
|
+
if os.path.isfile(fig_json_path):
|
|
1088
|
+
try:
|
|
1089
|
+
figs_json = load_json_file(fig_json_path)
|
|
1090
|
+
pcl_to_rimg_figures.setdefault(pcl_info.id, {})[img_hash] = figs_json
|
|
1091
|
+
except Exception as e:
|
|
1092
|
+
logger.info(
|
|
1093
|
+
"INFO FOR DEBUGGING",
|
|
1094
|
+
extra={
|
|
1095
|
+
"project_id": project.id,
|
|
1096
|
+
"dataset_id": dataset.id,
|
|
1097
|
+
"pointcloud_id": pcl_info.id,
|
|
1098
|
+
"fig_json_path": fig_json_path,
|
|
1099
|
+
},
|
|
1100
|
+
)
|
|
1101
|
+
raise e
|
|
1025
1102
|
|
|
1026
1103
|
if len(img_infos["img_metas"]) > 0:
|
|
1027
1104
|
try:
|
|
1028
|
-
api.pointcloud_episode.add_related_images(img_infos["img_metas"])
|
|
1105
|
+
uploaded_rimgs = api.pointcloud_episode.add_related_images(img_infos["img_metas"])
|
|
1106
|
+
# build mapping hash->id
|
|
1107
|
+
for info, uploaded in zip(img_infos["img_metas"], uploaded_rimgs):
|
|
1108
|
+
img_hash = info.get(ApiField.HASH)
|
|
1109
|
+
img_id = (
|
|
1110
|
+
uploaded.get(ApiField.ID)
|
|
1111
|
+
if isinstance(uploaded, dict)
|
|
1112
|
+
else getattr(uploaded, "id", None)
|
|
1113
|
+
)
|
|
1114
|
+
if img_hash is not None and img_id is not None:
|
|
1115
|
+
pcl_to_hash_to_id[img_hash] = img_id
|
|
1029
1116
|
except Exception as e:
|
|
1030
1117
|
logger.info(
|
|
1031
1118
|
"INFO FOR DEBUGGING",
|
|
@@ -1037,4 +1124,39 @@ def upload_pointcloud_episode_project(
|
|
|
1037
1124
|
)
|
|
1038
1125
|
raise e
|
|
1039
1126
|
|
|
1127
|
+
for pcl_info in pcl_infos:
|
|
1128
|
+
rimg_figures = pcl_to_rimg_figures.get(pcl_info.id)
|
|
1129
|
+
if not rimg_figures:
|
|
1130
|
+
continue
|
|
1131
|
+
|
|
1132
|
+
try:
|
|
1133
|
+
for img_hash, figs_json in rimg_figures.items():
|
|
1134
|
+
if img_hash in pcl_to_hash_to_id:
|
|
1135
|
+
img_id = pcl_to_hash_to_id[img_hash]
|
|
1136
|
+
for fig in figs_json:
|
|
1137
|
+
fig[ApiField.ENTITY_ID] = img_id
|
|
1138
|
+
fig[ApiField.DATASET_ID] = dataset.id
|
|
1139
|
+
fig[ApiField.PROJECT_ID] = project.id
|
|
1140
|
+
fig[ApiField.OBJECT_ID] = key_id_map.get_object_id(
|
|
1141
|
+
UUID(fig[OBJECT_KEY])
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
api.image.figure.create_bulk(
|
|
1145
|
+
figures_json=[fig for figs in rimg_figures.values() for fig in figs],
|
|
1146
|
+
dataset_id=dataset.id,
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
except Exception as e:
|
|
1150
|
+
logger.info(
|
|
1151
|
+
"INFO FOR DEBUGGING",
|
|
1152
|
+
extra={
|
|
1153
|
+
"project_id": project.id,
|
|
1154
|
+
"dataset_id": dataset.id,
|
|
1155
|
+
"pointcloud_id": pcl_info.id,
|
|
1156
|
+
"pointcloud_name": pcl_info.name,
|
|
1157
|
+
"rimg_figures": rimg_figures,
|
|
1158
|
+
},
|
|
1159
|
+
)
|
|
1160
|
+
raise e
|
|
1161
|
+
|
|
1040
1162
|
return project.id, project.name
|
|
@@ -6,6 +6,7 @@ import random
|
|
|
6
6
|
import shutil
|
|
7
7
|
from collections import namedtuple
|
|
8
8
|
from typing import Callable, Dict, List, NamedTuple, Optional, Tuple, Union
|
|
9
|
+
from uuid import UUID
|
|
9
10
|
|
|
10
11
|
import numpy as np
|
|
11
12
|
from tqdm import tqdm
|
|
@@ -30,6 +31,7 @@ from supervisely.io.fs import (
|
|
|
30
31
|
touch,
|
|
31
32
|
)
|
|
32
33
|
from supervisely.io.json import dump_json_file, load_json_file
|
|
34
|
+
from supervisely.pointcloud_annotation.constants import OBJECT_KEY
|
|
33
35
|
from supervisely.pointcloud_annotation.pointcloud_annotation import PointcloudAnnotation
|
|
34
36
|
from supervisely.project.project import Dataset, OpenMode
|
|
35
37
|
from supervisely.project.project import read_single_project as read_project_wrapper
|
|
@@ -500,6 +502,31 @@ class PointcloudDataset(VideoDataset):
|
|
|
500
502
|
def get_pointcloud_info_path(self, item_name: str) -> str:
|
|
501
503
|
return self.get_item_info_path(item_name)
|
|
502
504
|
|
|
505
|
+
def get_related_image_figures(self, item_name: str, img_name: str) -> List[Dict]:
|
|
506
|
+
"""
|
|
507
|
+
Get figures from related image with given name.
|
|
508
|
+
|
|
509
|
+
:param item_name: Name of the item in the dataset.
|
|
510
|
+
:type item_name: str
|
|
511
|
+
:param img_name: Related image name.
|
|
512
|
+
:type img_name: str
|
|
513
|
+
:return: List of figures from related image.
|
|
514
|
+
:rtype: List[Dict]
|
|
515
|
+
:raises: :class:`RuntimeError` if item not found in the project
|
|
516
|
+
"""
|
|
517
|
+
results = []
|
|
518
|
+
path = self.get_related_images_path(item_name)
|
|
519
|
+
if dir_exists(path):
|
|
520
|
+
files = list_files(path, sly_image.SUPPORTED_IMG_EXTS)
|
|
521
|
+
for file in files:
|
|
522
|
+
if get_file_name_with_ext(file) != img_name:
|
|
523
|
+
continue
|
|
524
|
+
fig_path = os.path.join(path, get_file_name_with_ext(file) + ".figures.json")
|
|
525
|
+
if file_exists(fig_path):
|
|
526
|
+
figs = load_json_file(fig_path)
|
|
527
|
+
results.append(figs)
|
|
528
|
+
return results
|
|
529
|
+
|
|
503
530
|
|
|
504
531
|
class PointcloudProject(VideoProject):
|
|
505
532
|
"""
|
|
@@ -989,6 +1016,26 @@ def download_pointcloud_project(
|
|
|
989
1016
|
pointcloud_names = [pointcloud_info.name for pointcloud_info in batch]
|
|
990
1017
|
|
|
991
1018
|
ann_jsons = api.pointcloud.annotation.download_bulk(dataset.id, pointcloud_ids)
|
|
1019
|
+
if download_related_images:
|
|
1020
|
+
try:
|
|
1021
|
+
rimgs = api.pointcloud.get_list_related_images_batch(dataset.id, pointcloud_ids)
|
|
1022
|
+
if len(rimgs) > 0:
|
|
1023
|
+
rimg_ids = [rimg[ApiField.ID] for rimg in rimgs]
|
|
1024
|
+
batch_rimg_figures = api.image.figure.download(
|
|
1025
|
+
dataset_id=dataset.id, image_ids=rimg_ids
|
|
1026
|
+
)
|
|
1027
|
+
else:
|
|
1028
|
+
batch_rimg_figures = []
|
|
1029
|
+
except Exception as e:
|
|
1030
|
+
logger.info(
|
|
1031
|
+
"INFO FOR DEBUGGING",
|
|
1032
|
+
extra={
|
|
1033
|
+
"project_id": project_id,
|
|
1034
|
+
"dataset_id": dataset.id,
|
|
1035
|
+
"pointcloud_ids": pointcloud_ids,
|
|
1036
|
+
},
|
|
1037
|
+
)
|
|
1038
|
+
raise e
|
|
992
1039
|
|
|
993
1040
|
for pointcloud_id, pointcloud_name, pointcloud_info, ann_json in zip(
|
|
994
1041
|
pointcloud_ids, pointcloud_names, batch, ann_jsons
|
|
@@ -1015,6 +1062,25 @@ def download_pointcloud_project(
|
|
|
1015
1062
|
else:
|
|
1016
1063
|
touch(pointcloud_file_path)
|
|
1017
1064
|
|
|
1065
|
+
pointcloud_file_path = pointcloud_file_path if download_items else None
|
|
1066
|
+
pointcloud_info = pointcloud_info._asdict() if download_pointclouds_info else None
|
|
1067
|
+
try:
|
|
1068
|
+
pointcloud_ann = PointcloudAnnotation.from_json(
|
|
1069
|
+
ann_json, project_fs.meta, key_id_map
|
|
1070
|
+
)
|
|
1071
|
+
except Exception as e:
|
|
1072
|
+
logger.info(
|
|
1073
|
+
"INFO FOR DEBUGGING",
|
|
1074
|
+
extra={
|
|
1075
|
+
"project_id": project_id,
|
|
1076
|
+
"dataset_id": dataset.id,
|
|
1077
|
+
"pointcloud_id": pointcloud_id,
|
|
1078
|
+
"pointcloud_name": pointcloud_name,
|
|
1079
|
+
"ann_json": ann_json,
|
|
1080
|
+
},
|
|
1081
|
+
)
|
|
1082
|
+
raise e
|
|
1083
|
+
|
|
1018
1084
|
if download_related_images:
|
|
1019
1085
|
related_images_path = dataset_fs.get_related_images_path(pointcloud_name)
|
|
1020
1086
|
try:
|
|
@@ -1048,6 +1114,7 @@ def download_pointcloud_project(
|
|
|
1048
1114
|
|
|
1049
1115
|
path_img = os.path.join(related_images_path, name)
|
|
1050
1116
|
path_json = os.path.join(related_images_path, name + ".json")
|
|
1117
|
+
path_figures = os.path.join(related_images_path, name + ".figures.json")
|
|
1051
1118
|
|
|
1052
1119
|
try:
|
|
1053
1120
|
api.pointcloud.download_related_image(rimage_id, path_img)
|
|
@@ -1066,24 +1133,36 @@ def download_pointcloud_project(
|
|
|
1066
1133
|
raise e
|
|
1067
1134
|
dump_json_file(rimage_info, path_json)
|
|
1068
1135
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1136
|
+
try:
|
|
1137
|
+
if rimage_id in batch_rimg_figures:
|
|
1138
|
+
rimg_figures = batch_rimg_figures[rimage_id]
|
|
1139
|
+
rimg_figures_json = []
|
|
1140
|
+
for fig in rimg_figures:
|
|
1141
|
+
fig_json = fig.to_json()
|
|
1142
|
+
if ApiField.OBJECT_ID in fig_json:
|
|
1143
|
+
fig_json[OBJECT_KEY] = str(
|
|
1144
|
+
key_id_map.get_object_key(fig_json[ApiField.OBJECT_ID])
|
|
1145
|
+
)
|
|
1146
|
+
fig_json.pop(ApiField.OBJECT_ID, None)
|
|
1147
|
+
rimg_figures_json.append(fig_json)
|
|
1148
|
+
else:
|
|
1149
|
+
raise RuntimeError(f"Figure {fig} has no object id")
|
|
1150
|
+
|
|
1151
|
+
dump_json_file(rimg_figures_json, path_figures)
|
|
1152
|
+
except Exception as e:
|
|
1153
|
+
logger.info(
|
|
1154
|
+
"INFO FOR DEBUGGING",
|
|
1155
|
+
extra={
|
|
1156
|
+
"project_id": project_id,
|
|
1157
|
+
"dataset_id": dataset.id,
|
|
1158
|
+
"pointcloud_id": pointcloud_id,
|
|
1159
|
+
"pointcloud_name": pointcloud_name,
|
|
1160
|
+
"rimage_id": rimage_id,
|
|
1161
|
+
"path_figures": path_figures,
|
|
1162
|
+
},
|
|
1163
|
+
)
|
|
1164
|
+
raise e
|
|
1165
|
+
|
|
1087
1166
|
try:
|
|
1088
1167
|
dataset_fs.add_item_file(
|
|
1089
1168
|
pointcloud_name,
|
|
@@ -1206,6 +1285,7 @@ def upload_pointcloud_project(
|
|
|
1206
1285
|
if len(related_items) != 0:
|
|
1207
1286
|
rimg_infos = []
|
|
1208
1287
|
camera_names = []
|
|
1288
|
+
rimg_figures = {}
|
|
1209
1289
|
for img_ind, (img_path, meta_json) in enumerate(related_items):
|
|
1210
1290
|
try:
|
|
1211
1291
|
img = api.pointcloud.upload_related_image(img_path)
|
|
@@ -1233,9 +1313,37 @@ def upload_pointcloud_project(
|
|
|
1233
1313
|
ApiField.META: meta_json[ApiField.META],
|
|
1234
1314
|
}
|
|
1235
1315
|
)
|
|
1316
|
+
img_name = meta_json[ApiField.NAME]
|
|
1317
|
+
fig_json_path = os.path.join(related_images_dir, img_name + ".figures.json")
|
|
1318
|
+
if os.path.exists(fig_json_path):
|
|
1319
|
+
try:
|
|
1320
|
+
rimg_figures[img] = load_json_file(fig_json_path)
|
|
1321
|
+
except Exception as e:
|
|
1322
|
+
logger.info(
|
|
1323
|
+
"INFO FOR DEBUGGING",
|
|
1324
|
+
extra={
|
|
1325
|
+
"project_id": project.id,
|
|
1326
|
+
"dataset_id": dataset.id,
|
|
1327
|
+
"pointcloud_id": pointcloud.id,
|
|
1328
|
+
"pointcloud_name": pointcloud.name,
|
|
1329
|
+
"fig_json_path": fig_json_path,
|
|
1330
|
+
},
|
|
1331
|
+
)
|
|
1332
|
+
raise e
|
|
1236
1333
|
|
|
1237
1334
|
try:
|
|
1238
|
-
api.pointcloud.add_related_images(rimg_infos, camera_names)
|
|
1335
|
+
uploaded_rimgs = api.pointcloud.add_related_images(rimg_infos, camera_names)
|
|
1336
|
+
# build mapping hash->id
|
|
1337
|
+
pcl_to_hash_to_id = {}
|
|
1338
|
+
for info, uploaded in zip(rimg_infos, uploaded_rimgs):
|
|
1339
|
+
img_hash = info.get(ApiField.HASH)
|
|
1340
|
+
img_id = (
|
|
1341
|
+
uploaded.get(ApiField.ID)
|
|
1342
|
+
if isinstance(uploaded, dict)
|
|
1343
|
+
else getattr(uploaded, "id", None)
|
|
1344
|
+
)
|
|
1345
|
+
if img_hash is not None and img_id is not None:
|
|
1346
|
+
pcl_to_hash_to_id[img_hash] = img_id
|
|
1239
1347
|
except Exception as e:
|
|
1240
1348
|
logger.info(
|
|
1241
1349
|
"INFO FOR DEBUGGING",
|
|
@@ -1249,6 +1357,39 @@ def upload_pointcloud_project(
|
|
|
1249
1357
|
},
|
|
1250
1358
|
)
|
|
1251
1359
|
raise e
|
|
1360
|
+
|
|
1361
|
+
# upload figures
|
|
1362
|
+
if len(rimg_figures) > 0:
|
|
1363
|
+
hash_to_ids = pcl_to_hash_to_id
|
|
1364
|
+
figures_payload = []
|
|
1365
|
+
for img_hash, figs_json in rimg_figures.items():
|
|
1366
|
+
if img_hash not in hash_to_ids:
|
|
1367
|
+
continue
|
|
1368
|
+
img_id = hash_to_ids[img_hash]
|
|
1369
|
+
for fig in figs_json:
|
|
1370
|
+
try:
|
|
1371
|
+
fig[ApiField.ENTITY_ID] = img_id
|
|
1372
|
+
fig[ApiField.DATASET_ID] = dataset.id
|
|
1373
|
+
fig[ApiField.PROJECT_ID] = project.id
|
|
1374
|
+
fig[ApiField.OBJECT_ID] = key_id_map.get_object_id(
|
|
1375
|
+
UUID(fig[OBJECT_KEY])
|
|
1376
|
+
)
|
|
1377
|
+
except Exception as e:
|
|
1378
|
+
logger.debug(
|
|
1379
|
+
f"Failed to process figure json for img_hash={img_hash}: {repr(e)}"
|
|
1380
|
+
)
|
|
1381
|
+
continue
|
|
1382
|
+
|
|
1383
|
+
figures_payload.extend(figs_json)
|
|
1384
|
+
|
|
1385
|
+
if len(figures_payload) > 0:
|
|
1386
|
+
try:
|
|
1387
|
+
api.image.figure.create_bulk(
|
|
1388
|
+
figures_json=figures_payload, dataset_id=dataset.id
|
|
1389
|
+
)
|
|
1390
|
+
except Exception as e:
|
|
1391
|
+
logger.debug(f"Failed to upload figures for related images: {repr(e)}")
|
|
1392
|
+
|
|
1252
1393
|
if ds_progress:
|
|
1253
1394
|
ds_progress(1)
|
|
1254
1395
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
supervisely/README.md,sha256=XM-DiMC6To3I9RjQZ0c61905EFRR_jnCUx2q3uNR-X8,3331
|
|
2
|
-
supervisely/__init__.py,sha256=
|
|
2
|
+
supervisely/__init__.py,sha256=tliCAEdCVwLeYtNSmvSGKm1JOK1aXsP0ckJ3042J-0U,10935
|
|
3
3
|
supervisely/_utils.py,sha256=Wrnck4645QXRZdmMpqIRQ_t6QynEAkYaFfcialxQBIE,19157
|
|
4
4
|
supervisely/function_wrapper.py,sha256=R5YajTQ0GnRp2vtjwfC9hINkzQc0JiyGsu8TER373xY,1912
|
|
5
5
|
supervisely/sly_logger.py,sha256=z92Vu5hmC0GgTIJO1n6kPDayRW9__8ix8hL6poDZj-Y,6274
|
|
@@ -59,7 +59,7 @@ supervisely/api/nn/deploy_api.py,sha256=fA5yk3-Q66BL821iu68HpsGWWO2qHXCcWTmrXoHE
|
|
|
59
59
|
supervisely/api/nn/neural_network_api.py,sha256=VYqpHb6xqCeEUofTNfOGUhvjI_5Di3T26qwVp5Z-0hE,10643
|
|
60
60
|
supervisely/api/pointcloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
61
|
supervisely/api/pointcloud/pointcloud_annotation_api.py,sha256=xIXqCu0rKYsGt5ezh2EFT2utwsVrr2Xo-MOWUCnbvXc,11259
|
|
62
|
-
supervisely/api/pointcloud/pointcloud_api.py,sha256=
|
|
62
|
+
supervisely/api/pointcloud/pointcloud_api.py,sha256=mPnVXWnxPxrb2P5giFLuabU969vV0xpeP6biETBNvUI,53875
|
|
63
63
|
supervisely/api/pointcloud/pointcloud_episode_annotation_api.py,sha256=zmDWDkRzUSfHKX65qDVrc44kNyYjfvItSwYmsERJ_8g,7012
|
|
64
64
|
supervisely/api/pointcloud/pointcloud_episode_api.py,sha256=xg1zRKONV9ly0-B72V1dR6OMPFIw35bujazuEdrPGTQ,7922
|
|
65
65
|
supervisely/api/pointcloud/pointcloud_episode_object_api.py,sha256=k2_wV0EVPo9vxSTVe1qOvqVOMSVE6zGDSkfR6TRNsKs,691
|
|
@@ -623,7 +623,7 @@ supervisely/convert/image/yolo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
|
|
|
623
623
|
supervisely/convert/image/yolo/yolo_converter.py,sha256=Wn5dR05y4SEPONcaxWr9ofnbvbf-SbRZN0fkksk5Dps,11391
|
|
624
624
|
supervisely/convert/image/yolo/yolo_helper.py,sha256=5b0ShsVlqikA071VT8AiRW_079_WD6pdB5Bx3OU12Bw,25989
|
|
625
625
|
supervisely/convert/pointcloud/__init__.py,sha256=WPeIpPoTWDIKAa0lF6t2SMUhFNZ0l-vKujf6yD6w7SA,589
|
|
626
|
-
supervisely/convert/pointcloud/pointcloud_converter.py,sha256=
|
|
626
|
+
supervisely/convert/pointcloud/pointcloud_converter.py,sha256=CeqPn62viKS35MFlQBTPQuRqH_8P9JGYjz-rY-eXtbI,11990
|
|
627
627
|
supervisely/convert/pointcloud/bag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
628
628
|
supervisely/convert/pointcloud/bag/bag_converter.py,sha256=WWd6A2hS7H4MRgtLdJ-yYgmNU-Wk2eycl6LTTJM2GKQ,11391
|
|
629
629
|
supervisely/convert/pointcloud/bag/bag_helper.py,sha256=2TFe49isZTxMhya-PApqLPxrvGnvRFMBc_--BwyCpWU,4284
|
|
@@ -642,10 +642,10 @@ supervisely/convert/pointcloud/ply/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
|
|
|
642
642
|
supervisely/convert/pointcloud/ply/ply_converter.py,sha256=2ZCYkhJQzUev-sWGsBwCPtj1TGjdcx8o-Q--RAHavp8,2698
|
|
643
643
|
supervisely/convert/pointcloud/ply/ply_helper.py,sha256=YfLiV9m6a4NNEMs0J32dmMTLffMLX4-JPTThMHOEK4w,268
|
|
644
644
|
supervisely/convert/pointcloud/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
645
|
-
supervisely/convert/pointcloud/sly/sly_pointcloud_converter.py,sha256=
|
|
645
|
+
supervisely/convert/pointcloud/sly/sly_pointcloud_converter.py,sha256=lS1VTN1ICO8zPcx-435OUiH4fgb8LKiybI5fBnxXRDU,5323
|
|
646
646
|
supervisely/convert/pointcloud/sly/sly_pointcloud_helper.py,sha256=kOluL97FfCFfIvnUE_FeN8iQLMlwdiMR5gayorOGDXw,3968
|
|
647
647
|
supervisely/convert/pointcloud_episodes/__init__.py,sha256=LePLQFEjXwhXap2zOY9SVTbW_NMbxKYZKBjBdRLimKE,557
|
|
648
|
-
supervisely/convert/pointcloud_episodes/pointcloud_episodes_converter.py,sha256=
|
|
648
|
+
supervisely/convert/pointcloud_episodes/pointcloud_episodes_converter.py,sha256=Q3FUZ_t5GkW7u-FNBqwdWvHb1txlmOx-5wYTKTAj5WM,14427
|
|
649
649
|
supervisely/convert/pointcloud_episodes/bag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
650
650
|
supervisely/convert/pointcloud_episodes/bag/bag_converter.py,sha256=jzWKXoFUWu11d5WlPfT1hphCubYpq_lhQZmhh07xZdQ,1659
|
|
651
651
|
supervisely/convert/pointcloud_episodes/kitti_360/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -657,7 +657,7 @@ supervisely/convert/pointcloud_episodes/nuscenes_conv/__init__.py,sha256=47DEQpj
|
|
|
657
657
|
supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py,sha256=O8QIwqwb0DUuYmS8oq6kGv3uTlzS3GyGvAxfL1bYW-s,12764
|
|
658
658
|
supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py,sha256=cJTwhFn1JgblbPjrTrZu30y6FxyjGF-12sMFfvN1xzM,8969
|
|
659
659
|
supervisely/convert/pointcloud_episodes/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
660
|
-
supervisely/convert/pointcloud_episodes/sly/sly_pointcloud_episodes_converter.py,sha256=
|
|
660
|
+
supervisely/convert/pointcloud_episodes/sly/sly_pointcloud_episodes_converter.py,sha256=N6daDLj06urxpe-WU40Y_ATOLbtlmc6OHIZ04QpH0lM,6412
|
|
661
661
|
supervisely/convert/pointcloud_episodes/sly/sly_pointcloud_episodes_helper.py,sha256=h4WvNH6cEHtjxxhCnU7Hs2vkyJMye0qwabqXNYVTywE,3570
|
|
662
662
|
supervisely/convert/video/__init__.py,sha256=8T99u_2rurKksx24aNQZf8b_TPFEiGViSDPzCqjDBfU,157
|
|
663
663
|
supervisely/convert/video/video_converter.py,sha256=f-b6FexBjXw9xWv5w8lxlNxCh4FvacNolX-WQDibWFs,11338
|
|
@@ -1038,8 +1038,8 @@ supervisely/pointcloud_episodes/pointcloud_episodes.py,sha256=trjCTqJEfHMnsk_HmY
|
|
|
1038
1038
|
supervisely/project/__init__.py,sha256=hlzdj9Pgy53Q3qdP8LMtGTChvZHQuuShdtui2eRUQeE,2601
|
|
1039
1039
|
supervisely/project/data_version.py,sha256=P5Lui6i64pYeJWmAdGJDv8GRXxjfpSSZ8zT_MxIrynE,19553
|
|
1040
1040
|
supervisely/project/download.py,sha256=yCFpRum_q8fbY_z2mcRAhYAcYFcDc215ldioO3Gzg3Q,28680
|
|
1041
|
-
supervisely/project/pointcloud_episode_project.py,sha256=
|
|
1042
|
-
supervisely/project/pointcloud_project.py,sha256=
|
|
1041
|
+
supervisely/project/pointcloud_episode_project.py,sha256=ahwL79Ojf5gLdvAph5SLKXr66jmVWzgiOXLsk2P8eVQ,48433
|
|
1042
|
+
supervisely/project/pointcloud_project.py,sha256=NcrsMf673cITLoErJhcdQN85w3k9TBqolEbWR4qjPZQ,56872
|
|
1043
1043
|
supervisely/project/project.py,sha256=bK_TWDLHmTSoxFEUqzSk-FJzddpb0BqsQfQzzwaZu-Y,240401
|
|
1044
1044
|
supervisely/project/project_meta.py,sha256=UTQPstRmRJvbtCcQ1noCtzcw3Sd4llwRMHes-Sz-JQg,51429
|
|
1045
1045
|
supervisely/project/project_settings.py,sha256=NLThzU_DCynOK6hkHhVdFyezwprn9UqlnrLDe_3qhkY,9347
|
|
@@ -1103,9 +1103,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
1103
1103
|
supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
|
|
1104
1104
|
supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
|
|
1105
1105
|
supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
|
|
1106
|
-
supervisely-6.73.
|
|
1107
|
-
supervisely-6.73.
|
|
1108
|
-
supervisely-6.73.
|
|
1109
|
-
supervisely-6.73.
|
|
1110
|
-
supervisely-6.73.
|
|
1111
|
-
supervisely-6.73.
|
|
1106
|
+
supervisely-6.73.380.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
1107
|
+
supervisely-6.73.380.dist-info/METADATA,sha256=CzUiN8OFu7oIhsIVfHs1IE7oEX-DXrxALa7o1LkYjss,35154
|
|
1108
|
+
supervisely-6.73.380.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
1109
|
+
supervisely-6.73.380.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
|
|
1110
|
+
supervisely-6.73.380.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
|
|
1111
|
+
supervisely-6.73.380.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|