supervisely 6.73.397__py3-none-any.whl → 6.73.399__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.
@@ -1,7 +1,15 @@
1
1
  import os
2
+ from collections import defaultdict
2
3
  from typing import Dict, Optional, Set, Union
3
4
 
4
- from supervisely import Annotation, ProjectMeta, logger
5
+ from supervisely import (
6
+ Annotation,
7
+ ProjectMeta,
8
+ TagApplicableTo,
9
+ TagMeta,
10
+ TagValueType,
11
+ logger,
12
+ )
5
13
  from supervisely.convert.base_converter import AvailableImageConverters
6
14
  from supervisely.convert.image.image_converter import ImageConverter
7
15
  from supervisely.convert.image.pascal_voc import pascal_voc_helper
@@ -139,6 +147,7 @@ class PascalVOCConverter(ImageConverter):
139
147
 
140
148
  def _create_items(self, possible_pascal_voc_dir: str) -> int:
141
149
  existing_cls_names = set([cls.name for cls in self._meta.obj_classes])
150
+ tags_to_values = defaultdict(set)
142
151
  detected_ann_cnt = 0
143
152
 
144
153
  images_list = list_files_recursively(self._imgs_dir, valid_extensions=self.allowed_exts)
@@ -153,13 +162,42 @@ class PascalVOCConverter(ImageConverter):
153
162
  item_name_noext = get_file_name(item.name)
154
163
  item = self._scan_for_item_segm_paths(item, item_name_noext)
155
164
  ann_path = img_ann_map.get(item_name_noext) or img_ann_map.get(item.name)
156
- item = self._scan_for_item_ann_path_and_update_meta(item, ann_path, existing_cls_names)
165
+ item = self._scan_for_item_ann_path_and_update_meta(
166
+ item, ann_path, existing_cls_names, tags_to_values
167
+ )
157
168
 
158
169
  if item.ann_data or item.segm_path:
159
170
  detected_ann_cnt += 1
160
171
  self._items.append(item)
172
+ self._meta = self._update_meta_with_tags(tags_to_values)
161
173
  return detected_ann_cnt
162
174
 
175
+ def _update_meta_with_tags(self, tags_to_values: Dict[str, Set[str]]) -> ProjectMeta:
176
+ meta = self._meta
177
+ object_class_names = set(meta.obj_classes.keys())
178
+ for tag_name, values in tags_to_values.items():
179
+ tag_meta = meta.get_tag_meta(tag_name)
180
+ if tag_meta is not None:
181
+ continue
182
+ if tag_name in pascal_voc_helper.DEFAULT_SUBCLASSES:
183
+ if values.difference({"0", "1"}):
184
+ logger.warning(
185
+ f"Tag '{tag_name}' has non-binary values.", extra={"values": values}
186
+ )
187
+ tag_meta = TagMeta(tag_name, TagValueType.NONE)
188
+ elif tag_name in object_class_names:
189
+ tag_meta = TagMeta(
190
+ tag_name,
191
+ TagValueType.ONEOF_STRING,
192
+ possible_values=list(values),
193
+ applicable_to=TagApplicableTo.OBJECTS_ONLY,
194
+ applicable_classes=[tag_name],
195
+ )
196
+ else:
197
+ tag_meta = TagMeta(tag_name, TagValueType.ANY_STRING)
198
+ meta = meta.add_tag_meta(tag_meta)
199
+ return meta
200
+
163
201
  def _scan_for_item_segm_paths(self, item: Item, item_name_noext: str) -> Item:
164
202
  if self._segm_dir is not None:
165
203
  segm_path = os.path.join(self._segm_dir, f"{item_name_noext}.png")
@@ -173,14 +211,18 @@ class PascalVOCConverter(ImageConverter):
173
211
  return item
174
212
 
175
213
  def _scan_for_item_ann_path_and_update_meta(
176
- self, item: Item, ann_path: Optional[str], existing_cls_names: Set[str]
214
+ self,
215
+ item: Item,
216
+ ann_path: Optional[str],
217
+ existing_cls_names: Set[str],
218
+ tags_to_values: Dict[str, Set[str]],
177
219
  ) -> Item:
178
220
  if ann_path is None:
179
221
  return item
180
222
  if not file_exists(ann_path):
181
223
  return item
182
224
  self._meta = pascal_voc_helper.update_meta_from_xml(
183
- ann_path, self._meta, existing_cls_names, self._bbox_classes_map
225
+ ann_path, self._meta, existing_cls_names, self._bbox_classes_map, tags_to_values
184
226
  )
185
227
  item.ann_data = ann_path
186
228
  return item
@@ -13,7 +13,7 @@ from supervisely.annotation.label import Label
13
13
  from supervisely.annotation.obj_class import ObjClass
14
14
  from supervisely.annotation.obj_class_collection import ObjClassCollection
15
15
  from supervisely.annotation.tag import Tag, TagValueType
16
- from supervisely.annotation.tag_meta import TagMeta
16
+ from supervisely.annotation.tag_meta import TagApplicableTo, TagMeta
17
17
  from supervisely.convert.image.image_helper import validate_image_bounds
18
18
  from supervisely.geometry.bitmap import Bitmap
19
19
  from supervisely.geometry.polygon import Polygon
@@ -36,6 +36,8 @@ TRAIN_TAG_NAME = "train"
36
36
  VAL_TAG_NAME = "val"
37
37
  TRAINVAL_TAG_NAME = "trainval"
38
38
  DEFAULT_OBJECT_FIELDS = {"name", "class", "bndbox"}
39
+ DEFAULT_SUBCLASSES = {"pose", "truncated", "difficult", "occluded", "obstacle", "out-of-scope"}
40
+
39
41
 
40
42
  default_classes_colors = {
41
43
  "neutral": (224, 224, 192),
@@ -179,7 +181,7 @@ def get_ann(
179
181
  ann = ann.add_labels(labels)
180
182
 
181
183
  if np.sum(colored_img) > 0:
182
- logger.warn(
184
+ logger.warning(
183
185
  f"Not all objects or classes are captured from source segmentation: {item.name}"
184
186
  )
185
187
 
@@ -214,7 +216,7 @@ def xml_to_sly_labels(
214
216
  cls_name = renamed_classes[cls_name]
215
217
  obj_cls = meta.obj_classes.get(cls_name)
216
218
  if obj_cls is None:
217
- logger.warn(f"Class {cls_name} is not found in meta. Skipping.")
219
+ logger.warning(f"Class {cls_name} is not found in meta. Skipping.")
218
220
  continue
219
221
  elif field_name == "bndbox":
220
222
  bbox_coords = [
@@ -227,11 +229,25 @@ def xml_to_sly_labels(
227
229
  tag_name = renamed_tags[tag_name]
228
230
  tag_meta = meta.get_tag_meta(tag_name)
229
231
  if tag_meta is None:
230
- logger.warn(f"Tag meta for '{field_name}' is not found in meta. Skipping.")
232
+ logger.warning(f"Tag meta for '{field_name}' is not found in meta. Skipping.")
231
233
  continue
232
- if not isinstance(value, str):
233
- value = str(value)
234
- tags.append(Tag(tag_meta, value))
234
+ if tag_meta.value_type == TagValueType.ANY_STRING:
235
+ if not isinstance(value, str):
236
+ value = str(value)
237
+ tags.append(Tag(tag_meta, value))
238
+ elif tag_meta.value_type == TagValueType.NONE:
239
+ if int(value) == 1:
240
+ tags.append(Tag(tag_meta))
241
+ else:
242
+ logger.debug("Tag with value '0' not added to labels.")
243
+ elif tag_meta.value_type == TagValueType.ONEOF_STRING:
244
+ if value not in tag_meta.possible_values:
245
+ logger.warning(
246
+ f"Value '{value}' for tag '{tag_name}' is not in possible values: {tag_meta.possible_values}. Skipping."
247
+ )
248
+ continue
249
+ tags.append(Tag(tag_meta, value))
250
+
235
251
  if geometry is None or obj_cls is None:
236
252
  continue
237
253
  labels.append(Label(geometry, obj_cls, tags))
@@ -245,6 +261,7 @@ def update_meta_from_xml(
245
261
  meta: ProjectMeta,
246
262
  existing_cls_names: set,
247
263
  bbox_classes_map: dict,
264
+ tags_to_values: Dict[str, set],
248
265
  ) -> ProjectMeta:
249
266
  import xml.etree.ElementTree as ET
250
267
 
@@ -282,10 +299,8 @@ def update_meta_from_xml(
282
299
  meta = meta.add_obj_class(obj_cls)
283
300
  bbox_classes_map[original_class_name] = class_name
284
301
  elif field_name not in DEFAULT_OBJECT_FIELDS:
285
- tag_meta = meta.get_tag_meta(field_name)
286
- if tag_meta is None:
287
- tag_meta = TagMeta(field_name, TagValueType.ANY_STRING)
288
- meta = meta.add_tag_meta(tag_meta)
302
+ value = element.text
303
+ tags_to_values[field_name].add(value)
289
304
 
290
305
  return meta
291
306
 
@@ -246,6 +246,7 @@ class PointcloudConverter(BaseConverter):
246
246
  ) or dir_name.endswith("_pcd"):
247
247
  rimg_ann_dict[file] = full_path
248
248
  elif imghdr.what(full_path):
249
+ dir_name = os.path.basename(root)
249
250
  if dir_name not in rimg_dict:
250
251
  rimg_dict[dir_name] = []
251
252
  rimg_dict[dir_name].append(full_path)
@@ -164,7 +164,7 @@ class NiiPlaneStructuredConverter(NiiConverter, VolumeConverter):
164
164
  item.custom_data["scores"] = scores
165
165
  except Exception as e:
166
166
  logger.warning(f"Failed to read scores from {scores_paths[0]}: {e}")
167
- item.is_semantic = len(possible_ann_paths) == 1
167
+ item.is_semantic = len(item.ann_data) == 1
168
168
  if cls_color_map is not None:
169
169
  item.custom_data["cls_color_map"] = cls_color_map
170
170
  self._items.append(item)
@@ -226,6 +226,7 @@ class NiiPlaneStructuredAnnotationConverter(NiiConverter, VolumeConverter):
226
226
  def __init__(self, *args, **kwargs):
227
227
  super().__init__(*args, **kwargs)
228
228
  self._is_semantic = False
229
+ self._is_scores = False
229
230
  self.volume_meta = None
230
231
 
231
232
  @property
@@ -236,6 +237,14 @@ class NiiPlaneStructuredAnnotationConverter(NiiConverter, VolumeConverter):
236
237
  def is_semantic(self, value: bool):
237
238
  self._is_semantic = value
238
239
 
240
+ @property
241
+ def is_scores(self) -> bool:
242
+ return self._is_scores
243
+
244
+ @is_scores.setter
245
+ def is_scores(self, value: bool):
246
+ self._is_scores = value
247
+
239
248
  def create_empty_annotation(self):
240
249
  return VolumeAnnotation(self.volume_meta)
241
250
 
@@ -274,17 +283,22 @@ class NiiPlaneStructuredAnnotationConverter(NiiConverter, VolumeConverter):
274
283
  for file in files:
275
284
  path = os.path.join(root, file)
276
285
  name_parts = helper.parse_name_parts(file)
277
- if is_nii(file):
278
- if name_parts is None or not name_parts.is_ann:
279
- continue
280
- try:
281
- nii = load(path)
282
- except filebasedimages.ImageFileError:
283
- logger.warning(f"Failed to load NIfTI file: {path}")
284
- continue
286
+ if name_parts is None:
287
+ continue
288
+ if is_nii(file) or name_parts.type == helper.SCORE_NAME:
285
289
  item = self.Item(item_path=None, ann_data=path)
286
- item.set_shape(nii.shape)
287
290
  item.custom_data["name_parts"] = name_parts
291
+ if name_parts.is_ann:
292
+ try:
293
+ nii = load(path)
294
+ except filebasedimages.ImageFileError:
295
+ logger.warning(f"Failed to load NIfTI file: {path}")
296
+ continue
297
+ item.set_shape(nii.shape)
298
+ elif name_parts.type == helper.SCORE_NAME:
299
+ item.is_scores = True
300
+ scores = helper.get_scores_from_table(path, name_parts.plane)
301
+ item.custom_data["scores"] = scores
288
302
  if cls_color_map is not None:
289
303
  item.custom_data["cls_color_map"] = cls_color_map
290
304
  self._items.append(item)
@@ -293,6 +307,13 @@ class NiiPlaneStructuredAnnotationConverter(NiiConverter, VolumeConverter):
293
307
  if cls_color_map is not None:
294
308
  obj_classes = [ObjClass(name, Mask3D, color) for name, color in cls_color_map.values()]
295
309
 
310
+ for item in self._items:
311
+ name_parts = item.custom_data.get("name_parts")
312
+ if item.is_scores:
313
+ continue
314
+ if name_parts.ending_idx is None:
315
+ item.is_semantic = True
316
+
296
317
  self._meta = ProjectMeta(obj_classes=obj_classes)
297
318
  return len(self._items) > 0
298
319
 
@@ -369,20 +390,65 @@ class NiiPlaneStructuredAnnotationConverter(NiiConverter, VolumeConverter):
369
390
  else:
370
391
  progress_cb = None
371
392
 
372
- for item, volume in matched_dict.items():
393
+ volumeids_to_objects = defaultdict(list)
394
+
395
+ for item, volume in sorted(matched_dict.items(), key=lambda pair: pair[0].is_scores):
373
396
  item.volume_meta = volume.meta
374
- ann = self.to_supervisely(item, meta, renamed_classes, None)
375
- if self._meta_changed:
376
- meta, renamed_classes, _ = self.merge_metas_with_conflicts(api, dataset_id)
377
- self._meta_changed = False
378
- api.volume.annotation.append(volume.id, ann, volume_info=volume)
397
+ if not item.is_scores:
398
+ ann = self.to_supervisely(item, meta, renamed_classes, None)
399
+ if self._meta_changed:
400
+ meta, renamed_classes, _ = self.merge_metas_with_conflicts(api, dataset_id)
401
+ self._meta_changed = False
402
+ api.volume.annotation.append(volume.id, ann, volume_info=volume)
403
+ else:
404
+ class_id_to_pixel_value = helper.get_class_id_to_pixel_value_map(meta)
405
+ scores = item.custom_data.get("scores", {})
406
+ if not scores:
407
+ logger.warning(f"No scores found for {item.ann_data}. Skipping.")
408
+ continue
409
+
410
+ if volume.dataset_id not in volumeids_to_objects:
411
+ for obj in api.volume.object.get_list(volume.dataset_id):
412
+ volumeids_to_objects[obj.entity_id].append(obj)
413
+
414
+ obj_id_to_class_id = {
415
+ obj.id: obj.class_id for obj in volumeids_to_objects[volume.id]
416
+ }
417
+ if not obj_id_to_class_id:
418
+ logger.warning(
419
+ f"No objects found for volume {volume.id}. Skipping figure updates."
420
+ )
421
+ continue
422
+
423
+ volume_figure_dict = api.volume.figure.download(
424
+ volume.dataset_id, [volume.id], skip_geometry=True
425
+ )
426
+ figures_list = volume_figure_dict.get(volume.id, [])
427
+ for figure in figures_list:
428
+ class_id = obj_id_to_class_id.get(figure.object_id, None)
429
+ if class_id is None:
430
+ logger.warning(
431
+ f"Class ID for figure (id: {figure.id}) not found in volume objects. Skipping figure update.",
432
+ extra={
433
+ "obj_id_to_class_id": obj_id_to_class_id,
434
+ "object_id": figure.object_id,
435
+ },
436
+ )
437
+ continue
438
+ pixel_value = class_id_to_pixel_value.get(class_id, None)
439
+ if pixel_value is None:
440
+ logger.warning(
441
+ f"Pixel value for class ID {class_id} not found in meta. Skipping figure update."
442
+ )
443
+ continue
444
+ figure_custom_data = scores.get(pixel_value, {})
445
+ if figure_custom_data:
446
+ api.volume.figure.update_custom_data(figure.id, figure_custom_data)
447
+ logger.debug(
448
+ f"Updated figure {figure.id} with custom data: {figure_custom_data}"
449
+ )
379
450
  progress_cb(1) if log_progress else None
380
451
 
381
- res_ds_info = api.dataset.get_info_by_id(dataset_id)
382
- if res_ds_info.items_count == 0:
383
- logger.info("Resulting dataset is empty. Removing it.")
384
- api.dataset.remove(dataset_id)
385
-
386
452
  if log_progress:
387
453
  if is_development():
388
454
  progress.close()
@@ -10,10 +10,12 @@ from supervisely import Api
10
10
  from supervisely.collection.str_enum import StrEnum
11
11
  from supervisely.geometry.mask_3d import Mask3D
12
12
  from supervisely.io.fs import ensure_base_path, get_file_ext, get_file_name
13
+ from supervisely.project.project_meta import ProjectMeta
13
14
  from supervisely.sly_logger import logger
14
15
  from supervisely.volume.volume import convert_3d_nifti_to_nrrd
15
16
 
16
17
  VOLUME_NAME = "anatomic"
18
+ SCORE_NAME = "score"
17
19
  LABEL_NAME = ["inference", "label", "annotation", "mask", "segmentation"]
18
20
  MASK_PIXEL_VALUE = "Mask pixel value: "
19
21
 
@@ -172,20 +174,62 @@ def get_scores_from_table(csv_file_path: str, plane: str) -> dict:
172
174
  return result
173
175
 
174
176
 
177
+ def _find_pixel_values(descr: str) -> int:
178
+ """
179
+ Find the pixel value in the description string.
180
+ """
181
+ lines = descr.split("\n")
182
+ for line in lines:
183
+ if line.strip().startswith(MASK_PIXEL_VALUE):
184
+ try:
185
+ value_part = line.strip().split(MASK_PIXEL_VALUE)[1]
186
+ return int(value_part.strip())
187
+ except (IndexError, ValueError):
188
+ continue
189
+ return None
190
+
191
+
192
+ def get_class_id_to_pixel_value_map(meta: ProjectMeta) -> dict:
193
+ class_id_to_pixel_value = {}
194
+ for obj_class in meta.obj_classes.items():
195
+ pixel_value = _find_pixel_values(obj_class.description)
196
+ if pixel_value is not None:
197
+ class_id_to_pixel_value[obj_class.sly_id] = pixel_value
198
+ elif "Segment_" in obj_class.name:
199
+ try:
200
+ pixel_value = int(obj_class.name.split("_")[-1])
201
+ class_id_to_pixel_value[obj_class.sly_id] = pixel_value
202
+ except (ValueError, IndexError):
203
+ logger.warning(
204
+ f"Failed to parse pixel value from class name: {obj_class.name}. "
205
+ "Please ensure the class name ends with a valid integer."
206
+ )
207
+ else:
208
+ logger.warning(
209
+ f"Class {obj_class.name} does not have a pixel value defined in its description. "
210
+ "Please update the class description to include 'Mask pixel value: <value>'."
211
+ )
212
+ return class_id_to_pixel_value
213
+
214
+
175
215
  class AnnotationMatcher:
176
216
  def __init__(self, items, dataset_id):
177
- self._items = items
178
- self._ds_id = dataset_id
179
217
  self._ann_paths = defaultdict(list)
180
218
  self._item_by_filename = {}
181
219
  self._item_by_path = {}
182
220
 
183
- self.set_items(items)
221
+ self.items = items
222
+ self._ds_id = dataset_id
184
223
 
185
224
  self._project_wide = False
186
225
  self._volumes = None
187
226
 
188
- def set_items(self, items):
227
+ @property
228
+ def items(self):
229
+ return self._items
230
+
231
+ @items.setter
232
+ def items(self, items):
189
233
  self._items = items
190
234
  self._ann_paths.clear()
191
235
  self._item_by_filename.clear()
@@ -202,13 +246,13 @@ class AnnotationMatcher:
202
246
 
203
247
  def get_volumes(self, api: Api):
204
248
  dataset_info = api.dataset.get_info_by_id(self._ds_id)
205
- datasets = {dataset_info.name: dataset_info}
206
- project_id = dataset_info.project_id
207
- if dataset_info.items_count > 0 and len(self._ann_paths.keys()) == 1:
249
+ if dataset_info.items_count > 0:
250
+ datasets = {dataset_info.name: dataset_info}
208
251
  self._project_wide = False
209
252
  else:
210
253
  datasets = {
211
- dsinfo.name: dsinfo for dsinfo in api.dataset.get_list(project_id, recursive=True)
254
+ dsinfo.name: dsinfo
255
+ for dsinfo in api.dataset.get_list(dataset_info.project_id, recursive=True)
212
256
  }
213
257
  self._project_wide = True
214
258
 
@@ -270,6 +314,8 @@ class AnnotationMatcher:
270
314
  # items_to_remove = []
271
315
  for item, volume in item_to_volume.items():
272
316
  volume_shape = tuple(volume.file_meta["sizes"])
317
+ if item.shape is None:
318
+ continue
273
319
  if item.shape != volume_shape:
274
320
  logger.warning(f"Volume shape mismatch: {item.shape} != {volume_shape}")
275
321
  # items_to_remove.append(item)
@@ -363,27 +409,26 @@ def parse_name_parts(full_name: str) -> NameParts:
363
409
  type = None
364
410
  is_ann = False
365
411
  if VOLUME_NAME in full_name:
366
- type = "anatomic"
367
- elif any(part in full_name for part in LABEL_NAME):
412
+ type = VOLUME_NAME
413
+ elif SCORE_NAME in full_name and full_name.endswith(".csv"):
414
+ type = SCORE_NAME
415
+ else:
368
416
  type = next((part for part in LABEL_NAME if part in full_name), None)
369
417
  is_ann = type is not None
370
- elif "score" in name_no_ext or get_file_ext(full_name) == ".csv":
371
- type = "score"
372
418
 
373
419
  if type is None:
374
420
  return
375
421
 
376
422
  plane = None
423
+ tokens = name_no_ext.lower().split("_")
377
424
  for part in PlanePrefix.values():
378
- if part in name:
425
+ if part in tokens:
379
426
  plane = part
380
427
  break
381
428
 
382
429
  if plane is None:
383
430
  return
384
431
 
385
- is_ann = any(part in name.lower() for part in LABEL_NAME)
386
-
387
432
  patient_uuid = None
388
433
  case_uuid = None
389
434
 
@@ -9,7 +9,7 @@ from typing import Callable, Dict, List, Optional, Union
9
9
  from requests.exceptions import HTTPError, RetryError
10
10
  from rich.console import Console
11
11
 
12
- from supervisely import is_development, is_community
12
+ from supervisely import is_community, is_development
13
13
  from supervisely.app import DialogWindowError
14
14
  from supervisely.sly_logger import EventType, logger
15
15
 
@@ -250,7 +250,9 @@ class ErrorHandler:
250
250
  def __init__(self, exception: Exception, stack: List[traceback.FrameSummary] = None):
251
251
  self.code = 2004
252
252
  self.title = "Image files size limit exceeded"
253
- self.message = "The given image file size is too large (more than 1 GB) for Community Edition."
253
+ self.message = (
254
+ "The given image file size is too large (more than 1 GB) for Community Edition."
255
+ )
254
256
 
255
257
  super().__init__(
256
258
  exception,
@@ -290,12 +292,14 @@ class ErrorHandler:
290
292
 
291
293
  class OutOfMemory(HandleException):
292
294
  def __init__(self, exception: Exception, stack: List[traceback.FrameSummary] = None):
293
- device_prefix = "GPU memory" if any([s in exception.args[0] for s in ["CUDA", "cuda", "GPU"]]) else "memory"
295
+ device_prefix = (
296
+ "GPU memory"
297
+ if any([s in exception.args[0] for s in ["CUDA", "cuda", "GPU"]])
298
+ else "memory"
299
+ )
294
300
  self.code = 2007
295
301
  self.title = f"Out of {device_prefix} on the computer where the agent is deployed"
296
- self.message = (
297
- "Please, check your agent's memory usage, reduce batch size or use a device with more memory capacity."
298
- )
302
+ self.message = "Please, check your agent's memory usage, reduce batch size or use a device with more memory capacity."
299
303
 
300
304
  super().__init__(
301
305
  exception,
@@ -471,7 +475,9 @@ class ErrorHandler:
471
475
  def __init__(self, exception: Exception, stack: List[traceback.FrameSummary] = None):
472
476
  self.code = 2019
473
477
  self.title = "Upload remote images is only available for PRO teams"
474
- self.message = "Please, upgrade plan to upload remote images: https://supervisely.com/pricing/"
478
+ self.message = (
479
+ "Please, upgrade plan to upload remote images: https://supervisely.com/pricing/"
480
+ )
475
481
 
476
482
  super().__init__(
477
483
  exception,
@@ -485,7 +491,9 @@ class ErrorHandler:
485
491
  def __init__(self, exception: Exception, stack: List[traceback.FrameSummary] = None):
486
492
  self.code = 2020
487
493
  self.title = "Upload remote videos is only available for PRO teams"
488
- self.message = "Please, upgrade plan to upload remote videos: https://supervisely.com/pricing/"
494
+ self.message = (
495
+ "Please, upgrade plan to upload remote videos: https://supervisely.com/pricing/"
496
+ )
489
497
 
490
498
  super().__init__(
491
499
  exception,
@@ -681,6 +689,7 @@ ERROR_PATTERNS = {
681
689
  r".*images\.bulk\.upload.*FileSize.*\"sizeLimit\":1073741824.*": ErrorHandler.API.ImageFilesSizeTooLarge,
682
690
  r".*videos\.bulk\.upload.*FileSize.*sizeLimit\":314572800.*": ErrorHandler.API.VideoFilesSizeTooLarge,
683
691
  r".*images\.bulk\.upload.*FileSize.*\"sizeLimit\":157286400.*": ErrorHandler.API.VolumeFilesSizeTooLarge,
692
+ r".*images\.bulk\.upload.*FileSize.*\"sizeLimit\":\"25mb\".*": ErrorHandler.API.VolumeFilesSizeTooLarge,
684
693
  r".*Dataset with datasetId.*is either archived, doesn't exist or you don't have enough permissions to access.*": ErrorHandler.API.DatasetNotFound,
685
694
  r".*Project with projectId.*is either archived, doesn't exist or you don't have enough permissions to access.*": ErrorHandler.API.ProjectNotFound,
686
695
  r".*api\.task\.set_field.*": ErrorHandler.API.AppSetFieldError,
@@ -477,8 +477,9 @@ def download_volume_project(
477
477
  mesh_ids.append(figure_id)
478
478
  figure_path = dataset_fs.get_interpolation_path(volume_name, sf)
479
479
  mesh_paths.append(figure_path)
480
-
481
- figs = api.volume.figure.download(dataset.id, [volume_id], skip_geometry=True)[volume_id]
480
+
481
+ figs = api.volume.figure.download(dataset.id, [volume_id], skip_geometry=True)
482
+ figs = figs.get(volume_id, {})
482
483
  figs_ids_map = {fig.id: fig for fig in figs}
483
484
  for ann_fig in ann.figures + ann.spatial_figures:
484
485
  fig = figs_ids_map.get(ann_fig.geometry.sly_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.397
3
+ Version: 6.73.399
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely
@@ -610,8 +610,8 @@ supervisely/convert/image/multi_view/multi_view.py,sha256=V-6oFN6oDre7UhejfyDkGK
610
610
  supervisely/convert/image/multispectral/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
611
611
  supervisely/convert/image/multispectral/multispectral_converter.py,sha256=T3etYVNI0AUUrQsQhxw_r85NthXrqhqmdZQfz8kUY0g,5194
612
612
  supervisely/convert/image/pascal_voc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
613
- supervisely/convert/image/pascal_voc/pascal_voc_converter.py,sha256=zpn_s_WT1Z72WztXlPEJIBfO7-DbHCnjsOZFGOoYMCA,7729
614
- supervisely/convert/image/pascal_voc/pascal_voc_helper.py,sha256=vu6L2Gm1IU5aDPGibngVJvdje0ndS8NW-0WRhmr6vPE,29415
613
+ supervisely/convert/image/pascal_voc/pascal_voc_converter.py,sha256=JwbkAT3W7aNHuxr9fas9PGrfDy4f5beyx6DWItIdVTw,9247
614
+ supervisely/convert/image/pascal_voc/pascal_voc_helper.py,sha256=Ek2KeQjKhSDup8PBE53Pg82x7HHNjDkWNilmz7BHYj4,30199
615
615
  supervisely/convert/image/pdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
616
616
  supervisely/convert/image/pdf/pdf_converter.py,sha256=LKvVng9jPp0cSIjYEjKLOb48wtdOdB7LXS2gjmOdZhE,2442
617
617
  supervisely/convert/image/pdf/pdf_helper.py,sha256=IDwLEvsVy8lu-KC1lXvSRkZZ9BCC6ylebnNEtLQU5L4,1288
@@ -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=X00tYXdIsSwaLHYX5xqqizQy30MjuhmVjhbrhtLnBjU,12078
626
+ supervisely/convert/pointcloud/pointcloud_converter.py,sha256=-FQ-YTXu-ywrJ0yBfg0tBV_t8SB6kKD4MlqkdAZMWEA,12132
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
@@ -674,9 +674,9 @@ supervisely/convert/volume/dicom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
674
674
  supervisely/convert/volume/dicom/dicom_converter.py,sha256=Hw4RxU_qvllk6M26udZE6G-m1RWR8-VVPcEPwFlqrVg,3354
675
675
  supervisely/convert/volume/dicom/dicom_helper.py,sha256=OrKlyt1hA5BOXKhE1LF1WxBIv3b6t96xRras4OSAuNM,2891
676
676
  supervisely/convert/volume/nii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
677
- supervisely/convert/volume/nii/nii_planes_volume_converter.py,sha256=QTdmtqLrRBFSa0IZKhAnFkLl1J3nayzQQDwpglvEN64,16915
677
+ supervisely/convert/volume/nii/nii_planes_volume_converter.py,sha256=XQ5o7mg3OJ9UO-wM14O718TIkVXeXDAz-lJDV--0UE8,20043
678
678
  supervisely/convert/volume/nii/nii_volume_converter.py,sha256=gR1_dfUf0L-K49B8qVHF7DiiDKDmxUQKKSPLdlqoYC4,8691
679
- supervisely/convert/volume/nii/nii_volume_helper.py,sha256=nkfTG2NGmnf4AfrZ0lULSHaUBx1G24NJUO_5FNejolE,16032
679
+ supervisely/convert/volume/nii/nii_volume_helper.py,sha256=oBALqwQDBuUwN9UHQP7PM-NyoH41d2FDickMSkVtksI,17597
680
680
  supervisely/convert/volume/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
681
681
  supervisely/convert/volume/sly/sly_volume_converter.py,sha256=TI1i_aVYFFoqLHqVzCXnFeR6xobhGcgN_xWFZcpRqbE,6730
682
682
  supervisely/convert/volume/sly/sly_volume_helper.py,sha256=gUY0GW3zDMlO2y-zQQG36uoXMrKkKz4-ErM1CDxFCxE,5620
@@ -724,7 +724,7 @@ supervisely/imaging/image.py,sha256=1KNc4qRbP9OlI4Yta07Kc2ohAgSBJ_9alF9Jag74w30,
724
724
  supervisely/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
725
725
  supervisely/io/docker_utils.py,sha256=hb_HXGM8IYB0PF-nD7NxMwaHgzaxIFxofsUzQ_RCUZI,7935
726
726
  supervisely/io/env.py,sha256=l-Xkil6FVxMP8FmnjF4H4p3RJQ3AoBd0OUjZHA2QVTc,21193
727
- supervisely/io/exception_handlers.py,sha256=_nAgMFeE94bCxEvWakR82hMtdOJUyn7Gc7OymMxI9WI,36484
727
+ supervisely/io/exception_handlers.py,sha256=22LPlLgyq59DnrhpaFrbGBYJE7uxO64VTZjsPJC0PLE,36757
728
728
  supervisely/io/fs.py,sha256=pzNAK5fbT3G-7PKRY5oYcOPjgXeZ9x5Dyry7fzzZsr8,63604
729
729
  supervisely/io/fs_cache.py,sha256=985gvBGzveLcDudgz10E4EWVjP9jxdU1Pa0GFfCBoCA,6520
730
730
  supervisely/io/github_utils.py,sha256=jGmvQJ5bjtACuSFABzrxL0jJdh14SezovrHp8T-9y8g,1779
@@ -1047,7 +1047,7 @@ supervisely/project/project_type.py,sha256=7mQ7zg6r7Bm2oFn5aR8n_PeLqMmOaPZd6ph7Z
1047
1047
  supervisely/project/readme_template.md,sha256=SFAfNF_uxSBJJ45A8qZ0MRuHnwSE4Gu_Z7UJqPMgRzg,9254
1048
1048
  supervisely/project/upload.py,sha256=ys95MXFh-rtq-EAsNsiRi3wgbFUCEsY2un3_bd5hJkE,3753
1049
1049
  supervisely/project/video_project.py,sha256=7i8__1zoU2Uryicjfa2_7p3JLnSPTv14ctLJPQGgnPY,66315
1050
- supervisely/project/volume_project.py,sha256=BhFDE6GTxbhuJ-y4Bum-70bjRJ0FiIowkMru7PZ-0mk,23548
1050
+ supervisely/project/volume_project.py,sha256=yHJ2KTPhkqxlSHkafXQqyJqDgl8KHkx2fKkkkgw86qg,23600
1051
1051
  supervisely/pyscripts_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1052
1052
  supervisely/pyscripts_utils/utils.py,sha256=scEwHJvHRQa8NHIOn2eTwH6-Zc8CGdLoxM-WzH9jcRo,314
1053
1053
  supervisely/report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1114,9 +1114,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
1114
1114
  supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
1115
1115
  supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
1116
1116
  supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
1117
- supervisely-6.73.397.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1118
- supervisely-6.73.397.dist-info/METADATA,sha256=IIC5TyRVz6pnTYzbaQVNCOYReuxJd8xM0TySIw_WcCs,35254
1119
- supervisely-6.73.397.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1120
- supervisely-6.73.397.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1121
- supervisely-6.73.397.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1122
- supervisely-6.73.397.dist-info/RECORD,,
1117
+ supervisely-6.73.399.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1118
+ supervisely-6.73.399.dist-info/METADATA,sha256=ZxlcTeH4-lJvPtaN7rpoQXr5o-qVhvyYrcfbFUiqm_M,35254
1119
+ supervisely-6.73.399.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1120
+ supervisely-6.73.399.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1121
+ supervisely-6.73.399.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1122
+ supervisely-6.73.399.dist-info/RECORD,,