supervisely 6.73.378__py3-none-any.whl → 6.73.379__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 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.12.44"
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
- for pcd_id, ann in zip(pcd_ids, anns):
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, (img_path, rimg_ann_path) in enumerate(item._related_images):
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
- img = api.pointcloud.upload_related_image(img_path)
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: img,
139
+ ApiField.HASH: img_hash,
125
140
  ApiField.META: meta_json[ApiField.META],
126
141
  }
127
142
  )
128
- api.pointcloud.add_related_images(rimg_infos, camera_names)
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
- item.set_related_images((rimg, rimg_ann))
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
- item.set_related_images((rimg, rimg_ann))
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: dict) -> None:
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, rimg_ann_paths = list(zip(*item._related_images))
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
- for img_ind, (img_hash, rimg_ann_path) in enumerate(
135
- zip(rimg_hashes, rimg_ann_paths)
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(rimg_ann_path)
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
- api.pointcloud.add_related_images(rimg_infos, camera_names)
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 upload related image or add it to pointcloud episo: {repr(e)}"
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(dataset_id, ann, frame_to_pointcloud_ids)
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 ext == ".json":
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
- item.set_related_images((rimg_path, rimg_ann_path))
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
- pointcloud_file_path = pointcloud_file_path if download_items else None
1070
- pointcloud_info = pointcloud_info._asdict() if download_pointclouds_info else None
1071
- try:
1072
- pointcloud_ann = PointcloudAnnotation.from_json(
1073
- ann_json, project_fs.meta, key_id_map
1074
- )
1075
- except Exception as e:
1076
- logger.info(
1077
- "INFO FOR DEBUGGING",
1078
- extra={
1079
- "project_id": project_id,
1080
- "dataset_id": dataset.id,
1081
- "pointcloud_id": pointcloud_id,
1082
- "pointcloud_name": pointcloud_name,
1083
- "ann_json": ann_json,
1084
- },
1085
- )
1086
- raise e
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.378
3
+ Version: 6.73.379
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely
@@ -1,5 +1,5 @@
1
1
  supervisely/README.md,sha256=XM-DiMC6To3I9RjQZ0c61905EFRR_jnCUx2q3uNR-X8,3331
2
- supervisely/__init__.py,sha256=k4_gIOqBDDy9Acg9irVI6FwXHdphDvCJbgF3lmq23hE,10935
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=Gii6INYqo5f3EUCkI14VMi2XuaxbRHEaqSb_HHmJJTA,53497
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=yCCpzm7GrvL6WT4lNesvtYWWwdO3DO32JIOBBSSQgSA,7130
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=r56Rwil-55cRnd0sIePFGrf_xXa-lKQSfwhEUrjOquk,5070
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=qULUzO96BvWgNVmyxSQ0pUPBPG3WHgUJuK_U7Z8NM-g,9428
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=fSEGxuTtFTAOLNBAZncOxw9PVALBOtB7yZ8qTCaET7w,6102
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=CTG1bJXJFUA2O7yUWZg8SBjYv7ZyqhsV5flHtXdjkrA,42403
1042
- supervisely/project/pointcloud_project.py,sha256=zRM5S7GpbEN9SkvoydRv9rdc8sFaCPE0g1Mz44iDnV0,49923
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.378.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1107
- supervisely-6.73.378.dist-info/METADATA,sha256=bp_zmJ4eMthGiUG1RfSVjuMI0DZU-2TLMPXJ4y_dwQE,35154
1108
- supervisely-6.73.378.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1109
- supervisely-6.73.378.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1110
- supervisely-6.73.378.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1111
- supervisely-6.73.378.dist-info/RECORD,,
1106
+ supervisely-6.73.379.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1107
+ supervisely-6.73.379.dist-info/METADATA,sha256=wF5oRIPeyHyS_2ml8LGidTODKTYoLNJg8HRsvEvTltw,35154
1108
+ supervisely-6.73.379.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1109
+ supervisely-6.73.379.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1110
+ supervisely-6.73.379.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1111
+ supervisely-6.73.379.dist-info/RECORD,,