supervisely 6.73.322__py3-none-any.whl → 6.73.324__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/_utils.py +17 -6
- supervisely/annotation/annotation.py +1 -1
- supervisely/api/file_api.py +87 -1
- supervisely/app/fastapi/utils.py +14 -0
- supervisely/convert/base_converter.py +1 -0
- supervisely/convert/volume/__init__.py +1 -0
- supervisely/convert/volume/nii/__init__.py +0 -0
- supervisely/convert/volume/nii/nii_volume_converter.py +151 -0
- supervisely/convert/volume/nii/nii_volume_helper.py +38 -0
- supervisely/nn/training/train_app.py +19 -24
- supervisely/project/download.py +29 -1
- supervisely/volume/volume.py +43 -0
- {supervisely-6.73.322.dist-info → supervisely-6.73.324.dist-info}/METADATA +1 -1
- {supervisely-6.73.322.dist-info → supervisely-6.73.324.dist-info}/RECORD +18 -15
- {supervisely-6.73.322.dist-info → supervisely-6.73.324.dist-info}/LICENSE +0 -0
- {supervisely-6.73.322.dist-info → supervisely-6.73.324.dist-info}/WHEEL +0 -0
- {supervisely-6.73.322.dist-info → supervisely-6.73.324.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.322.dist-info → supervisely-6.73.324.dist-info}/top_level.txt +0 -0
supervisely/_utils.py
CHANGED
|
@@ -471,9 +471,20 @@ def get_or_create_event_loop() -> asyncio.AbstractEventLoop:
|
|
|
471
471
|
return loop
|
|
472
472
|
|
|
473
473
|
|
|
474
|
-
def
|
|
474
|
+
def run_coroutine(coroutine):
|
|
475
475
|
"""
|
|
476
|
-
|
|
476
|
+
Runs an asynchronous coroutine in a synchronous context and waits for its result.
|
|
477
|
+
|
|
478
|
+
This function checks if an event loop is already running:
|
|
479
|
+
- If a loop is running, it schedules the coroutine using `asyncio.run_coroutine_threadsafe()`
|
|
480
|
+
and waits for the result.
|
|
481
|
+
- If no loop is running, it creates one and executes the coroutine with `run_until_complete()`.
|
|
482
|
+
|
|
483
|
+
This ensures compatibility with both synchronous and asynchronous environments
|
|
484
|
+
without creating unnecessary event loops.
|
|
485
|
+
|
|
486
|
+
⚠️ Note: This method is preferable when working with `asyncio` objects like `Semaphore`,
|
|
487
|
+
since it avoids issues with mismatched event loops.
|
|
477
488
|
|
|
478
489
|
:param coro: Asynchronous function.
|
|
479
490
|
:type coro: Coroutine
|
|
@@ -484,13 +495,13 @@ def sync_call(coro):
|
|
|
484
495
|
|
|
485
496
|
.. code-block:: python
|
|
486
497
|
|
|
487
|
-
from supervisely.
|
|
498
|
+
from supervisely._utils import run_coroutine
|
|
488
499
|
|
|
489
500
|
async def async_function():
|
|
490
501
|
await asyncio.sleep(1)
|
|
491
502
|
return "Hello, World!"
|
|
492
503
|
coro = async_function()
|
|
493
|
-
result =
|
|
504
|
+
result = run_coroutine(coro)
|
|
494
505
|
print(result)
|
|
495
506
|
# Output: Hello, World!
|
|
496
507
|
"""
|
|
@@ -498,10 +509,10 @@ def sync_call(coro):
|
|
|
498
509
|
loop = get_or_create_event_loop()
|
|
499
510
|
|
|
500
511
|
if loop.is_running():
|
|
501
|
-
future = asyncio.run_coroutine_threadsafe(
|
|
512
|
+
future = asyncio.run_coroutine_threadsafe(coroutine, loop=loop)
|
|
502
513
|
return future.result()
|
|
503
514
|
else:
|
|
504
|
-
return loop.run_until_complete(
|
|
515
|
+
return loop.run_until_complete(coroutine)
|
|
505
516
|
|
|
506
517
|
|
|
507
518
|
def get_filename_from_headers(url):
|
|
@@ -404,7 +404,7 @@ class Annotation:
|
|
|
404
404
|
f"Failed to deserialize one of the label from JSON format annotation: \n{repr(e)}"
|
|
405
405
|
)
|
|
406
406
|
|
|
407
|
-
custom_data = data.get(AnnotationJsonFields.CUSTOM_DATA, {})
|
|
407
|
+
custom_data = data.get(AnnotationJsonFields.CUSTOM_DATA, {}) or {}
|
|
408
408
|
prob_labels = None
|
|
409
409
|
if (
|
|
410
410
|
AnnotationJsonFields.PROBABILITY_LABELS in custom_data
|
supervisely/api/file_api.py
CHANGED
|
@@ -23,7 +23,7 @@ from typing_extensions import Literal
|
|
|
23
23
|
|
|
24
24
|
import supervisely.io.env as env
|
|
25
25
|
import supervisely.io.fs as sly_fs
|
|
26
|
-
from supervisely._utils import batched, rand_str
|
|
26
|
+
from supervisely._utils import batched, rand_str, run_coroutine
|
|
27
27
|
from supervisely.api.module_api import ApiField, ModuleApiBase
|
|
28
28
|
from supervisely.io.fs import (
|
|
29
29
|
ensure_base_path,
|
|
@@ -2302,3 +2302,89 @@ class FileApi(ModuleApiBase):
|
|
|
2302
2302
|
else:
|
|
2303
2303
|
raise e
|
|
2304
2304
|
return res_remote_dir
|
|
2305
|
+
|
|
2306
|
+
def upload_directory_fast(
|
|
2307
|
+
self,
|
|
2308
|
+
team_id: int,
|
|
2309
|
+
local_dir: str,
|
|
2310
|
+
remote_dir: str,
|
|
2311
|
+
change_name_if_conflict: Optional[bool] = True,
|
|
2312
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
2313
|
+
replace_if_conflict: Optional[bool] = False,
|
|
2314
|
+
enable_fallback: Optional[bool] = True,
|
|
2315
|
+
) -> str:
|
|
2316
|
+
"""
|
|
2317
|
+
Upload Directory to Team Files from local path in fast mode.
|
|
2318
|
+
Files are uploaded asynchronously. If an error occurs, the method will fallback to synchronous upload.
|
|
2319
|
+
|
|
2320
|
+
:param team_id: Team ID in Supervisely.
|
|
2321
|
+
:type team_id: int
|
|
2322
|
+
:param local_dir: Path to local Directory.
|
|
2323
|
+
:type local_dir: str
|
|
2324
|
+
:param remote_dir: Path to Directory in Team Files.
|
|
2325
|
+
:type remote_dir: str
|
|
2326
|
+
:param change_name_if_conflict: Checks if given name already exists and adds suffix to the end of the name.
|
|
2327
|
+
:type change_name_if_conflict: bool, optional
|
|
2328
|
+
:param progress_cb: Function for tracking download progress in bytes.
|
|
2329
|
+
:type progress_cb: Progress, optional
|
|
2330
|
+
:param replace_if_conflict: If True, replace existing dir.
|
|
2331
|
+
:type replace_if_conflict: bool, optional
|
|
2332
|
+
:param enable_fallback: If True, the method will fallback to synchronous upload if an error occurs.
|
|
2333
|
+
:type enable_fallback: bool, optional
|
|
2334
|
+
:return: Path to Directory in Team Files
|
|
2335
|
+
:rtype: :class:`str`
|
|
2336
|
+
"""
|
|
2337
|
+
coroutine = self.upload_directory_async(
|
|
2338
|
+
team_id=team_id,
|
|
2339
|
+
local_dir=local_dir,
|
|
2340
|
+
remote_dir=remote_dir,
|
|
2341
|
+
change_name_if_conflict=change_name_if_conflict,
|
|
2342
|
+
progress_size_cb=progress_cb,
|
|
2343
|
+
replace_if_conflict=replace_if_conflict,
|
|
2344
|
+
enable_fallback=enable_fallback,
|
|
2345
|
+
)
|
|
2346
|
+
return run_coroutine(coroutine)
|
|
2347
|
+
|
|
2348
|
+
def upload_bulk_fast(
|
|
2349
|
+
self,
|
|
2350
|
+
team_id: int,
|
|
2351
|
+
src_paths: List[str],
|
|
2352
|
+
dst_paths: List[str],
|
|
2353
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
2354
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
2355
|
+
progress_cb_type: Literal["number", "size"] = "size",
|
|
2356
|
+
enable_fallback: Optional[bool] = True,
|
|
2357
|
+
) -> None:
|
|
2358
|
+
"""
|
|
2359
|
+
Upload multiple files from local paths to Team Files in fast mode.
|
|
2360
|
+
Files are uploaded asynchronously. If an error occurs, the method will fallback to synchronous upload.
|
|
2361
|
+
|
|
2362
|
+
:param team_id: Team ID in Supervisely.
|
|
2363
|
+
:type team_id: int
|
|
2364
|
+
:param src_paths: List of local paths to files.
|
|
2365
|
+
:type src_paths: List[str]
|
|
2366
|
+
:param dst_paths: List of paths to save files in Team Files.
|
|
2367
|
+
:type dst_paths: List[str]
|
|
2368
|
+
:param semaphore: Semaphore for limiting the number of simultaneous uploads.
|
|
2369
|
+
:type semaphore: asyncio.Semaphore, optional
|
|
2370
|
+
:param progress_cb: Function for tracking download progress.
|
|
2371
|
+
:type progress_cb: tqdm or callable, optional
|
|
2372
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "size".
|
|
2373
|
+
"size" is used to track the number of transferred bytes.
|
|
2374
|
+
"number" is used to track the number of transferred files.
|
|
2375
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
2376
|
+
:param enable_fallback: If True, the method will fallback to synchronous upload if an error occurs.
|
|
2377
|
+
:type enable_fallback: bool, optional
|
|
2378
|
+
:return: None
|
|
2379
|
+
:rtype: :class:`NoneType`
|
|
2380
|
+
"""
|
|
2381
|
+
coroutine = self.upload_bulk_async(
|
|
2382
|
+
team_id=team_id,
|
|
2383
|
+
src_paths=src_paths,
|
|
2384
|
+
dst_paths=dst_paths,
|
|
2385
|
+
semaphore=semaphore,
|
|
2386
|
+
progress_cb=progress_cb,
|
|
2387
|
+
progress_cb_type=progress_cb_type,
|
|
2388
|
+
enable_fallback=enable_fallback,
|
|
2389
|
+
)
|
|
2390
|
+
return run_coroutine(coroutine)
|
supervisely/app/fastapi/utils.py
CHANGED
|
@@ -3,6 +3,20 @@ import concurrent.futures
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def run_sync(coroutine):
|
|
6
|
+
"""
|
|
7
|
+
Runs an asynchronous coroutine in a separate thread and waits for its result.
|
|
8
|
+
It is useful for running async functions in a synchronous
|
|
9
|
+
environment.
|
|
10
|
+
|
|
11
|
+
This method creates a new thread using ThreadPoolExecutor and executes the coroutine
|
|
12
|
+
inside a new event loop.
|
|
13
|
+
|
|
14
|
+
⚠️ Note: This function creates a new event loop every time it is called,
|
|
15
|
+
which can cause issues when using objects tied to a specific loop (e.g., asyncio.Semaphore).
|
|
16
|
+
|
|
17
|
+
:param coroutine: coroutine to run
|
|
18
|
+
:return: result of coroutine
|
|
19
|
+
"""
|
|
6
20
|
try:
|
|
7
21
|
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
|
|
8
22
|
result = executor.submit(
|
|
File without changes
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import magic
|
|
5
|
+
|
|
6
|
+
from supervisely import ProjectMeta, generate_free_name, logger
|
|
7
|
+
from supervisely._utils import batched, is_development
|
|
8
|
+
from supervisely.annotation.obj_class import ObjClass
|
|
9
|
+
from supervisely.annotation.obj_class_collection import ObjClassCollection
|
|
10
|
+
from supervisely.api.api import Api
|
|
11
|
+
from supervisely.convert.base_converter import AvailableVolumeConverters
|
|
12
|
+
from supervisely.convert.volume.nii import nii_volume_helper as helper
|
|
13
|
+
from supervisely.convert.volume.volume_converter import VolumeConverter
|
|
14
|
+
from supervisely.geometry.mask_3d import Mask3D
|
|
15
|
+
from supervisely.io.fs import (
|
|
16
|
+
get_file_ext,
|
|
17
|
+
get_file_name,
|
|
18
|
+
get_file_name_with_ext,
|
|
19
|
+
list_files,
|
|
20
|
+
)
|
|
21
|
+
from supervisely.volume.volume import is_nifti_file
|
|
22
|
+
from supervisely.volume_annotation.volume_annotation import VolumeAnnotation
|
|
23
|
+
from supervisely.volume_annotation.volume_object import VolumeObject
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class NiiConverter(VolumeConverter):
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
return AvailableVolumeConverters.NII
|
|
30
|
+
|
|
31
|
+
def validate_format(self) -> bool:
|
|
32
|
+
# create Items
|
|
33
|
+
converted_dir_name = "converted"
|
|
34
|
+
# nrrds_dict = {}
|
|
35
|
+
nifti_dict = {}
|
|
36
|
+
nifti_dirs = {}
|
|
37
|
+
for root, _, files in os.walk(self._input_data):
|
|
38
|
+
dir_name = os.path.basename(root)
|
|
39
|
+
nifti_dirs[dir_name] = root
|
|
40
|
+
if converted_dir_name in root:
|
|
41
|
+
continue
|
|
42
|
+
for file in files:
|
|
43
|
+
path = os.path.join(root, file)
|
|
44
|
+
mime = magic.from_file(path, mime=True)
|
|
45
|
+
if mime == "application/gzip" or mime == "application/octet-stream":
|
|
46
|
+
if is_nifti_file(path): # is nifti
|
|
47
|
+
name = get_file_name(path)
|
|
48
|
+
if name.endswith(".nii"):
|
|
49
|
+
name = get_file_name(name)
|
|
50
|
+
nifti_dict[name] = path
|
|
51
|
+
|
|
52
|
+
self._items = []
|
|
53
|
+
skip_files = []
|
|
54
|
+
for name, nrrd_path in nifti_dict.items():
|
|
55
|
+
if name in nifti_dirs:
|
|
56
|
+
item = self.Item(item_path=nrrd_path)
|
|
57
|
+
ann_dir = nifti_dirs[name]
|
|
58
|
+
item.ann_data = list_files(ann_dir, [".nii", ".nii.gz", ".gz"], None, True)
|
|
59
|
+
self._items.append(item)
|
|
60
|
+
skip_files.extend(item.ann_data)
|
|
61
|
+
skip_files.append(nrrd_path)
|
|
62
|
+
|
|
63
|
+
for name, nrrd_path in nifti_dict.items():
|
|
64
|
+
if nrrd_path in skip_files:
|
|
65
|
+
continue
|
|
66
|
+
item = self.Item(item_path=nrrd_path)
|
|
67
|
+
self._items.append(item)
|
|
68
|
+
|
|
69
|
+
self._meta = ProjectMeta()
|
|
70
|
+
return self.items_count > 0
|
|
71
|
+
|
|
72
|
+
def upload_dataset(
|
|
73
|
+
self,
|
|
74
|
+
api: Api,
|
|
75
|
+
dataset_id: int,
|
|
76
|
+
batch_size: int = 1,
|
|
77
|
+
log_progress=True,
|
|
78
|
+
):
|
|
79
|
+
"""Upload converted data to Supervisely"""
|
|
80
|
+
|
|
81
|
+
meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
|
|
82
|
+
|
|
83
|
+
existing_names = set([vol.name for vol in api.volume.get_list(dataset_id)])
|
|
84
|
+
|
|
85
|
+
if log_progress:
|
|
86
|
+
progress, progress_cb = self.get_progress(
|
|
87
|
+
self.items_count, "Converting and uploading volumes..."
|
|
88
|
+
)
|
|
89
|
+
else:
|
|
90
|
+
progress_cb = None
|
|
91
|
+
|
|
92
|
+
converted_dir_name = "converted"
|
|
93
|
+
converted_dir = os.path.join(self._input_data, converted_dir_name)
|
|
94
|
+
meta_changed = False
|
|
95
|
+
|
|
96
|
+
for batch in batched(self._items, batch_size=batch_size):
|
|
97
|
+
item_names = []
|
|
98
|
+
item_paths = []
|
|
99
|
+
|
|
100
|
+
for item in batch:
|
|
101
|
+
# nii_path = item.path
|
|
102
|
+
item.path = helper.nifti_to_nrrd(item.path, converted_dir)
|
|
103
|
+
ext = get_file_ext(item.path)
|
|
104
|
+
if ext.lower() != ext:
|
|
105
|
+
new_volume_path = Path(item.path).with_suffix(ext.lower()).as_posix()
|
|
106
|
+
os.rename(item.path, new_volume_path)
|
|
107
|
+
item.path = new_volume_path
|
|
108
|
+
item.name = get_file_name_with_ext(item.path)
|
|
109
|
+
item.name = generate_free_name(
|
|
110
|
+
existing_names, item.name, with_ext=True, extend_used_names=True
|
|
111
|
+
)
|
|
112
|
+
item_names.append(item.name)
|
|
113
|
+
item_paths.append(item.path)
|
|
114
|
+
|
|
115
|
+
volume_info = api.volume.upload_nrrd_serie_path(
|
|
116
|
+
dataset_id, name=item.name, path=item.path
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if isinstance(item.ann_data, list) and len(item.ann_data) > 0:
|
|
120
|
+
objs = []
|
|
121
|
+
spatial_figures = []
|
|
122
|
+
for ann_path in item.ann_data:
|
|
123
|
+
ann_name = get_file_name(ann_path)
|
|
124
|
+
if ann_name.endswith(".nii"):
|
|
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)
|
|
142
|
+
|
|
143
|
+
api.volume.annotation.append(volume_info.id, ann)
|
|
144
|
+
|
|
145
|
+
if log_progress:
|
|
146
|
+
progress_cb(len(batch))
|
|
147
|
+
|
|
148
|
+
if log_progress:
|
|
149
|
+
if is_development():
|
|
150
|
+
progress.close()
|
|
151
|
+
logger.info(f"Dataset ID:{dataset_id} has been successfully uploaded.")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Generator
|
|
3
|
+
|
|
4
|
+
import nrrd
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from supervisely.geometry.mask_3d import Mask3D
|
|
8
|
+
from supervisely.io.fs import ensure_base_path, get_file_ext, get_file_name
|
|
9
|
+
from supervisely.volume.volume import convert_3d_nifti_to_nrrd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def nifti_to_nrrd(nii_file_path: str, converted_dir: str) -> str:
|
|
13
|
+
"""Convert NIfTI 3D volume file to NRRD 3D volume file."""
|
|
14
|
+
|
|
15
|
+
output_name = get_file_name(nii_file_path)
|
|
16
|
+
if get_file_ext(output_name) == ".nii":
|
|
17
|
+
output_name = get_file_name(output_name)
|
|
18
|
+
|
|
19
|
+
data, header = convert_3d_nifti_to_nrrd(nii_file_path)
|
|
20
|
+
|
|
21
|
+
nrrd_file_path = os.path.join(converted_dir, f"{output_name}.nrrd")
|
|
22
|
+
ensure_base_path(nrrd_file_path)
|
|
23
|
+
|
|
24
|
+
nrrd.write(nrrd_file_path, data, header)
|
|
25
|
+
return nrrd_file_path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_annotation_from_nii(path: str) -> Generator[Mask3D, None, None]:
|
|
29
|
+
"""Get annotation from NIfTI 3D volume file."""
|
|
30
|
+
|
|
31
|
+
data, _ = convert_3d_nifti_to_nrrd(path)
|
|
32
|
+
unique_classes = np.unique(data)
|
|
33
|
+
|
|
34
|
+
for class_id in unique_classes:
|
|
35
|
+
if class_id == 0:
|
|
36
|
+
continue
|
|
37
|
+
mask = Mask3D(data == class_id)
|
|
38
|
+
yield mask, class_id
|
|
@@ -39,7 +39,7 @@ from supervisely import (
|
|
|
39
39
|
is_production,
|
|
40
40
|
logger,
|
|
41
41
|
)
|
|
42
|
-
from supervisely._utils import abs_url, get_filename_from_headers
|
|
42
|
+
from supervisely._utils import abs_url, get_filename_from_headers
|
|
43
43
|
from supervisely.api.file_api import FileInfo
|
|
44
44
|
from supervisely.app import get_synced_data_dir
|
|
45
45
|
from supervisely.app.widgets import Progress
|
|
@@ -60,7 +60,7 @@ from supervisely.nn.utils import ModelSource
|
|
|
60
60
|
from supervisely.output import set_directory
|
|
61
61
|
from supervisely.project.download import (
|
|
62
62
|
copy_from_cache,
|
|
63
|
-
|
|
63
|
+
download_fast,
|
|
64
64
|
download_to_cache,
|
|
65
65
|
get_cache_size,
|
|
66
66
|
is_cached,
|
|
@@ -806,7 +806,7 @@ class TrainApp:
|
|
|
806
806
|
with self.progress_bar_main(message="Downloading input data", total=total_images) as pbar:
|
|
807
807
|
logger.debug("Downloading project data without cache")
|
|
808
808
|
self.progress_bar_main.show()
|
|
809
|
-
|
|
809
|
+
download_fast(
|
|
810
810
|
api=self._api,
|
|
811
811
|
project_id=self.project_id,
|
|
812
812
|
dest_dir=self.project_dir,
|
|
@@ -1619,14 +1619,13 @@ class TrainApp:
|
|
|
1619
1619
|
unit_scale=True,
|
|
1620
1620
|
) as upload_artifacts_pbar:
|
|
1621
1621
|
self.progress_bar_main.show()
|
|
1622
|
-
remote_dir =
|
|
1623
|
-
self.
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
progress_size_cb=upload_artifacts_pbar.update,
|
|
1628
|
-
)
|
|
1622
|
+
remote_dir = self._api.file.upload_directory_fast(
|
|
1623
|
+
team_id=self.team_id,
|
|
1624
|
+
local_dir=local_demo_dir,
|
|
1625
|
+
remote_dir=remote_demo_dir,
|
|
1626
|
+
progress_cb=upload_artifacts_pbar.update,
|
|
1629
1627
|
)
|
|
1628
|
+
|
|
1630
1629
|
self.progress_bar_main.hide()
|
|
1631
1630
|
|
|
1632
1631
|
def _get_train_val_splits_for_app_state(self) -> Dict:
|
|
@@ -1733,13 +1732,11 @@ class TrainApp:
|
|
|
1733
1732
|
unit_scale=True,
|
|
1734
1733
|
) as upload_artifacts_pbar:
|
|
1735
1734
|
self.progress_bar_main.show()
|
|
1736
|
-
remote_dir =
|
|
1737
|
-
self.
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
progress_size_cb=upload_artifacts_pbar.update,
|
|
1742
|
-
)
|
|
1735
|
+
remote_dir = self._api.file.upload_directory_fast(
|
|
1736
|
+
team_id=self.team_id,
|
|
1737
|
+
local_dir=self.output_dir,
|
|
1738
|
+
remote_dir=remote_artifacts_dir,
|
|
1739
|
+
progress_cb=upload_artifacts_pbar.update,
|
|
1743
1740
|
)
|
|
1744
1741
|
self.progress_bar_main.hide()
|
|
1745
1742
|
|
|
@@ -2524,13 +2521,11 @@ class TrainApp:
|
|
|
2524
2521
|
logger.debug(f"Uploading {len(export_weights)} export weights of size {size} bytes")
|
|
2525
2522
|
logger.debug(f"Destination paths: {file_dest_paths}")
|
|
2526
2523
|
self.progress_bar_main.show()
|
|
2527
|
-
|
|
2528
|
-
self.
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
progress_cb=export_upload_main_pbar.update,
|
|
2533
|
-
)
|
|
2524
|
+
self._api.file.upload_bulk_fast(
|
|
2525
|
+
team_id=self.team_id,
|
|
2526
|
+
src_paths=export_weights.values(),
|
|
2527
|
+
dst_paths=file_dest_paths,
|
|
2528
|
+
progress_cb=export_upload_main_pbar.update,
|
|
2534
2529
|
)
|
|
2535
2530
|
|
|
2536
2531
|
self.progress_bar_main.hide()
|
supervisely/project/download.py
CHANGED
|
@@ -281,6 +281,34 @@ def download_async_or_sync(
|
|
|
281
281
|
)
|
|
282
282
|
|
|
283
283
|
|
|
284
|
+
def download_fast(
|
|
285
|
+
api: Api,
|
|
286
|
+
project_id: int,
|
|
287
|
+
dest_dir: str,
|
|
288
|
+
dataset_ids: Optional[List[int]] = None,
|
|
289
|
+
log_progress: bool = True,
|
|
290
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
291
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
292
|
+
**kwargs,
|
|
293
|
+
) -> None:
|
|
294
|
+
"""
|
|
295
|
+
Download project in a fast mode.
|
|
296
|
+
Items are downloaded asynchronously. If an error occurs, the method will fallback to synchronous download.
|
|
297
|
+
Automatically detects project type.
|
|
298
|
+
You can pass :class:`ProjectInfo` as `project_info` kwarg to avoid additional API requests.
|
|
299
|
+
"""
|
|
300
|
+
download_async_or_sync(
|
|
301
|
+
api=api,
|
|
302
|
+
project_id=project_id,
|
|
303
|
+
dest_dir=dest_dir,
|
|
304
|
+
dataset_ids=dataset_ids,
|
|
305
|
+
log_progress=log_progress,
|
|
306
|
+
progress_cb=progress_cb,
|
|
307
|
+
semaphore=semaphore,
|
|
308
|
+
**kwargs,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
284
312
|
def _get_cache_dir(project_id: int, dataset_path: str = None) -> str:
|
|
285
313
|
p = os.path.join(apps_cache_dir(), str(project_id))
|
|
286
314
|
if dataset_path is not None:
|
|
@@ -468,7 +496,7 @@ def _download_project_to_cache(
|
|
|
468
496
|
if len(dataset_infos) == 0:
|
|
469
497
|
logger.debug("No datasets to download")
|
|
470
498
|
return
|
|
471
|
-
|
|
499
|
+
download_fast(
|
|
472
500
|
api=api,
|
|
473
501
|
project_id=project_id,
|
|
474
502
|
dest_dir=cached_project_dir,
|
supervisely/volume/volume.py
CHANGED
|
@@ -799,6 +799,49 @@ def convert_nifti_to_nrrd(path: str) -> Tuple[np.ndarray, dict]:
|
|
|
799
799
|
}
|
|
800
800
|
return data, header
|
|
801
801
|
|
|
802
|
+
def convert_3d_nifti_to_nrrd(path: str) -> Tuple[np.ndarray, dict]:
|
|
803
|
+
"""Convert 3D NIFTI volume to NRRD format.
|
|
804
|
+
Volume automatically reordered to RAS orientation as closest to canonical.
|
|
805
|
+
|
|
806
|
+
:param path: Path to NIFTI volume file.
|
|
807
|
+
:type path: str
|
|
808
|
+
:return: Volume data in NumPy array format and dictionary with metadata (NRRD header).
|
|
809
|
+
:rtype: Tuple[np.ndarray, dict]
|
|
810
|
+
:Usage example:
|
|
811
|
+
|
|
812
|
+
.. code-block:: python
|
|
813
|
+
|
|
814
|
+
import supervisely as sly
|
|
815
|
+
|
|
816
|
+
path = "/home/admin/work/volumes/vol_01.nii"
|
|
817
|
+
data, header = sly.volume.convert_nifti_to_nrrd(path)
|
|
818
|
+
"""
|
|
819
|
+
|
|
820
|
+
import nibabel as nib # pylint: disable=import-error
|
|
821
|
+
|
|
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
|
+
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
|
+
}
|
|
843
|
+
return data, header
|
|
844
|
+
|
|
802
845
|
|
|
803
846
|
def is_nifti_file(path: str) -> bool:
|
|
804
847
|
"""Check if the file is a NIFTI file.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
supervisely/README.md,sha256=XM-DiMC6To3I9RjQZ0c61905EFRR_jnCUx2q3uNR-X8,3331
|
|
2
2
|
supervisely/__init__.py,sha256=mtgVKiRSlnRU7yKG0Re130mBL10wCzsNfOfi-w-Kj4c,10833
|
|
3
|
-
supervisely/_utils.py,sha256=
|
|
3
|
+
supervisely/_utils.py,sha256=KWfbw8XFfV2uxLzhuku_J0UQKpG-D80Hp6UOQD316P8,17460
|
|
4
4
|
supervisely/function_wrapper.py,sha256=R5YajTQ0GnRp2vtjwfC9hINkzQc0JiyGsu8TER373xY,1912
|
|
5
5
|
supervisely/sly_logger.py,sha256=z92Vu5hmC0GgTIJO1n6kPDayRW9__8ix8hL6poDZj-Y,6274
|
|
6
6
|
supervisely/tiny_timer.py,sha256=hkpe_7FE6bsKL79blSs7WBaktuPavEVu67IpEPrfmjE,183
|
|
7
7
|
supervisely/annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
supervisely/annotation/annotation.py,sha256=
|
|
8
|
+
supervisely/annotation/annotation.py,sha256=x1RizD9DPiwk14Mf8xGvuwPdzx_zI5Zx1CVvmCy_sII,114665
|
|
9
9
|
supervisely/annotation/annotation_transforms.py,sha256=TlVy_gUbM-XH6GbLpZPrAi6pMIGTr7Ow02iSKOSTa-I,9582
|
|
10
10
|
supervisely/annotation/json_geometries_map.py,sha256=nL6AmMhFy02fw9ryBm75plKyOkDh61QdOToSuLAcz_Q,1659
|
|
11
11
|
supervisely/annotation/label.py,sha256=NpHZ5o2H6dI4KiII22o2HpiLXG1yekh-bEy8WvI2Ljg,37498
|
|
@@ -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=S2xZAy36YzWA1R41SIlLXX9JwSf9ax18AIAIQGgHGlA,92801
|
|
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
|
|
@@ -95,7 +95,7 @@ supervisely/app/fastapi/offline.py,sha256=CwMMkJ1frD6wiZS-SEoNDtQ1UJcJe1Ob6ohE3r
|
|
|
95
95
|
supervisely/app/fastapi/request.py,sha256=NU7rKmxJ1pfkDZ7_yHckRcRAueJRQIqCor11UO2OHr8,766
|
|
96
96
|
supervisely/app/fastapi/subapp.py,sha256=5lMfFLYBfHzE1OmITHsogB9hScyTJFjGV45AKY67Hkg,45647
|
|
97
97
|
supervisely/app/fastapi/templating.py,sha256=JOAW8U-14GD47E286mzFi3mZSPbm_csJGqtXWLRM4rc,2929
|
|
98
|
-
supervisely/app/fastapi/utils.py,sha256=
|
|
98
|
+
supervisely/app/fastapi/utils.py,sha256=t_UquzlFrdkKtAJmH6eJ279pE8Aa3BaIu4XjX-SEaIE,946
|
|
99
99
|
supervisely/app/fastapi/websocket.py,sha256=TlRSPOAhRItTv1HGvdukK1ZvhRjMUxRa-lJlsRR9rJw,1308
|
|
100
100
|
supervisely/app/v1/__init__.py,sha256=OdU0PYv6hLwahYoyaLFO8m3cbJSchvPbqxuG1N3T734,848
|
|
101
101
|
supervisely/app/v1/app_config.md,sha256=-8GKbiQoX25RhEj3EDJ7TxiYuFw5wL2TO3qV5AJLZTs,2536
|
|
@@ -565,7 +565,7 @@ supervisely/collection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
565
565
|
supervisely/collection/key_indexed_collection.py,sha256=x2UVlkprspWhhae9oLUzjTWBoIouiWY9UQSS_MozfH0,37643
|
|
566
566
|
supervisely/collection/str_enum.py,sha256=Zp29yFGvnxC6oJRYNNlXhO2lTSdsriU1wiGHj6ahEJE,1250
|
|
567
567
|
supervisely/convert/__init__.py,sha256=ropgB1eebG2bfLoJyf2jp8Vv9UkFujaW3jVX-71ho1g,1353
|
|
568
|
-
supervisely/convert/base_converter.py,sha256=
|
|
568
|
+
supervisely/convert/base_converter.py,sha256=O2SP4I_Hd0aSn8kbOUocy8orkc_-iD-TQ-z4ieUqabA,18579
|
|
569
569
|
supervisely/convert/converter.py,sha256=tWxTDfFv7hwzQhUQrBxzfr6WP8FUGFX_ewg5T2HbUYo,8959
|
|
570
570
|
supervisely/convert/image/__init__.py,sha256=JEuyaBiiyiYmEUYqdn8Mog5FVXpz0H1zFubKkOOm73I,1395
|
|
571
571
|
supervisely/convert/image/image_converter.py,sha256=8vak8ZoKTN1ye2ZmCTvCZ605-Rw1AFLIEo7bJMfnR68,10426
|
|
@@ -658,11 +658,14 @@ 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=NjVfOa9uH1BdYvB-RynW6L28x0f_tqL9p7tHSIQ6Sso,245
|
|
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
|
+
supervisely/convert/volume/nii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
667
|
+
supervisely/convert/volume/nii/nii_volume_converter.py,sha256=kI2JmeFuLfLWgYGCEozoaka1QH4TocnfgyN0em6maa0,5946
|
|
668
|
+
supervisely/convert/volume/nii/nii_volume_helper.py,sha256=kzh20fsdeI8efA0vawW0M6Wh48nMlCLzHBQFuSNVFmc,1136
|
|
666
669
|
supervisely/convert/volume/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
667
670
|
supervisely/convert/volume/sly/sly_volume_converter.py,sha256=XmSuxnRqxchG87b244f3h0UHvOt6IkajMquL1drWlCM,5595
|
|
668
671
|
supervisely/convert/volume/sly/sly_volume_helper.py,sha256=gUY0GW3zDMlO2y-zQQG36uoXMrKkKz4-ErM1CDxFCxE,5620
|
|
@@ -976,7 +979,7 @@ supervisely/nn/tracker/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
976
979
|
supervisely/nn/tracker/utils/gmc.py,sha256=3JX8979H3NA-YHNaRQyj9Z-xb9qtyMittPEjGw8y2Jo,11557
|
|
977
980
|
supervisely/nn/tracker/utils/kalman_filter.py,sha256=eSFmCjM0mikHCAFvj-KCVzw-0Jxpoc3Cfc2NWEjJC1Q,17268
|
|
978
981
|
supervisely/nn/training/__init__.py,sha256=gY4PCykJ-42MWKsqb9kl-skemKa8yB6t_fb5kzqR66U,111
|
|
979
|
-
supervisely/nn/training/train_app.py,sha256=
|
|
982
|
+
supervisely/nn/training/train_app.py,sha256=lFvYxt2Zsd7FrqfeA2C-eNX0tQVLtz3V8om3DwvNFtM,104720
|
|
980
983
|
supervisely/nn/training/gui/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
981
984
|
supervisely/nn/training/gui/classes_selector.py,sha256=8UgzA4aogOAr1s42smwEcDbgaBj_i0JLhjwlZ9bFdIA,3772
|
|
982
985
|
supervisely/nn/training/gui/gui.py,sha256=CnT_QhihrxdSHKybpI0pXhPLwCaXEana_qdn0DhXByg,25558
|
|
@@ -1013,7 +1016,7 @@ supervisely/pointcloud_episodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
|
1013
1016
|
supervisely/pointcloud_episodes/pointcloud_episodes.py,sha256=cRXdtw7bMsbsdVQjxfWxFSESrO-LGiqqsZyyExl2Mbg,3430
|
|
1014
1017
|
supervisely/project/__init__.py,sha256=hlzdj9Pgy53Q3qdP8LMtGTChvZHQuuShdtui2eRUQeE,2601
|
|
1015
1018
|
supervisely/project/data_version.py,sha256=6vOz5ovBeCIiMAKUG7lGQ5IXvQnU1GbcnrWxdOvaVlo,19311
|
|
1016
|
-
supervisely/project/download.py,sha256=
|
|
1019
|
+
supervisely/project/download.py,sha256=GQFYN3KCdM_egXDzoyZrzl6Yeg2QshYQNFNlKi8Nh8A,25471
|
|
1017
1020
|
supervisely/project/pointcloud_episode_project.py,sha256=yiWdNBQiI6f1O9sr1pg8JHW6O-w3XUB1rikJNn3Oung,41866
|
|
1018
1021
|
supervisely/project/pointcloud_project.py,sha256=Kx1Vaes-krwG3BiRRtHRLQxb9G5m5bTHPN9IzRqmNWo,49399
|
|
1019
1022
|
supervisely/project/project.py,sha256=nKnnYVrSx_MWh5G_fObaAegkRxLFJg_J074SaduEYGo,205871
|
|
@@ -1056,7 +1059,7 @@ supervisely/volume/__init__.py,sha256=EBZBY_5mzabXzMUQh5akusIGd16XnX9n8J0jIi_JmW
|
|
|
1056
1059
|
supervisely/volume/nrrd_encoder.py,sha256=1lqwwyqxEvctw1ysQ70x4xPSV1uy1g5YcH5CURwL7-c,4084
|
|
1057
1060
|
supervisely/volume/nrrd_loader.py,sha256=_yqahKcqSRxunHZ5LtnUWIRA7UvIhPKOhAUwYijSGY4,9065
|
|
1058
1061
|
supervisely/volume/stl_converter.py,sha256=WIMQgHO_u4JT58QdcMXcb_euF1BFhM7D52IVX_0QTxE,6285
|
|
1059
|
-
supervisely/volume/volume.py,sha256
|
|
1062
|
+
supervisely/volume/volume.py,sha256=7ebCIICqZMwRP3ruRy3PtSeiUpBpSlyRH20VJybBQbI,25828
|
|
1060
1063
|
supervisely/volume_annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1061
1064
|
supervisely/volume_annotation/constants.py,sha256=BdFIh56fy7vzLIjt0gH8xP01EIU-qgQIwbSHVUcABCU,569
|
|
1062
1065
|
supervisely/volume_annotation/plane.py,sha256=wyezAcc8tLp38O44CwWY0wjdQxf3VjRdFLWooCrk-Nw,16301
|
|
@@ -1078,9 +1081,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
1078
1081
|
supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
|
|
1079
1082
|
supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
|
|
1080
1083
|
supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
|
|
1081
|
-
supervisely-6.73.
|
|
1082
|
-
supervisely-6.73.
|
|
1083
|
-
supervisely-6.73.
|
|
1084
|
-
supervisely-6.73.
|
|
1085
|
-
supervisely-6.73.
|
|
1086
|
-
supervisely-6.73.
|
|
1084
|
+
supervisely-6.73.324.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
1085
|
+
supervisely-6.73.324.dist-info/METADATA,sha256=mUZAJc6JxtQ7H7I31gf_QWoBAjl4qXsS-CilUCvf8f8,33596
|
|
1086
|
+
supervisely-6.73.324.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
1087
|
+
supervisely-6.73.324.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
|
|
1088
|
+
supervisely-6.73.324.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
|
|
1089
|
+
supervisely-6.73.324.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|