supervisely 6.73.220__py3-none-any.whl → 6.73.222__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.
Potentially problematic release.
This version of supervisely might be problematic. Click here for more details.
- supervisely/api/api.py +609 -3
- supervisely/api/file_api.py +574 -14
- supervisely/api/image_api.py +469 -0
- supervisely/api/pointcloud/pointcloud_api.py +390 -1
- supervisely/api/video/video_api.py +231 -1
- supervisely/api/volume/volume_api.py +223 -2
- supervisely/app/development/__init__.py +1 -0
- supervisely/app/development/development.py +96 -2
- supervisely/app/fastapi/subapp.py +19 -4
- supervisely/convert/base_converter.py +53 -4
- supervisely/convert/converter.py +6 -5
- supervisely/convert/image/image_converter.py +26 -13
- supervisely/convert/image/sly/fast_sly_image_converter.py +4 -0
- supervisely/convert/image/sly/sly_image_converter.py +9 -4
- supervisely/convert/pointcloud_episodes/sly/sly_pointcloud_episodes_converter.py +7 -1
- supervisely/convert/video/sly/sly_video_converter.py +9 -1
- supervisely/convert/video/video_converter.py +44 -23
- supervisely/io/fs.py +125 -0
- supervisely/io/fs_cache.py +19 -1
- supervisely/io/network_exceptions.py +20 -3
- supervisely/task/progress.py +1 -1
- {supervisely-6.73.220.dist-info → supervisely-6.73.222.dist-info}/METADATA +3 -1
- {supervisely-6.73.220.dist-info → supervisely-6.73.222.dist-info}/RECORD +27 -27
- {supervisely-6.73.220.dist-info → supervisely-6.73.222.dist-info}/LICENSE +0 -0
- {supervisely-6.73.220.dist-info → supervisely-6.73.222.dist-info}/WHEEL +0 -0
- {supervisely-6.73.220.dist-info → supervisely-6.73.222.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.220.dist-info → supervisely-6.73.222.dist-info}/top_level.txt +0 -0
supervisely/api/image_api.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# docs
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import asyncio
|
|
7
8
|
import io
|
|
8
9
|
import json
|
|
9
10
|
import re
|
|
@@ -16,6 +17,7 @@ from pathlib import Path
|
|
|
16
17
|
from time import sleep
|
|
17
18
|
from typing import (
|
|
18
19
|
Any,
|
|
20
|
+
AsyncGenerator,
|
|
19
21
|
Callable,
|
|
20
22
|
Dict,
|
|
21
23
|
Generator,
|
|
@@ -29,6 +31,7 @@ from typing import (
|
|
|
29
31
|
)
|
|
30
32
|
from uuid import uuid4
|
|
31
33
|
|
|
34
|
+
import aiofiles
|
|
32
35
|
import numpy as np
|
|
33
36
|
import requests
|
|
34
37
|
from requests.exceptions import HTTPError
|
|
@@ -57,6 +60,7 @@ from supervisely.io.fs import (
|
|
|
57
60
|
ensure_base_path,
|
|
58
61
|
get_file_ext,
|
|
59
62
|
get_file_hash,
|
|
63
|
+
get_file_hash_async,
|
|
60
64
|
get_file_name,
|
|
61
65
|
get_file_name_with_ext,
|
|
62
66
|
list_files,
|
|
@@ -3450,3 +3454,468 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
3450
3454
|
data = {ApiField.IMAGES: images_list, ApiField.CLEAR_LOCAL_DATA_SOURCE: True}
|
|
3451
3455
|
r = self._api.post("images.update.links", data)
|
|
3452
3456
|
return r.json()
|
|
3457
|
+
|
|
3458
|
+
async def _download_async(
|
|
3459
|
+
self,
|
|
3460
|
+
id: int,
|
|
3461
|
+
is_stream: bool = False,
|
|
3462
|
+
range_start: Optional[int] = None,
|
|
3463
|
+
range_end: Optional[int] = None,
|
|
3464
|
+
headers: Optional[dict] = None,
|
|
3465
|
+
) -> AsyncGenerator:
|
|
3466
|
+
"""
|
|
3467
|
+
Download Image with given ID asynchronously.
|
|
3468
|
+
If is_stream is True, returns stream of bytes, otherwise returns response object.
|
|
3469
|
+
For streaming, returns tuple of chunk and hash.
|
|
3470
|
+
|
|
3471
|
+
:param id: Image ID in Supervisely.
|
|
3472
|
+
:type id: int
|
|
3473
|
+
:param is_stream: If True, returns stream of bytes, otherwise returns response object.
|
|
3474
|
+
:type is_stream: bool, optional
|
|
3475
|
+
:param range_start: Start byte of range for partial download.
|
|
3476
|
+
:type range_start: int, optional
|
|
3477
|
+
:param range_end: End byte of range for partial download.
|
|
3478
|
+
:type range_end: int, optional
|
|
3479
|
+
:param headers: Headers for request.
|
|
3480
|
+
:type headers: dict, optional
|
|
3481
|
+
:return: Stream of bytes or response object.
|
|
3482
|
+
:rtype: AsyncGenerator
|
|
3483
|
+
"""
|
|
3484
|
+
api_method_name = "images.download"
|
|
3485
|
+
|
|
3486
|
+
json_body = {ApiField.ID: id}
|
|
3487
|
+
|
|
3488
|
+
if is_stream:
|
|
3489
|
+
async for chunk, hhash in self._api.stream_async(
|
|
3490
|
+
api_method_name,
|
|
3491
|
+
"POST",
|
|
3492
|
+
json_body,
|
|
3493
|
+
headers=headers,
|
|
3494
|
+
range_start=range_start,
|
|
3495
|
+
range_end=range_end,
|
|
3496
|
+
):
|
|
3497
|
+
yield chunk, hhash
|
|
3498
|
+
else:
|
|
3499
|
+
response = await self._api.post_async(api_method_name, json_body, headers=headers)
|
|
3500
|
+
yield response
|
|
3501
|
+
|
|
3502
|
+
async def download_np_async(
|
|
3503
|
+
self,
|
|
3504
|
+
id: int,
|
|
3505
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
3506
|
+
keep_alpha: Optional[bool] = False,
|
|
3507
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
3508
|
+
progress_cb_type: Literal["number", "size"] = "number",
|
|
3509
|
+
) -> np.ndarray:
|
|
3510
|
+
"""
|
|
3511
|
+
Downloads Image with given ID in NumPy format asynchronously.
|
|
3512
|
+
|
|
3513
|
+
:param id: Image ID in Supervisely.
|
|
3514
|
+
:type id: int
|
|
3515
|
+
:param semaphore: Semaphore for limiting the number of simultaneous downloads.
|
|
3516
|
+
:type semaphore: :class:`asyncio.Semaphore`, optional
|
|
3517
|
+
:param keep_alpha: If True keeps alpha mask for image, otherwise don't.
|
|
3518
|
+
:type keep_alpha: bool, optional
|
|
3519
|
+
:param progress_cb: Function for tracking download progress.
|
|
3520
|
+
:type progress_cb: tqdm or callable, optional
|
|
3521
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "number".
|
|
3522
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
3523
|
+
:return: Image in RGB numpy matrix format
|
|
3524
|
+
:rtype: :class:`np.ndarray`
|
|
3525
|
+
|
|
3526
|
+
:Usage example:
|
|
3527
|
+
|
|
3528
|
+
.. code-block:: python
|
|
3529
|
+
|
|
3530
|
+
import supervisely as sly
|
|
3531
|
+
import asyncio
|
|
3532
|
+
from tqdm.asyncio import tqdm
|
|
3533
|
+
|
|
3534
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
3535
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
3536
|
+
api = sly.Api.from_env()
|
|
3537
|
+
|
|
3538
|
+
DATASET_ID = 98357
|
|
3539
|
+
semaphore = asyncio.Semaphore(100)
|
|
3540
|
+
images = api.image.get_list(DATASET_ID)
|
|
3541
|
+
tasks = []
|
|
3542
|
+
pbar = tqdm(total=len(images), desc="Downloading images", unit="image")
|
|
3543
|
+
for image in images:
|
|
3544
|
+
task = api.image.download_np_async(image.id, semaphore, progress_cb=pbar)
|
|
3545
|
+
tasks.append(task)
|
|
3546
|
+
results = await asyncio.gather(*tasks)
|
|
3547
|
+
"""
|
|
3548
|
+
if semaphore is None:
|
|
3549
|
+
semaphore = self._api._get_default_semaphore()
|
|
3550
|
+
|
|
3551
|
+
async with semaphore:
|
|
3552
|
+
async for response in self._download_async(id):
|
|
3553
|
+
img = sly_image.read_bytes(response.content, keep_alpha)
|
|
3554
|
+
if progress_cb is not None:
|
|
3555
|
+
if progress_cb_type == "number":
|
|
3556
|
+
progress_cb(1)
|
|
3557
|
+
elif progress_cb_type == "size":
|
|
3558
|
+
progress_cb(len(response.content))
|
|
3559
|
+
return img
|
|
3560
|
+
|
|
3561
|
+
async def download_nps_async(
|
|
3562
|
+
self,
|
|
3563
|
+
ids: List[int],
|
|
3564
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
3565
|
+
keep_alpha: Optional[bool] = False,
|
|
3566
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
3567
|
+
progress_cb_type: Literal["number", "size"] = "number",
|
|
3568
|
+
) -> List[np.ndarray]:
|
|
3569
|
+
"""
|
|
3570
|
+
Downloads Images with given IDs in NumPy format asynchronously.
|
|
3571
|
+
|
|
3572
|
+
:param ids: List of Image IDs in Supervisely.
|
|
3573
|
+
:type ids: :class:`List[int]`
|
|
3574
|
+
:param semaphore: Semaphore for limiting the number of simultaneous downloads.
|
|
3575
|
+
:type semaphore: :class:`asyncio.Semaphore`, optional
|
|
3576
|
+
:param keep_alpha: If True keeps alpha mask for images, otherwise don't.
|
|
3577
|
+
:type keep_alpha: bool, optional
|
|
3578
|
+
:param progress_cb: Function for tracking download progress.
|
|
3579
|
+
:type progress_cb: tqdm or callable, optional
|
|
3580
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "number".
|
|
3581
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
3582
|
+
:return: List of Images in RGB numpy matrix format
|
|
3583
|
+
:rtype: :class:`List[np.ndarray]`
|
|
3584
|
+
|
|
3585
|
+
:Usage example:
|
|
3586
|
+
|
|
3587
|
+
.. code-block:: python
|
|
3588
|
+
|
|
3589
|
+
import supervisely as sly
|
|
3590
|
+
import asyncio
|
|
3591
|
+
|
|
3592
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
3593
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
3594
|
+
api = sly.Api.from_env()
|
|
3595
|
+
|
|
3596
|
+
DATASET_ID = 98357
|
|
3597
|
+
semaphore = asyncio.Semaphore(100)
|
|
3598
|
+
images = api.image.get_list(DATASET_ID)
|
|
3599
|
+
img_ids = [image.id for image in images]
|
|
3600
|
+
loop = asyncio.new_event_loop()
|
|
3601
|
+
asyncio.set_event_loop(loop)
|
|
3602
|
+
results = loop.run_until_complete(
|
|
3603
|
+
api.image.download_nps_async(img_ids, semaphore)
|
|
3604
|
+
)
|
|
3605
|
+
|
|
3606
|
+
"""
|
|
3607
|
+
if semaphore is None:
|
|
3608
|
+
semaphore = self._api._get_default_semaphore()
|
|
3609
|
+
tasks = [
|
|
3610
|
+
self.download_np_async(id, semaphore, keep_alpha, progress_cb, progress_cb_type)
|
|
3611
|
+
for id in ids
|
|
3612
|
+
]
|
|
3613
|
+
return await asyncio.gather(*tasks)
|
|
3614
|
+
|
|
3615
|
+
async def download_path_async(
|
|
3616
|
+
self,
|
|
3617
|
+
id: int,
|
|
3618
|
+
path: str,
|
|
3619
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
3620
|
+
range_start: Optional[int] = None,
|
|
3621
|
+
range_end: Optional[int] = None,
|
|
3622
|
+
headers: Optional[dict] = None,
|
|
3623
|
+
check_hash: bool = True,
|
|
3624
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
3625
|
+
progress_cb_type: Literal["number", "size"] = "number",
|
|
3626
|
+
) -> None:
|
|
3627
|
+
"""
|
|
3628
|
+
Downloads Image with given ID to local path.
|
|
3629
|
+
|
|
3630
|
+
:param id: Image ID in Supervisely.
|
|
3631
|
+
:type id: int
|
|
3632
|
+
:param path: Local save path for Image.
|
|
3633
|
+
:type path: str
|
|
3634
|
+
:param semaphore: Semaphore for limiting the number of simultaneous downloads.
|
|
3635
|
+
:type semaphore: :class:`asyncio.Semaphore`, optional
|
|
3636
|
+
:param range_start: Start byte of range for partial download.
|
|
3637
|
+
:type range_start: int, optional
|
|
3638
|
+
:param range_end: End byte of range for partial download.
|
|
3639
|
+
:type range_end: int, optional
|
|
3640
|
+
:param headers: Headers for request.
|
|
3641
|
+
:type headers: dict, optional
|
|
3642
|
+
:param check_hash: If True, checks hash of downloaded file.
|
|
3643
|
+
Check is not supported for partial downloads.
|
|
3644
|
+
When range is set, hash check is disabled.
|
|
3645
|
+
:type check_hash: bool, optional
|
|
3646
|
+
:param progress_cb: Function for tracking download progress.
|
|
3647
|
+
:type progress_cb: tqdm or callable, optional
|
|
3648
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "number".
|
|
3649
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
3650
|
+
:return: None
|
|
3651
|
+
:rtype: :class:`NoneType`
|
|
3652
|
+
:Usage example:
|
|
3653
|
+
|
|
3654
|
+
.. code-block:: python
|
|
3655
|
+
|
|
3656
|
+
import supervisely as sly
|
|
3657
|
+
import asyncio
|
|
3658
|
+
|
|
3659
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
3660
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
3661
|
+
api = sly.Api.from_env()
|
|
3662
|
+
|
|
3663
|
+
img_info = api.image.get_info_by_id(770918)
|
|
3664
|
+
save_path = os.path.join("/path/to/save/", img_info.name)
|
|
3665
|
+
|
|
3666
|
+
semaphore = asyncio.Semaphore(100)
|
|
3667
|
+
loop = asyncio.new_event_loop()
|
|
3668
|
+
asyncio.set_event_loop(loop)
|
|
3669
|
+
loop.run_until_complete(
|
|
3670
|
+
api.image.download_path_async(img_info.id, save_path, semaphore)
|
|
3671
|
+
)
|
|
3672
|
+
"""
|
|
3673
|
+
if range_start is not None or range_end is not None:
|
|
3674
|
+
check_hash = False # hash check is not supported for partial downloads
|
|
3675
|
+
headers = headers or {}
|
|
3676
|
+
headers["Range"] = f"bytes={range_start or ''}-{range_end or ''}"
|
|
3677
|
+
logger.debug(f"Image ID: {id}. Setting Range header: {headers['Range']}")
|
|
3678
|
+
|
|
3679
|
+
writing_method = "ab" if range_start not in [0, None] else "wb"
|
|
3680
|
+
|
|
3681
|
+
ensure_base_path(path)
|
|
3682
|
+
hash_to_check = None
|
|
3683
|
+
if semaphore is None:
|
|
3684
|
+
semaphore = self._api._get_default_semaphore()
|
|
3685
|
+
async with semaphore:
|
|
3686
|
+
async with aiofiles.open(path, writing_method) as fd:
|
|
3687
|
+
async for chunk, hhash in self._download_async(
|
|
3688
|
+
id,
|
|
3689
|
+
is_stream=True,
|
|
3690
|
+
range_start=range_start,
|
|
3691
|
+
range_end=range_end,
|
|
3692
|
+
headers=headers,
|
|
3693
|
+
):
|
|
3694
|
+
await fd.write(chunk)
|
|
3695
|
+
hash_to_check = hhash
|
|
3696
|
+
if progress_cb is not None and progress_cb_type == "size":
|
|
3697
|
+
progress_cb(len(chunk))
|
|
3698
|
+
if progress_cb is not None and progress_cb_type == "number":
|
|
3699
|
+
progress_cb(1)
|
|
3700
|
+
if check_hash:
|
|
3701
|
+
if hash_to_check is not None:
|
|
3702
|
+
downloaded_file_hash = await get_file_hash_async(path)
|
|
3703
|
+
if hash_to_check != downloaded_file_hash:
|
|
3704
|
+
raise RuntimeError(
|
|
3705
|
+
f"Downloaded hash of image with ID:{id} does not match the expected hash: {downloaded_file_hash} != {hash_to_check}"
|
|
3706
|
+
)
|
|
3707
|
+
|
|
3708
|
+
async def download_paths_async(
|
|
3709
|
+
self,
|
|
3710
|
+
ids: List[int],
|
|
3711
|
+
paths: List[str],
|
|
3712
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
3713
|
+
headers: Optional[dict] = None,
|
|
3714
|
+
check_hash: bool = True,
|
|
3715
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
3716
|
+
progress_cb_type: Literal["number", "size"] = "number",
|
|
3717
|
+
) -> None:
|
|
3718
|
+
"""
|
|
3719
|
+
Download Images with given IDs and saves them to given local paths asynchronously.
|
|
3720
|
+
|
|
3721
|
+
:param ids: List of Image IDs in Supervisely.
|
|
3722
|
+
:type ids: :class:`List[int]`
|
|
3723
|
+
:param paths: Local save paths for Images.
|
|
3724
|
+
:type paths: :class:`List[str]`
|
|
3725
|
+
:param semaphore: Semaphore for limiting the number of simultaneous downloads.
|
|
3726
|
+
:type semaphore: :class:`asyncio.Semaphore`, optional
|
|
3727
|
+
:param headers: Headers for request.
|
|
3728
|
+
:type headers: dict, optional
|
|
3729
|
+
:param check_hash: If True, checks hash of downloaded images.
|
|
3730
|
+
:type check_hash: bool, optional
|
|
3731
|
+
:param progress_cb: Function for tracking download progress.
|
|
3732
|
+
:type progress_cb: tqdm or callable, optional
|
|
3733
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "number".
|
|
3734
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
3735
|
+
:raises: :class:`ValueError` if len(ids) != len(paths)
|
|
3736
|
+
:return: None
|
|
3737
|
+
:rtype: :class:`NoneType`
|
|
3738
|
+
|
|
3739
|
+
:Usage example:
|
|
3740
|
+
|
|
3741
|
+
.. code-block:: python
|
|
3742
|
+
|
|
3743
|
+
import supervisely as sly
|
|
3744
|
+
import asyncio
|
|
3745
|
+
|
|
3746
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
3747
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
3748
|
+
api = sly.Api.from_env()
|
|
3749
|
+
|
|
3750
|
+
ids = [770918, 770919]
|
|
3751
|
+
paths = ["/path/to/save/image1.png", "/path/to/save/image2.png"]
|
|
3752
|
+
loop = asyncio.new_event_loop()
|
|
3753
|
+
asyncio.set_event_loop(loop)
|
|
3754
|
+
loop.run_until_complete(api.image.download_paths_async(ids, paths))
|
|
3755
|
+
"""
|
|
3756
|
+
if len(ids) == 0:
|
|
3757
|
+
return
|
|
3758
|
+
if len(ids) != len(paths):
|
|
3759
|
+
raise ValueError(
|
|
3760
|
+
f'Length of "ids" and "paths" should be equal. {len(ids)} != {len(paths)}'
|
|
3761
|
+
)
|
|
3762
|
+
if semaphore is None:
|
|
3763
|
+
semaphore = self._api._get_default_semaphore()
|
|
3764
|
+
tasks = []
|
|
3765
|
+
for img_id, img_path in zip(ids, paths):
|
|
3766
|
+
task = self.download_path_async(
|
|
3767
|
+
img_id,
|
|
3768
|
+
img_path,
|
|
3769
|
+
semaphore,
|
|
3770
|
+
headers=headers,
|
|
3771
|
+
check_hash=check_hash,
|
|
3772
|
+
progress_cb=progress_cb,
|
|
3773
|
+
progress_cb_type=progress_cb_type,
|
|
3774
|
+
)
|
|
3775
|
+
tasks.append(task)
|
|
3776
|
+
await asyncio.gather(*tasks)
|
|
3777
|
+
|
|
3778
|
+
async def download_bytes_single_async(
|
|
3779
|
+
self,
|
|
3780
|
+
id: int,
|
|
3781
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
3782
|
+
range_start: Optional[int] = None,
|
|
3783
|
+
range_end: Optional[int] = None,
|
|
3784
|
+
headers: Optional[dict] = None,
|
|
3785
|
+
check_hash: bool = True,
|
|
3786
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
3787
|
+
progress_cb_type: Literal["number", "size"] = "number",
|
|
3788
|
+
) -> bytes:
|
|
3789
|
+
"""
|
|
3790
|
+
Downloads Image bytes with given ID.
|
|
3791
|
+
|
|
3792
|
+
:param id: Image ID in Supervisely.
|
|
3793
|
+
:type id: int
|
|
3794
|
+
:param semaphore: Semaphore for limiting the number of simultaneous downloads.
|
|
3795
|
+
:type semaphore: :class:`asyncio.Semaphore`, optional
|
|
3796
|
+
:param range_start: Start byte of range for partial download.
|
|
3797
|
+
:type range_start: int, optional
|
|
3798
|
+
:param range_end: End byte of range for partial download.
|
|
3799
|
+
:type range_end: int, optional
|
|
3800
|
+
:param headers: Headers for request.
|
|
3801
|
+
:type headers: dict, optional
|
|
3802
|
+
:param check_hash: If True, checks hash of downloaded bytes.
|
|
3803
|
+
Check is not supported for partial downloads.
|
|
3804
|
+
When range is set, hash check is disabled.
|
|
3805
|
+
:type check_hash: bool, optional
|
|
3806
|
+
:param progress_cb: Function for tracking download progress.
|
|
3807
|
+
:type progress_cb: Optional[Union[tqdm, Callable]]
|
|
3808
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "number".
|
|
3809
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
3810
|
+
:return: Bytes of downloaded image.
|
|
3811
|
+
:rtype: :class:`bytes`
|
|
3812
|
+
:Usage example:
|
|
3813
|
+
|
|
3814
|
+
.. code-block:: python
|
|
3815
|
+
|
|
3816
|
+
import supervisely as sly
|
|
3817
|
+
import asyncio
|
|
3818
|
+
|
|
3819
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
3820
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
3821
|
+
api = sly.Api.from_env()
|
|
3822
|
+
|
|
3823
|
+
img_id = 770918
|
|
3824
|
+
loop = asyncio.new_event_loop()
|
|
3825
|
+
asyncio.set_event_loop(loop)
|
|
3826
|
+
img_bytes = loop.run_until_complete(api.image.download_bytes_async(img_id))
|
|
3827
|
+
|
|
3828
|
+
"""
|
|
3829
|
+
if range_start is not None or range_end is not None:
|
|
3830
|
+
check_hash = False # hash check is not supported for partial downloads
|
|
3831
|
+
headers = headers or {}
|
|
3832
|
+
headers["Range"] = f"bytes={range_start or ''}-{range_end or ''}"
|
|
3833
|
+
logger.debug(f"Image ID: {id}. Setting Range header: {headers['Range']}")
|
|
3834
|
+
|
|
3835
|
+
hash_to_check = None
|
|
3836
|
+
|
|
3837
|
+
if semaphore is None:
|
|
3838
|
+
semaphore = self._api._get_default_semaphore()
|
|
3839
|
+
async with semaphore:
|
|
3840
|
+
content = b""
|
|
3841
|
+
async for chunk, hhash in self._download_async(
|
|
3842
|
+
id,
|
|
3843
|
+
is_stream=True,
|
|
3844
|
+
headers=headers,
|
|
3845
|
+
range_start=range_start,
|
|
3846
|
+
range_end=range_end,
|
|
3847
|
+
):
|
|
3848
|
+
content += chunk
|
|
3849
|
+
hash_to_check = hhash
|
|
3850
|
+
if progress_cb is not None and progress_cb_type == "size":
|
|
3851
|
+
progress_cb(len(chunk))
|
|
3852
|
+
if check_hash:
|
|
3853
|
+
if hash_to_check is not None:
|
|
3854
|
+
downloaded_bytes_hash = get_bytes_hash(content)
|
|
3855
|
+
if hash_to_check != downloaded_bytes_hash:
|
|
3856
|
+
raise RuntimeError(
|
|
3857
|
+
f"Downloaded hash of image with ID:{id} does not match the expected hash: {downloaded_bytes_hash} != {hash_to_check}"
|
|
3858
|
+
)
|
|
3859
|
+
if progress_cb is not None and progress_cb_type == "number":
|
|
3860
|
+
progress_cb(1)
|
|
3861
|
+
return content
|
|
3862
|
+
|
|
3863
|
+
async def download_bytes_many_async(
|
|
3864
|
+
self,
|
|
3865
|
+
ids: List[int],
|
|
3866
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
3867
|
+
headers: Optional[dict] = None,
|
|
3868
|
+
check_hash: bool = True,
|
|
3869
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
3870
|
+
progress_cb_type: Literal["number", "size"] = "number",
|
|
3871
|
+
) -> List[bytes]:
|
|
3872
|
+
"""
|
|
3873
|
+
Downloads Images bytes with given IDs asynchronously
|
|
3874
|
+
and returns reults in the same order as in the input list.
|
|
3875
|
+
|
|
3876
|
+
:param ids: List of Image IDs in Supervisely.
|
|
3877
|
+
:type ids: :class:`List[int]`
|
|
3878
|
+
:param semaphore: Semaphore for limiting the number of simultaneous downloads.
|
|
3879
|
+
:type semaphore: :class:`asyncio.Semaphore`, optional
|
|
3880
|
+
:param headers: Headers for every request.
|
|
3881
|
+
:type headers: dict, optional
|
|
3882
|
+
:param check_hash: If True, checks hash of downloaded images.
|
|
3883
|
+
:type check_hash: bool, optional
|
|
3884
|
+
:param progress_cb: Function for tracking download progress.
|
|
3885
|
+
:type progress_cb: Optional[Union[tqdm, Callable]]
|
|
3886
|
+
:param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "number".
|
|
3887
|
+
:type progress_cb_type: Literal["number", "size"], optional
|
|
3888
|
+
:return: List of bytes of downloaded images.
|
|
3889
|
+
:rtype: :class:`List[bytes]`
|
|
3890
|
+
|
|
3891
|
+
:Usage example:
|
|
3892
|
+
|
|
3893
|
+
.. code-block:: python
|
|
3894
|
+
|
|
3895
|
+
import supervisely as sly
|
|
3896
|
+
import asyncio
|
|
3897
|
+
|
|
3898
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
3899
|
+
os.environ['API_TOKEN
|
|
3900
|
+
api = sly.Api.from_env()
|
|
3901
|
+
|
|
3902
|
+
loop = asyncio.new_event_loop()
|
|
3903
|
+
asyncio.set_event_loop(loop)
|
|
3904
|
+
semaphore = asyncio.Semaphore(100)
|
|
3905
|
+
img_bytes_list = loop.run_until_complete(api.image.download_bytes_imgs_async(ids, semaphore))
|
|
3906
|
+
"""
|
|
3907
|
+
if semaphore is None:
|
|
3908
|
+
semaphore = self._api._get_default_semaphore()
|
|
3909
|
+
tasks = []
|
|
3910
|
+
for id in ids:
|
|
3911
|
+
task = self.download_bytes_single_async(
|
|
3912
|
+
id,
|
|
3913
|
+
semaphore,
|
|
3914
|
+
headers=headers,
|
|
3915
|
+
check_hash=check_hash,
|
|
3916
|
+
progress_cb=progress_cb,
|
|
3917
|
+
progress_cb_type=progress_cb_type,
|
|
3918
|
+
)
|
|
3919
|
+
tasks.append(task)
|
|
3920
|
+
results = await asyncio.gather(*tasks)
|
|
3921
|
+
return results
|