supervisely 6.73.325__py3-none-any.whl → 6.73.327__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.
Files changed (28) hide show
  1. supervisely/annotation/annotation.py +1 -1
  2. supervisely/app/widgets/pretrained_models_selector/pretrained_models_selector.py +17 -14
  3. supervisely/app/widgets/pretrained_models_selector/template.html +2 -1
  4. supervisely/convert/image/yolo/yolo_helper.py +95 -25
  5. supervisely/convert/volume/nii/nii_planes_volume_converter.py +54 -6
  6. supervisely/convert/volume/nii/nii_volume_converter.py +7 -7
  7. supervisely/convert/volume/nii/nii_volume_helper.py +49 -0
  8. supervisely/nn/inference/gui/serving_gui_template.py +2 -3
  9. supervisely/nn/inference/inference.py +33 -25
  10. supervisely/nn/training/gui/classes_selector.py +24 -19
  11. supervisely/nn/training/gui/gui.py +90 -37
  12. supervisely/nn/training/gui/hyperparameters_selector.py +32 -15
  13. supervisely/nn/training/gui/input_selector.py +13 -2
  14. supervisely/nn/training/gui/model_selector.py +16 -6
  15. supervisely/nn/training/gui/train_val_splits_selector.py +10 -1
  16. supervisely/nn/training/gui/training_artifacts.py +23 -4
  17. supervisely/nn/training/gui/training_logs.py +15 -3
  18. supervisely/nn/training/gui/training_process.py +14 -13
  19. supervisely/nn/training/train_app.py +59 -24
  20. supervisely/nn/utils.py +9 -0
  21. supervisely/project/project.py +16 -3
  22. supervisely/volume/volume.py +19 -21
  23. {supervisely-6.73.325.dist-info → supervisely-6.73.327.dist-info}/METADATA +1 -1
  24. {supervisely-6.73.325.dist-info → supervisely-6.73.327.dist-info}/RECORD +28 -28
  25. {supervisely-6.73.325.dist-info → supervisely-6.73.327.dist-info}/LICENSE +0 -0
  26. {supervisely-6.73.325.dist-info → supervisely-6.73.327.dist-info}/WHEEL +0 -0
  27. {supervisely-6.73.325.dist-info → supervisely-6.73.327.dist-info}/entry_points.txt +0 -0
  28. {supervisely-6.73.325.dist-info → supervisely-6.73.327.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,16 @@ class TrainValSplitsSelector:
10
10
  lock_message = "Select input options to unlock"
11
11
 
12
12
  def __init__(self, api: Api, project_id: int, app_options: dict = {}):
13
+ # Init widgets
14
+ self.train_val_splits = None
15
+ self.validator_text = None
16
+ self.button = None
17
+ self.container = None
18
+ self.card = None
19
+ # -------------------------------- #
20
+
13
21
  self.display_widgets = []
22
+ self.app_options = app_options
14
23
  self.api = api
15
24
  self.project_id = project_id
16
25
 
@@ -50,7 +59,7 @@ class TrainValSplitsSelector:
50
59
  description=self.description,
51
60
  content=self.container,
52
61
  lock_message=self.lock_message,
53
- collapsable=app_options.get("collapsable", False),
62
+ collapsable=self.app_options.get("collapsable", False),
54
63
  )
55
64
  self.card.lock()
56
65
 
@@ -5,7 +5,6 @@ import supervisely.io.env as sly_env
5
5
  import supervisely.nn.training.gui.utils as gui_utils
6
6
  from supervisely import Api, logger
7
7
  from supervisely._utils import is_production
8
- from supervisely.api.api import ApiField
9
8
  from supervisely.app.widgets import (
10
9
  Card,
11
10
  Container,
@@ -34,13 +33,30 @@ class TrainingArtifacts:
34
33
  lock_message = "Artifacts will be available after training is completed"
35
34
 
36
35
  def __init__(self, api: Api, app_options: Dict[str, Any]):
36
+ # Init widgets
37
+ self.artifacts_thumbnail = None
38
+ self.artifacts_field = None
39
+ self.model_benchmark_report_thumbnail = None
40
+ self.model_benchmark_fail_text = None
41
+ self.model_benchmark_widgets = None
42
+ self.model_benchmark_report_field = None
43
+ self.pytorch_instruction = None
44
+ self.onnx_instruction = None
45
+ self.trt_instruction = None
46
+ self.inference_demo_field = None
47
+ self.validator_text = None
48
+ self.container = None
49
+ self.card = None
50
+ # -------------------------------- #
51
+
37
52
  self.display_widgets = []
53
+ self.app_options = app_options
54
+
38
55
  self.success_message_text = (
39
56
  "Training completed. Training artifacts were uploaded to Team Files. "
40
57
  "You can find and open tensorboard logs in the artifacts folder via the "
41
58
  "<a href='https://ecosystem.supervisely.com/apps/tensorboard-experiments-viewer' target='_blank'>Tensorboard Experiment Viewer</a> app."
42
59
  )
43
- self.app_options = app_options
44
60
 
45
61
  # GUI Components
46
62
  self.validator_text = Text("")
@@ -60,7 +76,7 @@ class TrainingArtifacts:
60
76
  self.display_widgets.extend([self.artifacts_field])
61
77
 
62
78
  # Optional Model Benchmark
63
- if app_options.get("model_benchmark", False):
79
+ if self.app_options.get("model_benchmark", False):
64
80
  self.model_benchmark_report_thumbnail = ReportThumbnail()
65
81
  self.model_benchmark_report_thumbnail.hide()
66
82
 
@@ -86,6 +102,8 @@ class TrainingArtifacts:
86
102
  # PyTorch, ONNX, TensorRT demo
87
103
  self.inference_demo_widgets = []
88
104
 
105
+ # Demo display works only for released apps
106
+ self.need_upload_demo = False
89
107
  model_demo = self.app_options.get("demo", None)
90
108
  if model_demo is not None:
91
109
  model_demo_path = model_demo.get("path", None)
@@ -111,7 +129,7 @@ class TrainingArtifacts:
111
129
  )
112
130
 
113
131
  if model_demo_gh_link is not None:
114
- gh_branch = "blob/main"
132
+ gh_branch = f"blob/{model_demo.get('branch', 'master')}"
115
133
  link_to_demo = f"{model_demo_gh_link}/{gh_branch}/{model_demo_path}"
116
134
 
117
135
  if model_demo_gh_link is not None and model_demo_path is not None:
@@ -186,6 +204,7 @@ class TrainingArtifacts:
186
204
  )
187
205
  self.inference_demo_field.hide()
188
206
  self.display_widgets.extend([self.inference_demo_field])
207
+ self.need_upload_demo = True
189
208
  # -------------------------------- #
190
209
 
191
210
  self.container = Container(self.display_widgets)
@@ -21,9 +21,21 @@ class TrainingLogs:
21
21
  lock_message = "Start training to unlock"
22
22
 
23
23
  def __init__(self, app_options: Dict[str, Any]):
24
+ # Init widgets
25
+ self.tensorboard_button = None
26
+ self.tensorboard_offline_button = None
27
+ self.logs_button = None
28
+ self.task_logs = None
29
+ self.progress_bar_main = None
30
+ self.progress_bar_secondary = None
31
+ self.validator_text = None
32
+ self.container = None
33
+ self.card = None
34
+ # -------------------------------- #
35
+
24
36
  self.display_widgets = []
25
- api = Api.from_env()
26
37
  self.app_options = app_options
38
+ api = Api.from_env()
27
39
 
28
40
  # GUI Components
29
41
  self.validator_text = Text("")
@@ -42,6 +54,7 @@ class TrainingLogs:
42
54
  self.tensorboard_link = f"{api.server_address}{sly_url_prefix}/tensorboard/"
43
55
  else:
44
56
  self.tensorboard_link = "http://localhost:8000/tensorboard"
57
+
45
58
  self.tensorboard_button = Button(
46
59
  "Open Tensorboard",
47
60
  button_type="info",
@@ -54,7 +67,6 @@ class TrainingLogs:
54
67
  self.display_widgets.extend([self.validator_text, self.tensorboard_button])
55
68
 
56
69
  # Offline session Tensorboard button
57
- self.tensorboard_offline_button = None
58
70
  if is_production():
59
71
  workspace_id = sly_env.workspace_id()
60
72
  app_name = "Tensorboard Experiments Viewer"
@@ -79,7 +91,7 @@ class TrainingLogs:
79
91
  )
80
92
 
81
93
  # Optional Show logs button
82
- if app_options.get("show_logs_in_gui", False):
94
+ if self.app_options.get("show_logs_in_gui", False):
83
95
  self.logs_button = Button(
84
96
  text="Show logs",
85
97
  plain=True,
@@ -1,16 +1,12 @@
1
1
  from typing import Any, Dict
2
2
 
3
- from supervisely import Api
4
3
  from supervisely.app.widgets import (
5
4
  Button,
6
5
  Card,
7
6
  Container,
8
- DoneLabel,
9
7
  Empty,
10
8
  Field,
11
- FolderThumbnail,
12
9
  Input,
13
- ReportThumbnail,
14
10
  SelectCudaDevice,
15
11
  Text,
16
12
  )
@@ -19,14 +15,25 @@ from supervisely.app.widgets import (
19
15
  class TrainingProcess:
20
16
  title = "Training Process"
21
17
  description = "Manage training process"
22
- lock_message = "Select hyperparametrs to unlock"
18
+ lock_message = "Select hyperparameters to unlock"
23
19
 
24
20
  def __init__(self, app_options: Dict[str, Any]):
21
+ # Initialize widgets to None
22
+ self.select_device = None
23
+ self.select_device_field = None
24
+ self.experiment_name_input = None
25
+ self.experiment_name_field = None
26
+ self.start_button = None
27
+ self.stop_button = None
28
+ self.validator_text = None
29
+ self.container = None
30
+ self.card = None
31
+ # -------------------------------- #
32
+
25
33
  self.display_widgets = []
26
34
  self.app_options = app_options
27
35
 
28
36
  # GUI Components
29
- # Optional Select CUDA device
30
37
  if self.app_options.get("device_selector", False):
31
38
  self.select_device = SelectCudaDevice()
32
39
  self.select_device_field = Field(
@@ -35,7 +42,6 @@ class TrainingProcess:
35
42
  content=self.select_device,
36
43
  )
37
44
  self.display_widgets.extend([self.select_device_field])
38
- # -------------------------------- #
39
45
 
40
46
  self.experiment_name_input = Input("Enter experiment name")
41
47
  self.experiment_name_field = Field(
@@ -59,13 +65,8 @@ class TrainingProcess:
59
65
  self.validator_text.hide()
60
66
 
61
67
  self.display_widgets.extend(
62
- [
63
- self.experiment_name_field,
64
- button_container,
65
- self.validator_text,
66
- ]
68
+ [self.experiment_name_field, button_container, self.validator_text]
67
69
  )
68
- # -------------------------------- #
69
70
 
70
71
  self.container = Container(self.display_widgets)
71
72
  self.card = Card(
@@ -8,7 +8,7 @@ training workflows in a Supervisely application.
8
8
  import shutil
9
9
  import subprocess
10
10
  from datetime import datetime
11
- from os import getcwd, listdir
11
+ from os import getcwd, listdir, walk
12
12
  from os.path import basename, exists, expanduser, isdir, isfile, join
13
13
  from typing import Any, Dict, List, Literal, Optional, Union
14
14
  from urllib.request import urlopen
@@ -56,7 +56,7 @@ from supervisely.nn.inference.inference import Inference
56
56
  from supervisely.nn.task_type import TaskType
57
57
  from supervisely.nn.training.gui.gui import TrainGUI
58
58
  from supervisely.nn.training.loggers import setup_train_logger, train_logger
59
- from supervisely.nn.utils import ModelSource
59
+ from supervisely.nn.utils import ModelSource, _get_model_name
60
60
  from supervisely.output import set_directory
61
61
  from supervisely.project.download import (
62
62
  copy_from_cache,
@@ -135,6 +135,7 @@ class TrainApp:
135
135
  self._hyperparameters = self._load_hyperparameters(hyperparameters)
136
136
  self._app_options = self._load_app_options(app_options)
137
137
  self._inference_class = None
138
+ self._is_inference_class_regirested = False
138
139
  # ----------------------------------------- #
139
140
 
140
141
  # Directories
@@ -174,6 +175,7 @@ class TrainApp:
174
175
  self.gui: TrainGUI = TrainGUI(
175
176
  self.framework_name, self._models, self._hyperparameters, self._app_options
176
177
  )
178
+
177
179
  self.app = Application(layout=self.gui.layout)
178
180
  self._server = self.app.get_server()
179
181
  self._train_func = None
@@ -345,6 +347,16 @@ class TrainApp:
345
347
  """
346
348
  return self.gui.model_selector.get_model_info()
347
349
 
350
+ @property
351
+ def task_type(self) -> TaskType:
352
+ """
353
+ Returns the task type of the model.
354
+
355
+ :return: Task type.
356
+ :rtype: TaskType
357
+ """
358
+ return self.gui.model_selector.get_selected_task_type()
359
+
348
360
  @property
349
361
  def device(self) -> str:
350
362
  """
@@ -359,9 +371,9 @@ class TrainApp:
359
371
  @property
360
372
  def classes(self) -> List[str]:
361
373
  """
362
- Returns the selected classes for training.
374
+ Returns the selected classes names for training.
363
375
 
364
- :return: List of selected classes.
376
+ :return: List of selected classes names.
365
377
  :rtype: List[str]
366
378
  """
367
379
  selected_classes = set(self.gui.classes_selector.get_selected_classes())
@@ -556,7 +568,10 @@ class TrainApp:
556
568
  model_meta,
557
569
  gt_project_id,
558
570
  )
559
- evaluation_report_link = abs_url(f"/model-benchmark?id={str(mb_eval_report_id)}")
571
+ if mb_eval_report_id is not None:
572
+ evaluation_report_link = abs_url(
573
+ f"/model-benchmark?id={str(mb_eval_report_id)}"
574
+ )
560
575
  except Exception as e:
561
576
  logger.error(f"Model benchmark failed: {e}")
562
577
 
@@ -596,7 +611,7 @@ class TrainApp:
596
611
  self._workflow_output(remote_dir, file_info, mb_eval_lnk_file_info, mb_eval_report_id)
597
612
 
598
613
  def register_inference_class(
599
- self, inference_class: Inference, inference_settings: dict = None
614
+ self, inference_class: Inference, inference_settings: Union[str, dict] = None
600
615
  ) -> None:
601
616
  """
602
617
  Registers an inference class for the training application to do model benchmarking.
@@ -606,8 +621,19 @@ class TrainApp:
606
621
  :param inference_settings: Settings for the inference class.
607
622
  :type inference_settings: dict
608
623
  """
624
+ # if not self.is_model_benchmark_enabled:
625
+ # raise ValueError(
626
+ # "Enable 'model_benchmark' in app_options.yaml to register an inference class."
627
+ # )
628
+
629
+ self._is_inference_class_regirested = True
609
630
  self._inference_class = inference_class
610
- self._inference_settings = inference_settings
631
+ self._inference_settings = None
632
+ if isinstance(inference_settings, str):
633
+ with open(inference_settings, "r") as file:
634
+ self._inference_settings = yaml.safe_load(file)
635
+ else:
636
+ self._inference_settings = inference_settings
611
637
 
612
638
  def get_app_state(self, experiment_info: dict = None) -> dict:
613
639
  """
@@ -616,7 +642,6 @@ class TrainApp:
616
642
  :return: Application state.
617
643
  :rtype: dict
618
644
  """
619
- input_data = {"project_id": self.project_id}
620
645
  train_val_splits = self._get_train_val_splits_for_app_state()
621
646
  model = self._get_model_config_for_app_state(experiment_info)
622
647
 
@@ -629,7 +654,6 @@ class TrainApp:
629
654
  }
630
655
 
631
656
  app_state = {
632
- "input": input_data,
633
657
  "train_val_split": train_val_splits,
634
658
  "classes": self.classes,
635
659
  "model": model,
@@ -778,6 +802,7 @@ class TrainApp:
778
802
  if not self.gui.input_selector.get_cache_value() or is_development():
779
803
  self._download_no_cache(dataset_infos, total_images)
780
804
  self.sly_project = Project(self.project_dir, OpenMode.READ)
805
+ self.sly_project.remove_classes_except(self.project_dir, self.classes, True)
781
806
  return
782
807
 
783
808
  try:
@@ -792,6 +817,7 @@ class TrainApp:
792
817
  self._download_no_cache(dataset_infos, total_images)
793
818
  finally:
794
819
  self.sly_project = Project(self.project_dir, OpenMode.READ)
820
+ self.sly_project.remove_classes_except(self.project_dir, self.classes, True)
795
821
  logger.info(f"Project downloaded successfully to: '{self.project_dir}'")
796
822
 
797
823
  def _download_no_cache(self, dataset_infos: List[DatasetInfo], total_images: int) -> None:
@@ -1160,9 +1186,9 @@ class TrainApp:
1160
1186
  experiment_info should contain the following keys:
1161
1187
  - model_name": str
1162
1188
  - task_type": str
1163
- - model_files": dict
1164
1189
  - checkpoints": list
1165
1190
  - best_checkpoint": str
1191
+ - model_files": Optional[dict]
1166
1192
 
1167
1193
  Other keys are generated by the TrainApp class automatically
1168
1194
 
@@ -1179,7 +1205,6 @@ class TrainApp:
1179
1205
  required_keys = {
1180
1206
  "model_name": str,
1181
1207
  "task_type": str,
1182
- "model_files": dict,
1183
1208
  "checkpoints": (list, str),
1184
1209
  "best_checkpoint": str,
1185
1210
  }
@@ -1388,7 +1413,14 @@ class TrainApp:
1388
1413
  # Prepare logs
1389
1414
  if sly_fs.dir_exists(self.log_dir):
1390
1415
  logs_dir = join(self.output_dir, "logs")
1391
- shutil.copytree(self.log_dir, logs_dir)
1416
+ logger_type = self._app_options.get("train_logger", None)
1417
+ for root, _, files in walk(self.log_dir):
1418
+ for file in files:
1419
+ if logger_type is None or logger_type == "tensorboard":
1420
+ if ".tfevents." in file:
1421
+ src_log_path = join(root, file)
1422
+ dst_log_path = join(logs_dir, file)
1423
+ sly_fs.copy_file(src_log_path, dst_log_path)
1392
1424
  return experiment_info
1393
1425
 
1394
1426
  # Generate experiment_info.json and app_state.json
@@ -1534,9 +1566,11 @@ class TrainApp:
1534
1566
  experiment_info["best_checkpoint"] = sly_fs.get_file_name_with_ext(
1535
1567
  experiment_info["best_checkpoint"]
1536
1568
  )
1537
- experiment_info["model_files"]["config"] = sly_fs.get_file_name_with_ext(
1538
- experiment_info["model_files"]["config"]
1539
- )
1569
+
1570
+ for file in experiment_info["model_files"]:
1571
+ experiment_info["model_files"][file] = sly_fs.get_file_name_with_ext(
1572
+ experiment_info["model_files"][file]
1573
+ )
1540
1574
 
1541
1575
  local_path = join(self.output_dir, self._experiment_json_file)
1542
1576
  remote_path = join(remote_dir, self._experiment_json_file)
@@ -1594,6 +1628,8 @@ class TrainApp:
1594
1628
  )
1595
1629
 
1596
1630
  def _upload_demo_files(self, remote_dir: str) -> None:
1631
+ if not self.gui.training_artifacts.need_upload_demo:
1632
+ return
1597
1633
  demo = self._app_options.get("demo")
1598
1634
  if demo is None:
1599
1635
  return
@@ -1671,9 +1707,7 @@ class TrainApp:
1671
1707
  experiment_info = experiment_info or {}
1672
1708
 
1673
1709
  if self.model_source == ModelSource.PRETRAINED:
1674
- model_name = experiment_info.get("model_name") or self.model_info.get("meta", {}).get(
1675
- "model_name"
1676
- )
1710
+ model_name = experiment_info.get("model_name") or _get_model_name(self.model_info)
1677
1711
  return {
1678
1712
  "source": ModelSource.PRETRAINED,
1679
1713
  "model_name": model_name,
@@ -1682,7 +1716,7 @@ class TrainApp:
1682
1716
  return {
1683
1717
  "source": ModelSource.CUSTOM,
1684
1718
  "task_id": self.task_id,
1685
- "checkpoint": "checkpoint.pth",
1719
+ "checkpoint": "custom checkpoint",
1686
1720
  }
1687
1721
 
1688
1722
  # ----------------------------------------- #
@@ -1764,10 +1798,10 @@ class TrainApp:
1764
1798
  self.gui.training_process.start_button.loading = False
1765
1799
  self.gui.training_process.start_button.disable()
1766
1800
  self.gui.training_process.stop_button.disable()
1767
- # self.gui.training_logs.tensorboard_button.disable()
1768
1801
 
1769
1802
  # Set artifacts to GUI
1770
- self._api.task.set_output_experiment(self.task_id, experiment_info)
1803
+ if is_production():
1804
+ self._api.task.set_output_experiment(self.task_id, experiment_info)
1771
1805
  set_directory(remote_dir)
1772
1806
  self.gui.training_artifacts.artifacts_thumbnail.set(file_info)
1773
1807
  self.gui.training_artifacts.artifacts_thumbnail.show()
@@ -1814,7 +1848,7 @@ class TrainApp:
1814
1848
  self.gui.training_artifacts.trt_instruction.show()
1815
1849
 
1816
1850
  # Show the inference demo widget if overview or any demo is available
1817
- if hasattr(self.gui.training_artifacts, "inference_demo_field") and any(
1851
+ if self.gui.training_artifacts.inference_demo_field is not None and any(
1818
1852
  [
1819
1853
  self.gui.training_artifacts.overview_demo_exists(demo_path),
1820
1854
  self.gui.training_artifacts.pytorch_demo_exists(demo_path),
@@ -1822,7 +1856,8 @@ class TrainApp:
1822
1856
  self.gui.training_artifacts.trt_demo_exists(demo_path),
1823
1857
  ]
1824
1858
  ):
1825
- self.gui.training_artifacts.inference_demo_field.show()
1859
+ if self.gui.training_artifacts.inference_demo_field is not None:
1860
+ self.gui.training_artifacts.inference_demo_field.show()
1826
1861
  # ---------------------------- #
1827
1862
 
1828
1863
  # Set status to completed and unlock
@@ -1932,6 +1967,7 @@ class TrainApp:
1932
1967
  "model_name": experiment_info["model_name"],
1933
1968
  "framework_name": self.framework_name,
1934
1969
  "model_meta": model_meta.to_json(),
1970
+ "task_type": task_type,
1935
1971
  }
1936
1972
 
1937
1973
  logger.info(f"Deploy parameters: {self._benchmark_params}")
@@ -2551,7 +2587,6 @@ class TrainApp:
2551
2587
  if task_type == TaskType.OBJECT_DETECTION:
2552
2588
  Project.to_detection_task(project.directory, inplace=True)
2553
2589
  pr_prefix = "[detection]: "
2554
- # @TODO: dont convert segmentation?
2555
2590
  elif (
2556
2591
  task_type == TaskType.INSTANCE_SEGMENTATION
2557
2592
  or task_type == TaskType.SEMANTIC_SEGMENTATION
supervisely/nn/utils.py CHANGED
@@ -55,3 +55,12 @@ class DeployInfo:
55
55
  model_precision: str
56
56
  hardware: str
57
57
  deploy_params: dict
58
+
59
+
60
+ def _get_model_name(model_info: dict):
61
+ name = model_info.get("model_name")
62
+ if not name:
63
+ name = model_info.get("meta", {}).get("model_name")
64
+ if not name:
65
+ raise ValueError("Model name not found not in model_info nor in meta.")
66
+ return name
@@ -1933,9 +1933,10 @@ class Dataset(KeyObject):
1933
1933
  self,
1934
1934
  meta: ProjectMeta,
1935
1935
  dest_dir: Optional[str] = None,
1936
- task_type: Literal["detection", "segmentation", "pose"] = "detection",
1936
+ task_type: Literal["detect", "segment", "pose"] = "detect",
1937
1937
  log_progress: bool = False,
1938
1938
  progress_cb: Optional[Callable] = None,
1939
+ is_val: Optional[bool] = None,
1939
1940
  ):
1940
1941
  """
1941
1942
  Convert Supervisely dataset to YOLO format.
@@ -1950,6 +1951,8 @@ class Dataset(KeyObject):
1950
1951
  :type log_progress: :class:`str`, optional
1951
1952
  :param progress_cb: Progress callback.
1952
1953
  :type progress_cb: :class:`Callable`, optional
1954
+ :param is_val: If True, the dataset is a validation dataset.
1955
+ :type is_val: :class:`bool`, optional
1953
1956
  :return: YOLO dataset in dictionary format.
1954
1957
  :rtype: :class:`dict`
1955
1958
 
@@ -1975,6 +1978,7 @@ class Dataset(KeyObject):
1975
1978
  task_type=task_type,
1976
1979
  log_progress=log_progress,
1977
1980
  progress_cb=progress_cb,
1981
+ is_val=is_val,
1978
1982
  )
1979
1983
 
1980
1984
  def to_pascal_voc(
@@ -3821,19 +3825,27 @@ class Project:
3821
3825
  def to_yolo(
3822
3826
  self,
3823
3827
  dest_dir: Optional[str] = None,
3824
- task_type: Literal["detection", "segmentation", "pose"] = "detection",
3828
+ task_type: Literal["detect", "segment", "pose"] = "detect",
3825
3829
  log_progress: bool = True,
3826
3830
  progress_cb: Optional[Callable] = None,
3831
+ val_datasets: Optional[List[str]] = None,
3827
3832
  ) -> None:
3828
3833
  """
3829
3834
  Convert Supervisely project to YOLO format.
3830
3835
 
3831
3836
  :param dest_dir: Destination directory.
3832
3837
  :type dest_dir: :class:`str`, optional
3838
+ :param task_type: Task type for YOLO format. Possible values: 'detection', 'segmentation', 'pose'.
3839
+ :type task_type: :class:`str` or :class:`TaskType`, optional
3833
3840
  :param log_progress: Show uploading progress bar.
3834
3841
  :type log_progress: :class:`bool`
3835
3842
  :param progress_cb: Function for tracking conversion progress (for all items in the project).
3836
3843
  :type progress_cb: callable, optional
3844
+ :param val_datasets: List of dataset names for validation.
3845
+ Full dataset names are required (e.g., 'ds0/nested_ds1/ds3').
3846
+ If specified, datasets from the list will be marked as val, others as train.
3847
+ If not specified, the function will determine the validation datasets automatically.
3848
+ :type val_datasets: :class:`list` [ :class:`str` ], optional
3837
3849
  :return: None
3838
3850
  :rtype: NoneType
3839
3851
 
@@ -3855,12 +3867,13 @@ class Project:
3855
3867
 
3856
3868
  from supervisely.convert import project_to_yolo
3857
3869
 
3858
- project_to_yolo(
3870
+ return project_to_yolo(
3859
3871
  project=self,
3860
3872
  dest_dir=dest_dir,
3861
3873
  task_type=task_type,
3862
3874
  log_progress=log_progress,
3863
3875
  progress_cb=progress_cb,
3876
+ val_datasets=val_datasets,
3864
3877
  )
3865
3878
 
3866
3879
  def to_pascal_voc(
@@ -816,30 +816,28 @@ def convert_3d_nifti_to_nrrd(path: str) -> Tuple[np.ndarray, dict]:
816
816
  path = "/home/admin/work/volumes/vol_01.nii"
817
817
  data, header = sly.volume.convert_nifti_to_nrrd(path)
818
818
  """
819
+ import SimpleITK as sitk
819
820
 
820
- import nibabel as nib # pylint: disable=import-error
821
+ nifti_image = sitk.ReadImage(path)
822
+ nifti_image = _sitk_image_orient_ras(nifti_image)
823
+ data = sitk.GetArrayFromImage(nifti_image)
824
+ data = np.transpose(data, (2, 1, 0))
825
+
826
+ direction = np.array(nifti_image.GetDirection()).reshape(3, 3)
827
+ spacing = np.array(nifti_image.GetSpacing())
828
+ origin = np.array(nifti_image.GetOrigin())
829
+
830
+ space_directions = (direction.T * spacing[:, None]).tolist()
821
831
 
822
- orientation_map = {
823
- ('R', 'A', 'S'): "right-anterior-superior",
824
- ('L', 'P', 'S'): "left-posterior-superior",
825
- ('R', 'P', 'I'): "right-posterior-inferior",
826
- ('L', 'A', 'I'): "left-anterior-inferior"
827
- }
828
- nifti = nib.load(path)
829
- reordered_to_ras_nifti = nib.as_closest_canonical(nifti)
830
- data = reordered_to_ras_nifti.get_fdata()
831
- affine = reordered_to_ras_nifti.affine
832
- orientation = nib.aff2axcodes(affine)
833
- space_directions = affine[:3, :3].tolist()
834
- space_origin = affine[:3, 3].tolist()
835
832
  header = {
836
- "space": orientation_map.get(orientation, "unknown"),
837
- "space directions": space_directions,
838
- "space origin": space_origin,
839
- "sizes": data.shape,
840
- "type": str(data.dtype),
841
- "dimension": len(data.shape),
842
- }
833
+ "dimension": 3,
834
+ "space": "right-anterior-superior",
835
+ "sizes": list(data.shape),
836
+ "space directions": space_directions,
837
+ "endian": "little",
838
+ "encoding": "gzip",
839
+ "space origin": origin
840
+ }
843
841
  return data, header
844
842
 
845
843
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.325
3
+ Version: 6.73.327
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely