supervisely 6.73.324__py3-none-any.whl → 6.73.325__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/api/file_api.py +3 -1
 - supervisely/convert/converter.py +44 -6
 - supervisely/convert/volume/__init__.py +3 -0
 - supervisely/convert/volume/nii/nii_planes_volume_converter.py +114 -0
 - supervisely/convert/volume/nii/nii_volume_converter.py +107 -39
 - supervisely/convert/volume/nii/nii_volume_helper.py +12 -0
 - {supervisely-6.73.324.dist-info → supervisely-6.73.325.dist-info}/METADATA +1 -1
 - {supervisely-6.73.324.dist-info → supervisely-6.73.325.dist-info}/RECORD +12 -11
 - {supervisely-6.73.324.dist-info → supervisely-6.73.325.dist-info}/LICENSE +0 -0
 - {supervisely-6.73.324.dist-info → supervisely-6.73.325.dist-info}/WHEEL +0 -0
 - {supervisely-6.73.324.dist-info → supervisely-6.73.325.dist-info}/entry_points.txt +0 -0
 - {supervisely-6.73.324.dist-info → supervisely-6.73.325.dist-info}/top_level.txt +0 -0
 
    
        supervisely/api/file_api.py
    CHANGED
    
    | 
         @@ -25,6 +25,7 @@ import supervisely.io.env as env 
     | 
|
| 
       25 
25 
     | 
    
         
             
            import supervisely.io.fs as sly_fs
         
     | 
| 
       26 
26 
     | 
    
         
             
            from supervisely._utils import batched, rand_str, run_coroutine
         
     | 
| 
       27 
27 
     | 
    
         
             
            from supervisely.api.module_api import ApiField, ModuleApiBase
         
     | 
| 
      
 28 
     | 
    
         
            +
            from supervisely.api.remote_storage_api import RemoteStorageApi
         
     | 
| 
       28 
29 
     | 
    
         
             
            from supervisely.io.fs import (
         
     | 
| 
       29 
30 
     | 
    
         
             
                ensure_base_path,
         
     | 
| 
       30 
31 
     | 
    
         
             
                get_file_ext,
         
     | 
| 
         @@ -1420,7 +1421,8 @@ class FileApi(ModuleApiBase): 
     | 
|
| 
       1420 
1421 
     | 
    
         
             
                        api.file.upload_directory(9, local_path, path_to_dir)
         
     | 
| 
       1421 
1422 
     | 
    
         
             
                    """
         
     | 
| 
       1422 
1423 
     | 
    
         
             
                    if not remote_dir.startswith("/"):
         
     | 
| 
       1423 
     | 
    
         
            -
                         
     | 
| 
      
 1424 
     | 
    
         
            +
                        if not RemoteStorageApi.is_bucket_url(remote_dir):
         
     | 
| 
      
 1425 
     | 
    
         
            +
                            remote_dir = "/" + remote_dir
         
     | 
| 
       1424 
1426 
     | 
    
         | 
| 
       1425 
1427 
     | 
    
         
             
                    if self.dir_exists(team_id, remote_dir):
         
     | 
| 
       1426 
1428 
     | 
    
         
             
                        if change_name_if_conflict is True:
         
     | 
    
        supervisely/convert/converter.py
    CHANGED
    
    | 
         @@ -57,12 +57,12 @@ class ImportManager: 
     | 
|
| 
       57 
57 
     | 
    
         
             
                    self._labeling_interface = labeling_interface
         
     | 
| 
       58 
58 
     | 
    
         
             
                    self._upload_as_links = upload_as_links
         
     | 
| 
       59 
59 
     | 
    
         
             
                    self._remote_files_map = {}
         
     | 
| 
      
 60 
     | 
    
         
            +
                    self._modality = project_type
         
     | 
| 
       60 
61 
     | 
    
         | 
| 
       61 
62 
     | 
    
         
             
                    self._input_data = self._prepare_input_data(input_data)
         
     | 
| 
       62 
63 
     | 
    
         
             
                    self._unpack_archives(self._input_data)
         
     | 
| 
       63 
64 
     | 
    
         
             
                    remove_junk_from_dir(self._input_data)
         
     | 
| 
       64 
65 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
                    self._modality = project_type
         
     | 
| 
       66 
66 
     | 
    
         
             
                    self._converter = self.get_converter()
         
     | 
| 
       67 
67 
     | 
    
         
             
                    if isinstance(self._converter, (HighColorDepthImageConverter, CSVConverter)):
         
     | 
| 
       68 
68 
     | 
    
         
             
                        self._converter.team_id = self._team_id
         
     | 
| 
         @@ -112,17 +112,27 @@ class ImportManager: 
     | 
|
| 
       112 
112 
     | 
    
         
             
                        logger.info(f"Input data is a local file: {input_data}. Will use its directory")
         
     | 
| 
       113 
113 
     | 
    
         
             
                        return os.path.dirname(input_data)
         
     | 
| 
       114 
114 
     | 
    
         
             
                    elif self._api.storage.exists(self._team_id, input_data):
         
     | 
| 
       115 
     | 
    
         
            -
                        if self._upload_as_links 
     | 
| 
      
 115 
     | 
    
         
            +
                        if self._upload_as_links and str(self._modality) in [
         
     | 
| 
      
 116 
     | 
    
         
            +
                            ProjectType.IMAGES.value,
         
     | 
| 
      
 117 
     | 
    
         
            +
                            ProjectType.VIDEOS.value,
         
     | 
| 
      
 118 
     | 
    
         
            +
                        ]:
         
     | 
| 
       116 
119 
     | 
    
         
             
                            logger.info(f"Input data is a remote file: {input_data}. Scanning...")
         
     | 
| 
       117 
     | 
    
         
            -
                            return self. 
     | 
| 
      
 120 
     | 
    
         
            +
                            return self._reproduce_remote_files(input_data)
         
     | 
| 
       118 
121 
     | 
    
         
             
                        else:
         
     | 
| 
      
 122 
     | 
    
         
            +
                            if self._upload_as_links and str(self._modality) == ProjectType.VOLUMES.value:
         
     | 
| 
      
 123 
     | 
    
         
            +
                                self._scan_remote_files(input_data)
         
     | 
| 
       119 
124 
     | 
    
         
             
                            logger.info(f"Input data is a remote file: {input_data}. Downloading...")
         
     | 
| 
       120 
125 
     | 
    
         
             
                            return self._download_input_data(input_data)
         
     | 
| 
       121 
126 
     | 
    
         
             
                    elif self._api.storage.dir_exists(self._team_id, input_data):
         
     | 
| 
       122 
     | 
    
         
            -
                        if self._upload_as_links 
     | 
| 
      
 127 
     | 
    
         
            +
                        if self._upload_as_links and str(self._modality) in [
         
     | 
| 
      
 128 
     | 
    
         
            +
                            ProjectType.IMAGES.value,
         
     | 
| 
      
 129 
     | 
    
         
            +
                            ProjectType.VIDEOS.value,
         
     | 
| 
      
 130 
     | 
    
         
            +
                        ]:
         
     | 
| 
       123 
131 
     | 
    
         
             
                            logger.info(f"Input data is a remote directory: {input_data}. Scanning...")
         
     | 
| 
       124 
     | 
    
         
            -
                            return self. 
     | 
| 
      
 132 
     | 
    
         
            +
                            return self._reproduce_remote_files(input_data, is_dir=True)
         
     | 
| 
       125 
133 
     | 
    
         
             
                        else:
         
     | 
| 
      
 134 
     | 
    
         
            +
                            if self._upload_as_links and str(self._modality) == ProjectType.VOLUMES.value:
         
     | 
| 
      
 135 
     | 
    
         
            +
                                self._scan_remote_files(input_data, is_dir=True)
         
     | 
| 
       126 
136 
     | 
    
         
             
                            logger.info(f"Input data is a remote directory: {input_data}. Downloading...")
         
     | 
| 
       127 
137 
     | 
    
         
             
                            return self._download_input_data(input_data, is_dir=True)
         
     | 
| 
       128 
138 
     | 
    
         
             
                    else:
         
     | 
| 
         @@ -160,7 +170,35 @@ class ImportManager: 
     | 
|
| 
       160 
170 
     | 
    
         
             
                    return local_path
         
     | 
| 
       161 
171 
     | 
    
         | 
| 
       162 
172 
     | 
    
         
             
                def _scan_remote_files(self, remote_path, is_dir=False):
         
     | 
| 
       163 
     | 
    
         
            -
                    """ 
     | 
| 
      
 173 
     | 
    
         
            +
                    """
         
     | 
| 
      
 174 
     | 
    
         
            +
                    Scan remote directory. Collect local-remote paths mapping
         
     | 
| 
      
 175 
     | 
    
         
            +
                    Will be used to save relations between uploaded files and remote files (for volumes).
         
     | 
| 
      
 176 
     | 
    
         
            +
                    """
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                    dir_path = remote_path.rstrip("/") if is_dir else os.path.dirname(remote_path)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    dir_name = os.path.basename(dir_path)
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    local_path = os.path.join(get_data_dir(), dir_name)
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                    if is_dir:
         
     | 
| 
      
 184 
     | 
    
         
            +
                        files = self._api.storage.list(self._team_id, remote_path, include_folders=False)
         
     | 
| 
      
 185 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 186 
     | 
    
         
            +
                        files = [self._api.storage.get_info_by_path(self._team_id, remote_path)]
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                    unique_directories = set()
         
     | 
| 
      
 189 
     | 
    
         
            +
                    for file in files:
         
     | 
| 
      
 190 
     | 
    
         
            +
                        new_path = file.path.replace(dir_path, local_path)
         
     | 
| 
      
 191 
     | 
    
         
            +
                        self._remote_files_map[new_path] = file.path
         
     | 
| 
      
 192 
     | 
    
         
            +
                        unique_directories.add(str(Path(file.path).parent))
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                    logger.info(f"Scanned remote directories:\n   - " + "\n   - ".join(unique_directories))
         
     | 
| 
      
 195 
     | 
    
         
            +
                    return local_path
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                def _reproduce_remote_files(self, remote_path, is_dir=False):
         
     | 
| 
      
 198 
     | 
    
         
            +
                    """
         
     | 
| 
      
 199 
     | 
    
         
            +
                    Scan remote directory and create dummy structure locally.
         
     | 
| 
      
 200 
     | 
    
         
            +
                    Will be used to detect annotation format (by dataset structure) remotely.
         
     | 
| 
      
 201 
     | 
    
         
            +
                    """
         
     | 
| 
       164 
202 
     | 
    
         | 
| 
       165 
203 
     | 
    
         
             
                    dir_path = remote_path.rstrip("/") if is_dir else os.path.dirname(remote_path)
         
     | 
| 
       166 
204 
     | 
    
         
             
                    dir_name = os.path.basename(dir_path)
         
     | 
| 
         @@ -2,3 +2,6 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            from supervisely.convert.volume.sly.sly_volume_converter import SLYVolumeConverter
         
     | 
| 
       3 
3 
     | 
    
         
             
            from supervisely.convert.volume.dicom.dicom_converter import DICOMConverter
         
     | 
| 
       4 
4 
     | 
    
         
             
            from supervisely.convert.volume.nii.nii_volume_converter import NiiConverter
         
     | 
| 
      
 5 
     | 
    
         
            +
            from supervisely.convert.volume.nii.nii_planes_volume_converter import (
         
     | 
| 
      
 6 
     | 
    
         
            +
                NiiPlaneStructuredConverter,
         
     | 
| 
      
 7 
     | 
    
         
            +
            )
         
     | 
| 
         @@ -0,0 +1,114 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import os
         
     | 
| 
      
 2 
     | 
    
         
            +
            from collections import defaultdict
         
     | 
| 
      
 3 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            from supervisely import ProjectMeta, logger
         
     | 
| 
      
 6 
     | 
    
         
            +
            from supervisely.annotation.obj_class import ObjClass
         
     | 
| 
      
 7 
     | 
    
         
            +
            from supervisely.convert.volume.nii import nii_volume_helper as helper
         
     | 
| 
      
 8 
     | 
    
         
            +
            from supervisely.convert.volume.nii.nii_volume_converter import NiiConverter
         
     | 
| 
      
 9 
     | 
    
         
            +
            from supervisely.convert.volume.volume_converter import VolumeConverter
         
     | 
| 
      
 10 
     | 
    
         
            +
            from supervisely.geometry.mask_3d import Mask3D
         
     | 
| 
      
 11 
     | 
    
         
            +
            from supervisely.io.fs import get_file_name
         
     | 
| 
      
 12 
     | 
    
         
            +
            from supervisely.volume.volume import is_nifti_file
         
     | 
| 
      
 13 
     | 
    
         
            +
            from supervisely.volume_annotation.volume_annotation import VolumeAnnotation
         
     | 
| 
      
 14 
     | 
    
         
            +
            from supervisely.volume_annotation.volume_object import VolumeObject
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            class NiiPlaneStructuredConverter(NiiConverter, VolumeConverter):
         
     | 
| 
      
 18 
     | 
    
         
            +
                """Convert NIfTI 3D volume file to Supervisely format.
         
     | 
| 
      
 19 
     | 
    
         
            +
                The NIfTI file should be structured as follows:
         
     | 
| 
      
 20 
     | 
    
         
            +
                - <prefix>_anatomic_<idx>.nii (or .nii.gz)
         
     | 
| 
      
 21 
     | 
    
         
            +
                - <prefix>_inference_<idx>.nii (or .nii.gz)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    where   <prefix> is one of the following: cor, sag, axl
         
     | 
| 
      
 23 
     | 
    
         
            +
                            <idx> is the index of the volume (to match volumes with annotations)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                Supports .nii and .nii.gz files.
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 28 
     | 
    
         
            +
                    📂 .
         
     | 
| 
      
 29 
     | 
    
         
            +
                    ├── 🩻 axl_anatomic_1.nii
         
     | 
| 
      
 30 
     | 
    
         
            +
                    ├── 🩻 axl_inference_1.nii class 1 (may contain multiple instances of the same class)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    ├── 🩻 cor_anatomic_1.nii
         
     | 
| 
      
 32 
     | 
    
         
            +
                    ├── 🩻 cor_inference_1.nii class 1
         
     | 
| 
      
 33 
     | 
    
         
            +
                    ├── 🩻 sag_anatomic_1.nii
         
     | 
| 
      
 34 
     | 
    
         
            +
                    ├── 🩻 sag_inference_1.nii class 1
         
     | 
| 
      
 35 
     | 
    
         
            +
                    ├── 🩻 sag_inference_2.nii class 2
         
     | 
| 
      
 36 
     | 
    
         
            +
                    └── 🩻 sag_inference_3.nii class 3
         
     | 
| 
      
 37 
     | 
    
         
            +
                """
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def validate_format(self) -> bool:
         
     | 
| 
      
 40 
     | 
    
         
            +
                    # create Items
         
     | 
| 
      
 41 
     | 
    
         
            +
                    converted_dir_name = "converted"
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    volumes_dict = defaultdict(list)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    ann_dict = defaultdict(list)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    for root, _, files in os.walk(self._input_data):
         
     | 
| 
      
 47 
     | 
    
         
            +
                        if converted_dir_name in root:
         
     | 
| 
      
 48 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 49 
     | 
    
         
            +
                        for file in files:
         
     | 
| 
      
 50 
     | 
    
         
            +
                            path = os.path.join(root, file)
         
     | 
| 
      
 51 
     | 
    
         
            +
                            if is_nifti_file(path):
         
     | 
| 
      
 52 
     | 
    
         
            +
                                full_name = get_file_name(path)
         
     | 
| 
      
 53 
     | 
    
         
            +
                                if full_name.endswith(".nii"):
         
     | 
| 
      
 54 
     | 
    
         
            +
                                    full_name = get_file_name(full_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                                prefix = full_name.split("_")[0]
         
     | 
| 
      
 56 
     | 
    
         
            +
                                if prefix not in helper.PlanePrefix.values():
         
     | 
| 
      
 57 
     | 
    
         
            +
                                    continue
         
     | 
| 
      
 58 
     | 
    
         
            +
                                name = full_name.split("_")[1]
         
     | 
| 
      
 59 
     | 
    
         
            +
                                idx = 1 if len(name.split("_")) < 3 else int(name.split("_")[2])
         
     | 
| 
      
 60 
     | 
    
         
            +
                                if name in helper.LABEL_NAME or name[:-1] in helper.LABEL_NAME:
         
     | 
| 
      
 61 
     | 
    
         
            +
                                    ann_dict[prefix].append(path)
         
     | 
| 
      
 62 
     | 
    
         
            +
                                else:
         
     | 
| 
      
 63 
     | 
    
         
            +
                                    volumes_dict[prefix].append(path)
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    self._items = []
         
     | 
| 
      
 66 
     | 
    
         
            +
                    for prefix, paths in volumes_dict.items():
         
     | 
| 
      
 67 
     | 
    
         
            +
                        if len(paths) == 1:
         
     | 
| 
      
 68 
     | 
    
         
            +
                            item = self.Item(item_path=paths[0])
         
     | 
| 
      
 69 
     | 
    
         
            +
                            item.ann_data = ann_dict.get(prefix)
         
     | 
| 
      
 70 
     | 
    
         
            +
                            self._items.append(item)
         
     | 
| 
      
 71 
     | 
    
         
            +
                        elif len(paths) > 1:
         
     | 
| 
      
 72 
     | 
    
         
            +
                            logger.info(
         
     | 
| 
      
 73 
     | 
    
         
            +
                                f"Found {len(paths)} volumes with prefix {prefix}. Will try to match them by directories."
         
     | 
| 
      
 74 
     | 
    
         
            +
                            )
         
     | 
| 
      
 75 
     | 
    
         
            +
                            for path in paths:
         
     | 
| 
      
 76 
     | 
    
         
            +
                                item = self.Item(item_path=path)
         
     | 
| 
      
 77 
     | 
    
         
            +
                                possible_ann_paths = []
         
     | 
| 
      
 78 
     | 
    
         
            +
                                for ann_path in ann_dict.get(prefix):
         
     | 
| 
      
 79 
     | 
    
         
            +
                                    if Path(ann_path).parent == Path(path).parent:
         
     | 
| 
      
 80 
     | 
    
         
            +
                                        possible_ann_paths.append(ann_path)
         
     | 
| 
      
 81 
     | 
    
         
            +
                                item.ann_data = possible_ann_paths
         
     | 
| 
      
 82 
     | 
    
         
            +
                                self._items.append(item)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    self._meta = ProjectMeta()
         
     | 
| 
      
 84 
     | 
    
         
            +
                    return self.items_count > 0
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                def to_supervisely(
         
     | 
| 
      
 87 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 88 
     | 
    
         
            +
                    item: VolumeConverter.Item,
         
     | 
| 
      
 89 
     | 
    
         
            +
                    meta: ProjectMeta = None,
         
     | 
| 
      
 90 
     | 
    
         
            +
                    renamed_classes: dict = None,
         
     | 
| 
      
 91 
     | 
    
         
            +
                    renamed_tags: dict = None,
         
     | 
| 
      
 92 
     | 
    
         
            +
                ) -> VolumeAnnotation:
         
     | 
| 
      
 93 
     | 
    
         
            +
                    """Convert to Supervisely format."""
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 96 
     | 
    
         
            +
                        objs = []
         
     | 
| 
      
 97 
     | 
    
         
            +
                        spatial_figures = []
         
     | 
| 
      
 98 
     | 
    
         
            +
                        for idx, ann_path in enumerate(item.ann_data, start=1):
         
     | 
| 
      
 99 
     | 
    
         
            +
                            for mask, _ in helper.get_annotation_from_nii(ann_path):
         
     | 
| 
      
 100 
     | 
    
         
            +
                                class_name = f"Segment_{idx}"
         
     | 
| 
      
 101 
     | 
    
         
            +
                                class_name = renamed_classes.get(class_name, class_name)
         
     | 
| 
      
 102 
     | 
    
         
            +
                                obj_class = meta.get_obj_class(class_name)
         
     | 
| 
      
 103 
     | 
    
         
            +
                                if obj_class is None:
         
     | 
| 
      
 104 
     | 
    
         
            +
                                    obj_class = ObjClass(class_name, Mask3D)
         
     | 
| 
      
 105 
     | 
    
         
            +
                                    meta = meta.add_obj_class(obj_class)
         
     | 
| 
      
 106 
     | 
    
         
            +
                                    self._meta_changed = True
         
     | 
| 
      
 107 
     | 
    
         
            +
                                    self._meta = meta
         
     | 
| 
      
 108 
     | 
    
         
            +
                                obj = VolumeObject(obj_class, mask_3d=mask)
         
     | 
| 
      
 109 
     | 
    
         
            +
                                spatial_figures.append(obj.figure)
         
     | 
| 
      
 110 
     | 
    
         
            +
                                objs.append(obj)
         
     | 
| 
      
 111 
     | 
    
         
            +
                        return VolumeAnnotation(item.volume_meta, objects=objs, spatial_figures=spatial_figures)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 113 
     | 
    
         
            +
                        logger.warning(f"Failed to convert {item.path} to Supervisely format: {e}")
         
     | 
| 
      
 114 
     | 
    
         
            +
                        return item.create_empty_annotation()
         
     | 
| 
         @@ -1,12 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import os
         
     | 
| 
       2 
2 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            import magic
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
4 
     | 
    
         
             
            from supervisely import ProjectMeta, generate_free_name, logger
         
     | 
| 
       7 
5 
     | 
    
         
             
            from supervisely._utils import batched, is_development
         
     | 
| 
       8 
6 
     | 
    
         
             
            from supervisely.annotation.obj_class import ObjClass
         
     | 
| 
       9 
     | 
    
         
            -
            from supervisely.annotation.obj_class_collection import ObjClassCollection
         
     | 
| 
       10 
7 
     | 
    
         
             
            from supervisely.api.api import Api
         
     | 
| 
       11 
8 
     | 
    
         
             
            from supervisely.convert.base_converter import AvailableVolumeConverters
         
     | 
| 
       12 
9 
     | 
    
         
             
            from supervisely.convert.volume.nii import nii_volume_helper as helper
         
     | 
| 
         @@ -18,12 +15,47 @@ from supervisely.io.fs import ( 
     | 
|
| 
       18 
15 
     | 
    
         
             
                get_file_name_with_ext,
         
     | 
| 
       19 
16 
     | 
    
         
             
                list_files,
         
     | 
| 
       20 
17 
     | 
    
         
             
            )
         
     | 
| 
       21 
     | 
    
         
            -
            from supervisely. 
     | 
| 
      
 18 
     | 
    
         
            +
            from supervisely.task.progress import tqdm_sly
         
     | 
| 
      
 19 
     | 
    
         
            +
            from supervisely.volume.volume import is_nifti_file, read_nrrd_serie_volume_np
         
     | 
| 
       22 
20 
     | 
    
         
             
            from supervisely.volume_annotation.volume_annotation import VolumeAnnotation
         
     | 
| 
       23 
21 
     | 
    
         
             
            from supervisely.volume_annotation.volume_object import VolumeObject
         
     | 
| 
       24 
22 
     | 
    
         | 
| 
       25 
23 
     | 
    
         | 
| 
       26 
24 
     | 
    
         
             
            class NiiConverter(VolumeConverter):
         
     | 
| 
      
 25 
     | 
    
         
            +
                """
         
     | 
| 
      
 26 
     | 
    
         
            +
                Convert NIfTI 3D volume file to Supervisely format.
         
     | 
| 
      
 27 
     | 
    
         
            +
                Supports .nii and .nii.gz files.
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                The NIfTI file should be structured as follows:
         
     | 
| 
      
 30 
     | 
    
         
            +
                - <volume_name>.nii
         
     | 
| 
      
 31 
     | 
    
         
            +
                - <volume_name>/
         
     | 
| 
      
 32 
     | 
    
         
            +
                    - <cls_name_1>.nii
         
     | 
| 
      
 33 
     | 
    
         
            +
                    - <cls_name_2>.nii
         
     | 
| 
      
 34 
     | 
    
         
            +
                    - ...
         
     | 
| 
      
 35 
     | 
    
         
            +
                - ...
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                where   <volume_name> is the name of the volume
         
     | 
| 
      
 38 
     | 
    
         
            +
                        If the volume has annotations, they should be in the corresponding directory
         
     | 
| 
      
 39 
     | 
    
         
            +
                            with the same name as the volume (without extension)
         
     | 
| 
      
 40 
     | 
    
         
            +
                        <cls_name> is the name of the annotation class
         
     | 
| 
      
 41 
     | 
    
         
            +
                            <cls_name>.nii:
         
     | 
| 
      
 42 
     | 
    
         
            +
                                - represent objects of the single class
         
     | 
| 
      
 43 
     | 
    
         
            +
                                - should be unique for the current volume (e.g. tumor.nii.gz, lung.nii.gz)
         
     | 
| 
      
 44 
     | 
    
         
            +
                                - can contain multiple objects of the class (each object should be represented by a different value in the mask)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 47 
     | 
    
         
            +
                📂 .
         
     | 
| 
      
 48 
     | 
    
         
            +
                ├── 📂 CTChest
         
     | 
| 
      
 49 
     | 
    
         
            +
                │   ├── 🩻 lung.nii.gz
         
     | 
| 
      
 50 
     | 
    
         
            +
                │   └── 🩻 tumor.nii.gz
         
     | 
| 
      
 51 
     | 
    
         
            +
                ├── 🩻 CTChest.nii.gz
         
     | 
| 
      
 52 
     | 
    
         
            +
                └── 🩻 Spine.nii.gz
         
     | 
| 
      
 53 
     | 
    
         
            +
                """
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                def __init__(self, *args, **kwargs):
         
     | 
| 
      
 56 
     | 
    
         
            +
                    super().__init__(*args, **kwargs)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    self._supports_links = True
         
     | 
| 
      
 58 
     | 
    
         
            +
                    self._meta_changed = False
         
     | 
| 
       27 
59 
     | 
    
         | 
| 
       28 
60 
     | 
    
         
             
                def __str__(self) -> str:
         
     | 
| 
       29 
61 
     | 
    
         
             
                    return AvailableVolumeConverters.NII
         
     | 
| 
         @@ -34,6 +66,9 @@ class NiiConverter(VolumeConverter): 
     | 
|
| 
       34 
66 
     | 
    
         
             
                    # nrrds_dict = {}
         
     | 
| 
       35 
67 
     | 
    
         
             
                    nifti_dict = {}
         
     | 
| 
       36 
68 
     | 
    
         
             
                    nifti_dirs = {}
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    planes_detected = {p: False for p in helper.PlanePrefix.values()}
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
       37 
72 
     | 
    
         
             
                    for root, _, files in os.walk(self._input_data):
         
     | 
| 
       38 
73 
     | 
    
         
             
                        dir_name = os.path.basename(root)
         
     | 
| 
       39 
74 
     | 
    
         
             
                        nifti_dirs[dir_name] = root
         
     | 
| 
         @@ -41,13 +76,17 @@ class NiiConverter(VolumeConverter): 
     | 
|
| 
       41 
76 
     | 
    
         
             
                            continue
         
     | 
| 
       42 
77 
     | 
    
         
             
                        for file in files:
         
     | 
| 
       43 
78 
     | 
    
         
             
                            path = os.path.join(root, file)
         
     | 
| 
       44 
     | 
    
         
            -
                             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                                if  
     | 
| 
       47 
     | 
    
         
            -
                                    name = get_file_name( 
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                                     
     | 
| 
      
 79 
     | 
    
         
            +
                            if is_nifti_file(path):  # is nifti
         
     | 
| 
      
 80 
     | 
    
         
            +
                                name = get_file_name(path)
         
     | 
| 
      
 81 
     | 
    
         
            +
                                if name.endswith(".nii"):
         
     | 
| 
      
 82 
     | 
    
         
            +
                                    name = get_file_name(name)
         
     | 
| 
      
 83 
     | 
    
         
            +
                                nifti_dict[name] = path
         
     | 
| 
      
 84 
     | 
    
         
            +
                                for prefix in planes_detected.keys():
         
     | 
| 
      
 85 
     | 
    
         
            +
                                    if name.startswith(prefix):
         
     | 
| 
      
 86 
     | 
    
         
            +
                                        planes_detected[prefix] = True
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    if any(planes_detected.values()):
         
     | 
| 
      
 89 
     | 
    
         
            +
                        return False
         
     | 
| 
       51 
90 
     | 
    
         | 
| 
       52 
91 
     | 
    
         
             
                    self._items = []
         
     | 
| 
       53 
92 
     | 
    
         
             
                    skip_files = []
         
     | 
| 
         @@ -69,6 +108,39 @@ class NiiConverter(VolumeConverter): 
     | 
|
| 
       69 
108 
     | 
    
         
             
                    self._meta = ProjectMeta()
         
     | 
| 
       70 
109 
     | 
    
         
             
                    return self.items_count > 0
         
     | 
| 
       71 
110 
     | 
    
         | 
| 
      
 111 
     | 
    
         
            +
                def to_supervisely(
         
     | 
| 
      
 112 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 113 
     | 
    
         
            +
                    item: VolumeConverter.Item,
         
     | 
| 
      
 114 
     | 
    
         
            +
                    meta: ProjectMeta = None,
         
     | 
| 
      
 115 
     | 
    
         
            +
                    renamed_classes: dict = None,
         
     | 
| 
      
 116 
     | 
    
         
            +
                    renamed_tags: dict = None,
         
     | 
| 
      
 117 
     | 
    
         
            +
                ) -> VolumeAnnotation:
         
     | 
| 
      
 118 
     | 
    
         
            +
                    """Convert to Supervisely format."""
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 121 
     | 
    
         
            +
                        objs = []
         
     | 
| 
      
 122 
     | 
    
         
            +
                        spatial_figures = []
         
     | 
| 
      
 123 
     | 
    
         
            +
                        for ann_path in item.ann_data:
         
     | 
| 
      
 124 
     | 
    
         
            +
                            ann_name = get_file_name(ann_path)
         
     | 
| 
      
 125 
     | 
    
         
            +
                            if ann_name.endswith(".nii"):
         
     | 
| 
      
 126 
     | 
    
         
            +
                                ann_name = get_file_name(ann_name)
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                            ann_name = renamed_classes.get(ann_name, ann_name)
         
     | 
| 
      
 129 
     | 
    
         
            +
                            for mask, _ in helper.get_annotation_from_nii(ann_path):
         
     | 
| 
      
 130 
     | 
    
         
            +
                                obj_class = meta.get_obj_class(ann_name)
         
     | 
| 
      
 131 
     | 
    
         
            +
                                if obj_class is None:
         
     | 
| 
      
 132 
     | 
    
         
            +
                                    obj_class = ObjClass(ann_name, Mask3D)
         
     | 
| 
      
 133 
     | 
    
         
            +
                                    meta = meta.add_obj_class(obj_class)
         
     | 
| 
      
 134 
     | 
    
         
            +
                                    self._meta_changed = True
         
     | 
| 
      
 135 
     | 
    
         
            +
                                    self._meta = meta
         
     | 
| 
      
 136 
     | 
    
         
            +
                                obj = VolumeObject(obj_class, mask_3d=mask)
         
     | 
| 
      
 137 
     | 
    
         
            +
                                spatial_figures.append(obj.figure)
         
     | 
| 
      
 138 
     | 
    
         
            +
                                objs.append(obj)
         
     | 
| 
      
 139 
     | 
    
         
            +
                        return VolumeAnnotation(item.volume_meta, objects=objs, spatial_figures=spatial_figures)
         
     | 
| 
      
 140 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 141 
     | 
    
         
            +
                        logger.warning(f"Failed to convert {item.path} to Supervisely format: {e}")
         
     | 
| 
      
 142 
     | 
    
         
            +
                        return item.create_empty_annotation()
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
       72 
144 
     | 
    
         
             
                def upload_dataset(
         
     | 
| 
       73 
145 
     | 
    
         
             
                    self,
         
     | 
| 
       74 
146 
     | 
    
         
             
                    api: Api,
         
     | 
| 
         @@ -78,7 +150,7 @@ class NiiConverter(VolumeConverter): 
     | 
|
| 
       78 
150 
     | 
    
         
             
                ):
         
     | 
| 
       79 
151 
     | 
    
         
             
                    """Upload converted data to Supervisely"""
         
     | 
| 
       80 
152 
     | 
    
         | 
| 
       81 
     | 
    
         
            -
                    meta, renamed_classes,  
     | 
| 
      
 153 
     | 
    
         
            +
                    meta, renamed_classes, _ = self.merge_metas_with_conflicts(api, dataset_id)
         
     | 
| 
       82 
154 
     | 
    
         | 
| 
       83 
155 
     | 
    
         
             
                    existing_names = set([vol.name for vol in api.volume.get_list(dataset_id)])
         
     | 
| 
       84 
156 
     | 
    
         | 
| 
         @@ -91,13 +163,16 @@ class NiiConverter(VolumeConverter): 
     | 
|
| 
       91 
163 
     | 
    
         | 
| 
       92 
164 
     | 
    
         
             
                    converted_dir_name = "converted"
         
     | 
| 
       93 
165 
     | 
    
         
             
                    converted_dir = os.path.join(self._input_data, converted_dir_name)
         
     | 
| 
       94 
     | 
    
         
            -
                    meta_changed = False
         
     | 
| 
       95 
166 
     | 
    
         | 
| 
       96 
167 
     | 
    
         
             
                    for batch in batched(self._items, batch_size=batch_size):
         
     | 
| 
       97 
168 
     | 
    
         
             
                        item_names = []
         
     | 
| 
       98 
169 
     | 
    
         
             
                        item_paths = []
         
     | 
| 
       99 
170 
     | 
    
         | 
| 
       100 
171 
     | 
    
         
             
                        for item in batch:
         
     | 
| 
      
 172 
     | 
    
         
            +
                            if self._upload_as_links:
         
     | 
| 
      
 173 
     | 
    
         
            +
                                remote_path = self.remote_files_map.get(item.path)
         
     | 
| 
      
 174 
     | 
    
         
            +
                                if remote_path is not None:
         
     | 
| 
      
 175 
     | 
    
         
            +
                                    item.custom_data = {"remote_path": remote_path}
         
     | 
| 
       101 
176 
     | 
    
         
             
                            # nii_path = item.path
         
     | 
| 
       102 
177 
     | 
    
         
             
                            item.path = helper.nifti_to_nrrd(item.path, converted_dir)
         
     | 
| 
       103 
178 
     | 
    
         
             
                            ext = get_file_ext(item.path)
         
     | 
| 
         @@ -112,35 +187,28 @@ class NiiConverter(VolumeConverter): 
     | 
|
| 
       112 
187 
     | 
    
         
             
                            item_names.append(item.name)
         
     | 
| 
       113 
188 
     | 
    
         
             
                            item_paths.append(item.path)
         
     | 
| 
       114 
189 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
                             
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
      
 190 
     | 
    
         
            +
                            # upload volume
         
     | 
| 
      
 191 
     | 
    
         
            +
                            volume_np, volume_meta = read_nrrd_serie_volume_np(item.path)
         
     | 
| 
      
 192 
     | 
    
         
            +
                            progress_nrrd = tqdm_sly(
         
     | 
| 
      
 193 
     | 
    
         
            +
                                desc=f"Uploading volume '{item.name}'",
         
     | 
| 
      
 194 
     | 
    
         
            +
                                total=sum(volume_np.shape),
         
     | 
| 
      
 195 
     | 
    
         
            +
                                leave=True if progress_cb is None else False,
         
     | 
| 
      
 196 
     | 
    
         
            +
                                position=1,
         
     | 
| 
       117 
197 
     | 
    
         
             
                            )
         
     | 
| 
      
 198 
     | 
    
         
            +
                            if item.custom_data is not None:
         
     | 
| 
      
 199 
     | 
    
         
            +
                                volume_meta.update(item.custom_data)
         
     | 
| 
      
 200 
     | 
    
         
            +
                            api.volume.upload_np(dataset_id, item.name, volume_np, volume_meta, progress_nrrd)
         
     | 
| 
      
 201 
     | 
    
         
            +
                            info = api.volume.get_info_by_name(dataset_id, item.name)
         
     | 
| 
      
 202 
     | 
    
         
            +
                            item.volume_meta = info.meta
         
     | 
| 
       118 
203 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                             
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                                 
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
     | 
    
         
            -
                                     
     | 
| 
       125 
     | 
    
         
            -
                                        ann_name = get_file_name(ann_name)
         
     | 
| 
       126 
     | 
    
         
            -
                                    for mask, _ in helper.get_annotation_from_nii(ann_path):
         
     | 
| 
       127 
     | 
    
         
            -
                                        obj_class = meta.get_obj_class(ann_name)
         
     | 
| 
       128 
     | 
    
         
            -
                                        if obj_class is None:
         
     | 
| 
       129 
     | 
    
         
            -
                                            obj_class = ObjClass(ann_name, Mask3D)
         
     | 
| 
       130 
     | 
    
         
            -
                                            meta = meta.add_obj_class(obj_class)
         
     | 
| 
       131 
     | 
    
         
            -
                                            meta_changed = True
         
     | 
| 
       132 
     | 
    
         
            -
                                        obj = VolumeObject(obj_class, mask_3d=mask)
         
     | 
| 
       133 
     | 
    
         
            -
                                        spatial_figures.append(obj.figure)
         
     | 
| 
       134 
     | 
    
         
            -
                                        objs.append(obj)
         
     | 
| 
       135 
     | 
    
         
            -
                                ann = VolumeAnnotation(
         
     | 
| 
       136 
     | 
    
         
            -
                                    volume_info.meta, objects=objs, spatial_figures=spatial_figures
         
     | 
| 
       137 
     | 
    
         
            -
                                )
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                                if meta_changed:
         
     | 
| 
       140 
     | 
    
         
            -
                                    self._meta = meta
         
     | 
| 
       141 
     | 
    
         
            -
                                    _, _, _ = self.merge_metas_with_conflicts(api, dataset_id)
         
     | 
| 
      
 204 
     | 
    
         
            +
                            # create and upload annotation
         
     | 
| 
      
 205 
     | 
    
         
            +
                            if item.ann_data is not None:
         
     | 
| 
      
 206 
     | 
    
         
            +
                                ann = self.to_supervisely(item, meta, renamed_classes, None)
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                                if self._meta_changed:
         
     | 
| 
      
 209 
     | 
    
         
            +
                                    meta, renamed_classes, _ = self.merge_metas_with_conflicts(api, dataset_id)
         
     | 
| 
       142 
210 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
                                api.volume.annotation.append( 
     | 
| 
      
 211 
     | 
    
         
            +
                                api.volume.annotation.append(info.id, ann)
         
     | 
| 
       144 
212 
     | 
    
         | 
| 
       145 
213 
     | 
    
         
             
                        if log_progress:
         
     | 
| 
       146 
214 
     | 
    
         
             
                            progress_cb(len(batch))
         
     | 
| 
         @@ -4,10 +4,22 @@ from typing import Generator 
     | 
|
| 
       4 
4 
     | 
    
         
             
            import nrrd
         
     | 
| 
       5 
5 
     | 
    
         
             
            import numpy as np
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
            from supervisely.collection.str_enum import StrEnum
         
     | 
| 
       7 
8 
     | 
    
         
             
            from supervisely.geometry.mask_3d import Mask3D
         
     | 
| 
       8 
9 
     | 
    
         
             
            from supervisely.io.fs import ensure_base_path, get_file_ext, get_file_name
         
     | 
| 
       9 
10 
     | 
    
         
             
            from supervisely.volume.volume import convert_3d_nifti_to_nrrd
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
            VOLUME_NAME = "anatomic"
         
     | 
| 
      
 13 
     | 
    
         
            +
            LABEL_NAME = ["inference", "label", "annotation", "mask", "segmentation"]
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            class PlanePrefix(str, StrEnum):
         
     | 
| 
      
 17 
     | 
    
         
            +
                """Prefix for plane names."""
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                CORONAL = "cor"
         
     | 
| 
      
 20 
     | 
    
         
            +
                SAGITTAL = "sag"
         
     | 
| 
      
 21 
     | 
    
         
            +
                AXIAL = "axl"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       11 
23 
     | 
    
         | 
| 
       12 
24 
     | 
    
         
             
            def nifti_to_nrrd(nii_file_path: str, converted_dir: str) -> str:
         
     | 
| 
       13 
25 
     | 
    
         
             
                """Convert NIfTI 3D volume file to NRRD 3D volume file."""
         
     | 
| 
         @@ -25,7 +25,7 @@ supervisely/api/annotation_api.py,sha256=kuk4qwojTJxYr2iqAKbW-QhWw_DFc4TsjA2Wc2M 
     | 
|
| 
       25 
25 
     | 
    
         
             
            supervisely/api/api.py,sha256=6TczKT1t0MWlbArSW31RmeyWP04pqngfUO_NrG5FETE,66287
         
     | 
| 
       26 
26 
     | 
    
         
             
            supervisely/api/app_api.py,sha256=RsbVej8WxWVn9cNo5s3Fqd1symsCdsfOaKVBKEUapRY,71927
         
     | 
| 
       27 
27 
     | 
    
         
             
            supervisely/api/dataset_api.py,sha256=GH7prDRJKyJlTv_7_Y-RkTwJN7ED4EkXNqqmi3iIdI4,41352
         
     | 
| 
       28 
     | 
    
         
            -
            supervisely/api/file_api.py,sha256= 
     | 
| 
      
 28 
     | 
    
         
            +
            supervisely/api/file_api.py,sha256=bVWv6kf3B5n6qlB14HmUa6iUr8ara5cr-pPK8QC7XWg,92932
         
     | 
| 
       29 
29 
     | 
    
         
             
            supervisely/api/github_api.py,sha256=NIexNjEer9H5rf5sw2LEZd7C1WR-tK4t6IZzsgeAAwQ,623
         
     | 
| 
       30 
30 
     | 
    
         
             
            supervisely/api/image_annotation_tool_api.py,sha256=YcUo78jRDBJYvIjrd-Y6FJAasLta54nnxhyaGyanovA,5237
         
     | 
| 
       31 
31 
     | 
    
         
             
            supervisely/api/image_api.py,sha256=WIML_6N1qgOWBm3acexmGSWz4hAaSxlYmUtbytROaP8,192375
         
     | 
| 
         @@ -566,7 +566,7 @@ supervisely/collection/key_indexed_collection.py,sha256=x2UVlkprspWhhae9oLUzjTWB 
     | 
|
| 
       566 
566 
     | 
    
         
             
            supervisely/collection/str_enum.py,sha256=Zp29yFGvnxC6oJRYNNlXhO2lTSdsriU1wiGHj6ahEJE,1250
         
     | 
| 
       567 
567 
     | 
    
         
             
            supervisely/convert/__init__.py,sha256=ropgB1eebG2bfLoJyf2jp8Vv9UkFujaW3jVX-71ho1g,1353
         
     | 
| 
       568 
568 
     | 
    
         
             
            supervisely/convert/base_converter.py,sha256=O2SP4I_Hd0aSn8kbOUocy8orkc_-iD-TQ-z4ieUqabA,18579
         
     | 
| 
       569 
     | 
    
         
            -
            supervisely/convert/converter.py,sha256= 
     | 
| 
      
 569 
     | 
    
         
            +
            supervisely/convert/converter.py,sha256=ymhjzy75bhtpOTJSB7Xfq5tcfZjK_DMxJXIa_uuEitA,10668
         
     | 
| 
       570 
570 
     | 
    
         
             
            supervisely/convert/image/__init__.py,sha256=JEuyaBiiyiYmEUYqdn8Mog5FVXpz0H1zFubKkOOm73I,1395
         
     | 
| 
       571 
571 
     | 
    
         
             
            supervisely/convert/image/image_converter.py,sha256=8vak8ZoKTN1ye2ZmCTvCZ605-Rw1AFLIEo7bJMfnR68,10426
         
     | 
| 
       572 
572 
     | 
    
         
             
            supervisely/convert/image/image_helper.py,sha256=fdV0edQD6hVGQ8TXn2JGDzsnrAXPDMacHBQsApzOME8,3677
         
     | 
| 
         @@ -658,14 +658,15 @@ supervisely/convert/video/mot/mot_converter.py,sha256=wXbv-9Psc2uVnhzHuOt5VnRIvS 
     | 
|
| 
       658 
658 
     | 
    
         
             
            supervisely/convert/video/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       659 
659 
     | 
    
         
             
            supervisely/convert/video/sly/sly_video_converter.py,sha256=S2qif7JFxqIi9VN_ez_iBtoJXpG9W6Ky2k5Er3-DtUo,4418
         
     | 
| 
       660 
660 
     | 
    
         
             
            supervisely/convert/video/sly/sly_video_helper.py,sha256=D8PgoXpi0y3z-VEqvBLDf_gSUQ2hTL3irrfJyGhaV0Y,6758
         
     | 
| 
       661 
     | 
    
         
            -
            supervisely/convert/volume/__init__.py,sha256= 
     | 
| 
      
 661 
     | 
    
         
            +
            supervisely/convert/volume/__init__.py,sha256=RpSYjufciJT6AdhI9Oqp70b3XoFTtSkxFNexoqeOPW4,353
         
     | 
| 
       662 
662 
     | 
    
         
             
            supervisely/convert/volume/volume_converter.py,sha256=3jpt2Yn_G4FSP_vHFsJHQfYNQpT7q6ar_sRyr_xrPnA,5335
         
     | 
| 
       663 
663 
     | 
    
         
             
            supervisely/convert/volume/dicom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       664 
664 
     | 
    
         
             
            supervisely/convert/volume/dicom/dicom_converter.py,sha256=__QP8fMAaq_BdWFYh1_nAYT2gpY1WwZzdlDj39YwHhw,3195
         
     | 
| 
       665 
665 
     | 
    
         
             
            supervisely/convert/volume/dicom/dicom_helper.py,sha256=1EXmxl5Z8Xi3ZkZnfJ4EbiPCVyITSXUc0Cn_oo02pPE,1284
         
     | 
| 
       666 
666 
     | 
    
         
             
            supervisely/convert/volume/nii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       667 
     | 
    
         
            -
            supervisely/convert/volume/nii/ 
     | 
| 
       668 
     | 
    
         
            -
            supervisely/convert/volume/nii/ 
     | 
| 
      
 667 
     | 
    
         
            +
            supervisely/convert/volume/nii/nii_planes_volume_converter.py,sha256=SV1OAoel92qOvWTfcVqzee9LQypAQGQ-hJEMJ9j7nLg,4995
         
     | 
| 
      
 668 
     | 
    
         
            +
            supervisely/convert/volume/nii/nii_volume_converter.py,sha256=nVHKK7wVhMvBiX1awMoXOwt2f5MHujqVmof0v_zW5E4,8502
         
     | 
| 
      
 669 
     | 
    
         
            +
            supervisely/convert/volume/nii/nii_volume_helper.py,sha256=wy8GqjgnFJaEgKKSOgKw_QPETdiYYY5c9BBfyJE3Hqo,1417
         
     | 
| 
       669 
670 
     | 
    
         
             
            supervisely/convert/volume/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       670 
671 
     | 
    
         
             
            supervisely/convert/volume/sly/sly_volume_converter.py,sha256=XmSuxnRqxchG87b244f3h0UHvOt6IkajMquL1drWlCM,5595
         
     | 
| 
       671 
672 
     | 
    
         
             
            supervisely/convert/volume/sly/sly_volume_helper.py,sha256=gUY0GW3zDMlO2y-zQQG36uoXMrKkKz4-ErM1CDxFCxE,5620
         
     | 
| 
         @@ -1081,9 +1082,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ 
     | 
|
| 
       1081 
1082 
     | 
    
         
             
            supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
         
     | 
| 
       1082 
1083 
     | 
    
         
             
            supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
         
     | 
| 
       1083 
1084 
     | 
    
         
             
            supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
         
     | 
| 
       1084 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1085 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1086 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1087 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1088 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1089 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
      
 1085 
     | 
    
         
            +
            supervisely-6.73.325.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
         
     | 
| 
      
 1086 
     | 
    
         
            +
            supervisely-6.73.325.dist-info/METADATA,sha256=wRj0QjkNc2OGdL0AtTslp2JAlgDO8-1f3XeeUfoZfOY,33596
         
     | 
| 
      
 1087 
     | 
    
         
            +
            supervisely-6.73.325.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
         
     | 
| 
      
 1088 
     | 
    
         
            +
            supervisely-6.73.325.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
         
     | 
| 
      
 1089 
     | 
    
         
            +
            supervisely-6.73.325.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
         
     | 
| 
      
 1090 
     | 
    
         
            +
            supervisely-6.73.325.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |