supervisely 6.73.355__py3-none-any.whl → 6.73.357__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/api.py +4 -0
- supervisely/api/app_api.py +12 -2
- supervisely/api/constants.py +21 -0
- supervisely/api/entities_collection_api.py +304 -0
- supervisely/api/image_api.py +15 -0
- supervisely/api/labeling_job_api.py +4 -1
- supervisely/api/labeling_queue_api.py +735 -0
- supervisely/api/module_api.py +24 -0
- supervisely/volume_annotation/volume_figure.py +8 -2
- {supervisely-6.73.355.dist-info → supervisely-6.73.357.dist-info}/METADATA +1 -1
- {supervisely-6.73.355.dist-info → supervisely-6.73.357.dist-info}/RECORD +15 -13
- {supervisely-6.73.355.dist-info → supervisely-6.73.357.dist-info}/LICENSE +0 -0
- {supervisely-6.73.355.dist-info → supervisely-6.73.357.dist-info}/WHEEL +0 -0
- {supervisely-6.73.355.dist-info → supervisely-6.73.357.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.355.dist-info → supervisely-6.73.357.dist-info}/top_level.txt +0 -0
supervisely/api/api.py
CHANGED
|
@@ -45,6 +45,8 @@ import supervisely.api.image_api as image_api
|
|
|
45
45
|
import supervisely.api.import_storage_api as import_stoarge_api
|
|
46
46
|
import supervisely.api.issues_api as issues_api
|
|
47
47
|
import supervisely.api.labeling_job_api as labeling_job_api
|
|
48
|
+
import supervisely.api.labeling_queue_api as labeling_queue_api
|
|
49
|
+
import supervisely.api.entities_collection_api as entities_collection_api
|
|
48
50
|
import supervisely.api.neural_network_api as neural_network_api
|
|
49
51
|
import supervisely.api.object_class_api as object_class_api
|
|
50
52
|
import supervisely.api.plugin_api as plugin_api
|
|
@@ -353,6 +355,7 @@ class Api:
|
|
|
353
355
|
self.role = role_api.RoleApi(self)
|
|
354
356
|
self.user = user_api.UserApi(self)
|
|
355
357
|
self.labeling_job = labeling_job_api.LabelingJobApi(self)
|
|
358
|
+
self.labeling_queue = labeling_queue_api.LabelingQueueApi(self)
|
|
356
359
|
self.video = video_api.VideoApi(self)
|
|
357
360
|
# self.project_class = project_class_api.ProjectClassApi(self)
|
|
358
361
|
self.object_class = object_class_api.ObjectClassApi(self)
|
|
@@ -370,6 +373,7 @@ class Api:
|
|
|
370
373
|
self.github = github_api.GithubApi(self)
|
|
371
374
|
self.volume = volume_api.VolumeApi(self)
|
|
372
375
|
self.issues = issues_api.IssuesApi(self)
|
|
376
|
+
self.entities_collection = entities_collection_api.EntitiesCollectionApi(self)
|
|
373
377
|
|
|
374
378
|
self.retry_count = retry_count
|
|
375
379
|
self.retry_sleep_sec = retry_sleep_sec
|
supervisely/api/app_api.py
CHANGED
|
@@ -1446,7 +1446,7 @@ class AppApi(TaskApi):
|
|
|
1446
1446
|
return response.json()
|
|
1447
1447
|
|
|
1448
1448
|
def get_ecosystem_module_info(
|
|
1449
|
-
self, module_id: int, version: Optional[str] = None
|
|
1449
|
+
self, module_id: int = None, version: Optional[str] = None, slug: Optional[str] = None
|
|
1450
1450
|
) -> ModuleInfo:
|
|
1451
1451
|
"""Returns ModuleInfo object by module id and version.
|
|
1452
1452
|
|
|
@@ -1454,6 +1454,10 @@ class AppApi(TaskApi):
|
|
|
1454
1454
|
:type module_id: int
|
|
1455
1455
|
:param version: version of the module, e.g. "v1.0.0"
|
|
1456
1456
|
:type version: Optional[str]
|
|
1457
|
+
:param slug: slug of the module, e.g. "supervisely-ecosystem/export-to-supervisely-format"
|
|
1458
|
+
:type slug: Optional[str]
|
|
1459
|
+
:raises ValueError: if both module_id and slug are None
|
|
1460
|
+
:raises ValueError: if both module_id and slug are provided
|
|
1457
1461
|
:return: ModuleInfo object
|
|
1458
1462
|
:rtype: ModuleInfo
|
|
1459
1463
|
:Usage example:
|
|
@@ -1473,7 +1477,13 @@ class AppApi(TaskApi):
|
|
|
1473
1477
|
module_id = 81
|
|
1474
1478
|
module_info = api.app.get_ecosystem_module_info(module_id)
|
|
1475
1479
|
"""
|
|
1476
|
-
|
|
1480
|
+
if module_id is None and slug is None:
|
|
1481
|
+
raise ValueError("Either module_id or slug must be provided")
|
|
1482
|
+
if module_id is not None:
|
|
1483
|
+
data = {ApiField.ID: module_id}
|
|
1484
|
+
else:
|
|
1485
|
+
data = {ApiField.SLUG: slug}
|
|
1486
|
+
|
|
1477
1487
|
if version is not None:
|
|
1478
1488
|
data[ApiField.VERSION] = version
|
|
1479
1489
|
response = self._api.post("ecosystem.info", data)
|
supervisely/api/constants.py
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
3
|
DOWNLOAD_BATCH_SIZE = None
|
|
4
|
+
|
|
5
|
+
# for Video Labeling Toolbox
|
|
6
|
+
PLAYBACK_RATE_POSSIBLE_VALUES = [
|
|
7
|
+
0.1,
|
|
8
|
+
0.3,
|
|
9
|
+
0.5,
|
|
10
|
+
0.6,
|
|
11
|
+
0.7,
|
|
12
|
+
0.8,
|
|
13
|
+
0.9,
|
|
14
|
+
1,
|
|
15
|
+
1.1,
|
|
16
|
+
1.2,
|
|
17
|
+
1.3,
|
|
18
|
+
1.5,
|
|
19
|
+
2,
|
|
20
|
+
4,
|
|
21
|
+
8,
|
|
22
|
+
16,
|
|
23
|
+
32,
|
|
24
|
+
]
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
"""create or manipulate already existing Entities Collection in Supervisely"""
|
|
3
|
+
|
|
4
|
+
# docs
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, NamedTuple, Optional
|
|
8
|
+
|
|
9
|
+
from supervisely.api.module_api import (
|
|
10
|
+
ApiField,
|
|
11
|
+
ModuleApi,
|
|
12
|
+
RemoveableModuleApi,
|
|
13
|
+
UpdateableModule,
|
|
14
|
+
)
|
|
15
|
+
from supervisely.sly_logger import logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EntitiesCollectionInfo(NamedTuple):
|
|
19
|
+
id: int
|
|
20
|
+
name: str
|
|
21
|
+
team_id: int
|
|
22
|
+
project_id: int
|
|
23
|
+
description: str
|
|
24
|
+
created_at: str
|
|
25
|
+
updated_at: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EntitiesCollectionApi(UpdateableModule, RemoveableModuleApi):
|
|
29
|
+
"""
|
|
30
|
+
API for working with Entities Collection. :class:`EntitiesCollectionApi<EntitiesCollectionApi>` object is immutable.
|
|
31
|
+
|
|
32
|
+
:param api: API connection to the server.
|
|
33
|
+
:type api: Api
|
|
34
|
+
:Usage example:
|
|
35
|
+
|
|
36
|
+
.. code-block:: python
|
|
37
|
+
|
|
38
|
+
import os
|
|
39
|
+
from dotenv import load_dotenv
|
|
40
|
+
|
|
41
|
+
import supervisely as sly
|
|
42
|
+
|
|
43
|
+
# Load secrets and create API object from .env file (recommended)
|
|
44
|
+
# Learn more here: https://developer.supervisely.com/getting-started/basics-of-authentication
|
|
45
|
+
if sly.is_development():
|
|
46
|
+
load_dotenv(os.path.expanduser("~/supervisely.env"))
|
|
47
|
+
api = sly.Api.from_env()
|
|
48
|
+
|
|
49
|
+
# Pass values into the API constructor (optional, not recommended)
|
|
50
|
+
# api = sly.Api(server_address="https://app.supervise.ly", token="4r47N...xaTatb")
|
|
51
|
+
|
|
52
|
+
collection = api.entities_collection.get_list(9) # api usage example
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def info_sequence():
|
|
57
|
+
"""
|
|
58
|
+
NamedTuple EntitiesCollectionInfo information about Entities Collection.
|
|
59
|
+
|
|
60
|
+
:Example:
|
|
61
|
+
|
|
62
|
+
.. code-block:: python
|
|
63
|
+
|
|
64
|
+
EntitiesCollectionInfo(
|
|
65
|
+
id=2,
|
|
66
|
+
name='Enitites Collections #1',
|
|
67
|
+
team_id=4,
|
|
68
|
+
project_id=58,
|
|
69
|
+
description='',
|
|
70
|
+
created_at='2020-04-08T15:10:12.618Z',
|
|
71
|
+
updated_at='2020-04-08T15:10:19.833Z',
|
|
72
|
+
)
|
|
73
|
+
"""
|
|
74
|
+
return [
|
|
75
|
+
ApiField.ID,
|
|
76
|
+
ApiField.NAME,
|
|
77
|
+
ApiField.TEAM_ID,
|
|
78
|
+
ApiField.PROJECT_ID,
|
|
79
|
+
ApiField.DESCRIPTION,
|
|
80
|
+
ApiField.CREATED_AT,
|
|
81
|
+
ApiField.UPDATED_AT,
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def info_tuple_name():
|
|
86
|
+
"""
|
|
87
|
+
NamedTuple name - **EntitiesCollectionInfo**.
|
|
88
|
+
"""
|
|
89
|
+
return "EntitiesCollectionInfo"
|
|
90
|
+
|
|
91
|
+
def __init__(self, api):
|
|
92
|
+
ModuleApi.__init__(self, api)
|
|
93
|
+
|
|
94
|
+
def create(
|
|
95
|
+
self, project_id: int, name: str, description: Optional[str] = None
|
|
96
|
+
) -> EntitiesCollectionInfo:
|
|
97
|
+
"""
|
|
98
|
+
Creates Entities Collections.
|
|
99
|
+
|
|
100
|
+
:param project_id: Project ID in Supervisely.
|
|
101
|
+
:type project_id: int
|
|
102
|
+
:param name: Entities Collection name in Supervisely.
|
|
103
|
+
:type name: str
|
|
104
|
+
:param description: Entities Collection description.
|
|
105
|
+
:type description: str
|
|
106
|
+
:return: Information about new Entities Collection
|
|
107
|
+
:rtype: :class:`EntitiesCollectionInfo`
|
|
108
|
+
:Usage example:
|
|
109
|
+
|
|
110
|
+
.. code-block:: python
|
|
111
|
+
|
|
112
|
+
import supervisely as sly
|
|
113
|
+
|
|
114
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
115
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
116
|
+
api = sly.Api.from_env()
|
|
117
|
+
|
|
118
|
+
user_name = 'Collection #1'
|
|
119
|
+
project_id = 602
|
|
120
|
+
new_collection = api.entities_collection.create(name, project_id)
|
|
121
|
+
print(new_collection)
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
data = {ApiField.NAME: name, ApiField.PROJECT_ID: project_id}
|
|
125
|
+
if description is not None:
|
|
126
|
+
data[ApiField.DESCRIPTION] = description
|
|
127
|
+
response = self._api.post("entities-collections.add", data)
|
|
128
|
+
return self._convert_json_info(response.json())
|
|
129
|
+
|
|
130
|
+
def get_list(
|
|
131
|
+
self,
|
|
132
|
+
project_id: int,
|
|
133
|
+
filters: Optional[List[Dict[str, str]]] = None,
|
|
134
|
+
) -> List[EntitiesCollectionInfo]:
|
|
135
|
+
"""
|
|
136
|
+
Get list of information about Entities Collection for the given project.
|
|
137
|
+
|
|
138
|
+
:param project_id: Project ID in Supervisely.
|
|
139
|
+
:type project_id: int, optional
|
|
140
|
+
:return: List of information about Entities Collections.
|
|
141
|
+
:rtype: :class:`List[EntitiesCollectionInfo]`
|
|
142
|
+
:Usage example:
|
|
143
|
+
|
|
144
|
+
.. code-block:: python
|
|
145
|
+
|
|
146
|
+
import supervisely as sly
|
|
147
|
+
|
|
148
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
149
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
150
|
+
api = sly.Api.from_env()
|
|
151
|
+
|
|
152
|
+
collections = api.entities_collection.get_list(4)
|
|
153
|
+
"""
|
|
154
|
+
if filters is None:
|
|
155
|
+
filters = []
|
|
156
|
+
return self.get_list_all_pages(
|
|
157
|
+
"entities-collections.list",
|
|
158
|
+
{ApiField.PROJECT_ID: project_id, ApiField.FILTER: filters},
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def get_info_by_id(self, id: int) -> EntitiesCollectionInfo:
|
|
162
|
+
"""
|
|
163
|
+
Get information about Entities Collection with given ID.
|
|
164
|
+
|
|
165
|
+
:param id: Entities Collection ID in Supervisely.
|
|
166
|
+
:type id: int
|
|
167
|
+
:return: Information about Entities Collection.
|
|
168
|
+
:rtype: :class:`EntitiesCollectionInfo`
|
|
169
|
+
:Usage example:
|
|
170
|
+
|
|
171
|
+
.. code-block:: python
|
|
172
|
+
|
|
173
|
+
import supervisely as sly
|
|
174
|
+
|
|
175
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
176
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
177
|
+
api = sly.Api.from_env()
|
|
178
|
+
|
|
179
|
+
collection = api.entities_collection.get_info_by_id(2)
|
|
180
|
+
print(collection)
|
|
181
|
+
# Output:
|
|
182
|
+
# {
|
|
183
|
+
# "id": 1,
|
|
184
|
+
# "teamId": 1,
|
|
185
|
+
# "projectId": 1,
|
|
186
|
+
# "name": "ds",
|
|
187
|
+
# "description": "",
|
|
188
|
+
# "createdAt": "2018-08-21T14:25:56.140Z",
|
|
189
|
+
# "updatedAt": "2018-08-21T14:25:56.140Z"
|
|
190
|
+
# }
|
|
191
|
+
"""
|
|
192
|
+
return self._get_info_by_id(id, "entities-collections.info")
|
|
193
|
+
|
|
194
|
+
def _get_update_method(self):
|
|
195
|
+
""" """
|
|
196
|
+
return "entities-collections.editInfo"
|
|
197
|
+
|
|
198
|
+
def _remove_api_method_name(self):
|
|
199
|
+
""" """
|
|
200
|
+
return "entities-collections.remove"
|
|
201
|
+
|
|
202
|
+
def _get_update_method(self):
|
|
203
|
+
""" """
|
|
204
|
+
return "entities-collections.editInfo"
|
|
205
|
+
|
|
206
|
+
def add_items(self, id: int, items: List[int]) -> List[Dict[str, int]]:
|
|
207
|
+
"""
|
|
208
|
+
Add items to Entities Collection.
|
|
209
|
+
|
|
210
|
+
:param id: Entities Collection ID in Supervisely.
|
|
211
|
+
:type id: int
|
|
212
|
+
:param items: List of item IDs in Supervisely.
|
|
213
|
+
:type items: List[int]
|
|
214
|
+
:Usage example:
|
|
215
|
+
|
|
216
|
+
.. code-block:: python
|
|
217
|
+
|
|
218
|
+
import supervisely as sly
|
|
219
|
+
|
|
220
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
221
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
222
|
+
api = sly.Api.from_env()
|
|
223
|
+
|
|
224
|
+
collection_id = 2
|
|
225
|
+
item_ids = [525, 526]
|
|
226
|
+
new_items = api.entities_collection.add_items(collection_id, item_ids)
|
|
227
|
+
print(new_items)
|
|
228
|
+
# Output: [
|
|
229
|
+
# {"id": 1, "entityId": 525, 'createdAt': '2025-04-10T08:49:41.852Z'},
|
|
230
|
+
# {"id": 2, "entityId": 526, 'createdAt': '2025-04-10T08:49:41.852Z'}
|
|
231
|
+
]
|
|
232
|
+
"""
|
|
233
|
+
data = {ApiField.COLLECTION_ID: id, ApiField.ENTITY_IDS: items}
|
|
234
|
+
response = self._api.post("entities-collections.items.bulk.add", data)
|
|
235
|
+
response = response.json()
|
|
236
|
+
if len(response["missing"]) > 0:
|
|
237
|
+
raise RuntimeError(
|
|
238
|
+
f"Failed to add items to Entities Collection. IDs: {response['missing']}. "
|
|
239
|
+
)
|
|
240
|
+
return response["items"]
|
|
241
|
+
|
|
242
|
+
def get_items(self, id: int, project_id: Optional[int] = None) -> List[int]:
|
|
243
|
+
"""
|
|
244
|
+
Get items from Entities Collection.
|
|
245
|
+
|
|
246
|
+
:param id: Entities Collection ID in Supervisely.
|
|
247
|
+
:type id: int
|
|
248
|
+
:param project_id: Project ID in Supervisely.
|
|
249
|
+
:type project_id: int, optional
|
|
250
|
+
:return: List of item IDs in Supervisely.
|
|
251
|
+
:rtype: List[int]
|
|
252
|
+
:raises RuntimeError: If Entities Collection with given ID not found.
|
|
253
|
+
:Usage example:
|
|
254
|
+
|
|
255
|
+
.. code-block:: python
|
|
256
|
+
|
|
257
|
+
import supervisely as sly
|
|
258
|
+
|
|
259
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
260
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
261
|
+
api = sly.Api.from_env()
|
|
262
|
+
|
|
263
|
+
collection_id = 2
|
|
264
|
+
project_id = 4
|
|
265
|
+
item_ids = api.entities_collection.get_items(collection_id, project_id)
|
|
266
|
+
print(item_ids)
|
|
267
|
+
"""
|
|
268
|
+
if project_id is None:
|
|
269
|
+
info = self.get_info_by_id(id)
|
|
270
|
+
if info is None:
|
|
271
|
+
raise RuntimeError(f"Entities Collection with id={id} not found.")
|
|
272
|
+
project_id = info.project_id
|
|
273
|
+
|
|
274
|
+
return self._api.image.get_list(project_id=project_id, entities_collection_id=id)
|
|
275
|
+
|
|
276
|
+
def remove_items(self, id: int, items: List[int]) -> List[Dict[str, int]]:
|
|
277
|
+
"""
|
|
278
|
+
Remove items from Entities Collection.
|
|
279
|
+
|
|
280
|
+
:param id: Entities Collection ID in Supervisely.
|
|
281
|
+
:type id: int
|
|
282
|
+
:param items: List of item IDs in Supervisely.
|
|
283
|
+
:type items: List[int]
|
|
284
|
+
:Usage example:
|
|
285
|
+
|
|
286
|
+
.. code-block:: python
|
|
287
|
+
|
|
288
|
+
import supervisely as sly
|
|
289
|
+
api = sly.Api.from_env()
|
|
290
|
+
|
|
291
|
+
collection_id = 2
|
|
292
|
+
item_ids = [525, 526, 527]
|
|
293
|
+
removed_items = api.entities_collection.remove_items(collection_id, item_ids)
|
|
294
|
+
# print(removed_items)
|
|
295
|
+
# Output: [{"id": 1, "entityId": 525}, {"id": 2, "entityId": 526}]
|
|
296
|
+
"""
|
|
297
|
+
data = {ApiField.COLLECTION_ID: id, ApiField.ENTITY_IDS: items}
|
|
298
|
+
response = self._api.post("entities-collections.items.bulk.remove", data)
|
|
299
|
+
response = response.json()
|
|
300
|
+
if len(response["missing"]) > 0:
|
|
301
|
+
logger.warning(
|
|
302
|
+
f"Failed to remove items from Entities Collection. IDs: {response['missing']}. "
|
|
303
|
+
)
|
|
304
|
+
return response["items"]
|
supervisely/api/image_api.py
CHANGED
|
@@ -601,6 +601,7 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
601
601
|
only_labelled: Optional[bool] = False,
|
|
602
602
|
fields: Optional[List[str]] = None,
|
|
603
603
|
recursive: Optional[bool] = False,
|
|
604
|
+
entities_collection_id: Optional[int] = None,
|
|
604
605
|
) -> List[ImageInfo]:
|
|
605
606
|
"""
|
|
606
607
|
List of Images in the given :class:`Dataset<supervisely.project.project.Dataset>`.
|
|
@@ -627,6 +628,8 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
627
628
|
:type fields: List[str], optional
|
|
628
629
|
:param recursive: If True, returns all images from dataset recursively (including images in nested datasets).
|
|
629
630
|
:type recursive: bool, optional
|
|
631
|
+
:param entities_collection_id: :class:`EntitiesCollection<supervisely.api.entities_collection_api.EntitiesCollectionApi>` ID to which the images belong. Can be used to filter images by specific entities collection.
|
|
632
|
+
:type entities_collection_id: int, optional
|
|
630
633
|
:return: Objects with image information from Supervisely.
|
|
631
634
|
:rtype: :class:`List[ImageInfo]<ImageInfo>`
|
|
632
635
|
:Usage example:
|
|
@@ -700,6 +703,18 @@ class ImageApi(RemoveableBulkModuleApi):
|
|
|
700
703
|
},
|
|
701
704
|
}
|
|
702
705
|
]
|
|
706
|
+
if entities_collection_id is not None:
|
|
707
|
+
if ApiField.FILTERS not in data:
|
|
708
|
+
data[ApiField.FILTERS] = []
|
|
709
|
+
data[ApiField.FILTERS].append(
|
|
710
|
+
{
|
|
711
|
+
"type": "entities_collection",
|
|
712
|
+
"data": {
|
|
713
|
+
ApiField.COLLECTION_ID: entities_collection_id,
|
|
714
|
+
ApiField.INCLUDE: True,
|
|
715
|
+
},
|
|
716
|
+
}
|
|
717
|
+
)
|
|
703
718
|
if fields is not None:
|
|
704
719
|
data[ApiField.FIELDS] = fields
|
|
705
720
|
return self.get_list_all_pages(
|
|
@@ -88,6 +88,7 @@ class LabelingJobInfo(NamedTuple):
|
|
|
88
88
|
include_images_with_tags: list
|
|
89
89
|
exclude_images_with_tags: list
|
|
90
90
|
entities: list
|
|
91
|
+
priority: int
|
|
91
92
|
|
|
92
93
|
|
|
93
94
|
class LabelingJobApi(RemoveableBulkModuleApi, ModuleWithStatus):
|
|
@@ -179,7 +180,8 @@ class LabelingJobApi(RemoveableBulkModuleApi, ModuleWithStatus):
|
|
|
179
180
|
filter_images_by_tags=[],
|
|
180
181
|
include_images_with_tags=[],
|
|
181
182
|
exclude_images_with_tags=[],
|
|
182
|
-
entities=None
|
|
183
|
+
entities=None,
|
|
184
|
+
priority=2)
|
|
183
185
|
"""
|
|
184
186
|
return [
|
|
185
187
|
ApiField.ID,
|
|
@@ -220,6 +222,7 @@ class LabelingJobApi(RemoveableBulkModuleApi, ModuleWithStatus):
|
|
|
220
222
|
ApiField.INCLUDE_IMAGES_WITH_TAGS,
|
|
221
223
|
ApiField.EXCLUDE_IMAGES_WITH_TAGS,
|
|
222
224
|
ApiField.ENTITIES,
|
|
225
|
+
ApiField.PRIORITY,
|
|
223
226
|
]
|
|
224
227
|
|
|
225
228
|
@staticmethod
|
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
"""create or manipulate already existing labeling queues"""
|
|
3
|
+
|
|
4
|
+
# docs
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Literal, NamedTuple, Optional, Union
|
|
8
|
+
|
|
9
|
+
from supervisely.api.constants import PLAYBACK_RATE_POSSIBLE_VALUES
|
|
10
|
+
from supervisely.api.labeling_job_api import LabelingJobApi
|
|
11
|
+
from supervisely.api.module_api import (
|
|
12
|
+
ApiField,
|
|
13
|
+
ModuleApi,
|
|
14
|
+
ModuleWithStatus,
|
|
15
|
+
RemoveableBulkModuleApi,
|
|
16
|
+
)
|
|
17
|
+
from supervisely.project.project_meta import ProjectMeta
|
|
18
|
+
from supervisely.project.project_type import ProjectType
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LabelingQueueInfo(NamedTuple):
|
|
22
|
+
id: int
|
|
23
|
+
name: str
|
|
24
|
+
team_id: int
|
|
25
|
+
project_id: int
|
|
26
|
+
dataset_id: int
|
|
27
|
+
created_by_id: int
|
|
28
|
+
labelers: list
|
|
29
|
+
reviewers: list
|
|
30
|
+
created_at: str
|
|
31
|
+
finished_at: str
|
|
32
|
+
status: str
|
|
33
|
+
jobs: list
|
|
34
|
+
entities_count: int
|
|
35
|
+
accepted_count: int
|
|
36
|
+
annotated_count: int
|
|
37
|
+
in_progress_count: int
|
|
38
|
+
pending_count: int
|
|
39
|
+
meta: dict
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LabelingQueueApi(RemoveableBulkModuleApi, ModuleWithStatus):
|
|
43
|
+
"""
|
|
44
|
+
API for working with Labeling Queues. :class:`LabelingQueueApi<LabelingQueueApi>` object is immutable.
|
|
45
|
+
|
|
46
|
+
:param api: API connection to the server.
|
|
47
|
+
:type api: Api
|
|
48
|
+
:Usage example:
|
|
49
|
+
|
|
50
|
+
.. code-block:: python
|
|
51
|
+
|
|
52
|
+
import os
|
|
53
|
+
from dotenv import load_dotenv
|
|
54
|
+
|
|
55
|
+
import supervisely as sly
|
|
56
|
+
|
|
57
|
+
# Load secrets and create API object from .env file (recommended)
|
|
58
|
+
# Learn more here: https://developer.supervisely.com/getting-started/basics-of-authentication
|
|
59
|
+
if sly.is_development():
|
|
60
|
+
load_dotenv(os.path.expanduser("~/supervisely.env"))
|
|
61
|
+
api = sly.Api.from_env()
|
|
62
|
+
|
|
63
|
+
# Pass values into the API constructor (optional, not recommended)
|
|
64
|
+
# api = sly.Api(server_address="https://app.supervise.ly", token="4r47N...xaTatb")
|
|
65
|
+
|
|
66
|
+
queue = api.labeling_queues.get_info_by_id(2) # api usage example
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def info_sequence():
|
|
71
|
+
"""
|
|
72
|
+
NamedTuple LabelingQueueInfo information about Labeling Queue.
|
|
73
|
+
|
|
74
|
+
:Example:
|
|
75
|
+
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
|
|
78
|
+
LabelingQueueInfo(
|
|
79
|
+
id=2,
|
|
80
|
+
name='Annotation Queue (#1)',
|
|
81
|
+
team_id=4,
|
|
82
|
+
project_id=58,
|
|
83
|
+
dataset_id=54,
|
|
84
|
+
created_by_id=4,
|
|
85
|
+
labelers=[4],
|
|
86
|
+
reviewers=[4],
|
|
87
|
+
created_at='2020-04-08T15:10:12.618Z',
|
|
88
|
+
finished_at='2020-04-08T15:13:39.788Z',
|
|
89
|
+
status='completed',
|
|
90
|
+
jobs=[283, 282, 281],
|
|
91
|
+
entities_count=3,
|
|
92
|
+
accepted_count=2,
|
|
93
|
+
annotated_count=3,
|
|
94
|
+
in_progress_count=2,
|
|
95
|
+
pending_count=1,
|
|
96
|
+
meta={}
|
|
97
|
+
)
|
|
98
|
+
"""
|
|
99
|
+
return [
|
|
100
|
+
ApiField.ID,
|
|
101
|
+
ApiField.NAME,
|
|
102
|
+
ApiField.TEAM_ID,
|
|
103
|
+
ApiField.PROJECT_ID,
|
|
104
|
+
ApiField.DATASET_ID,
|
|
105
|
+
ApiField.CREATED_BY_ID,
|
|
106
|
+
ApiField.LABELERS,
|
|
107
|
+
ApiField.REVIEWERS,
|
|
108
|
+
ApiField.CREATED_AT,
|
|
109
|
+
ApiField.FINISHED_AT,
|
|
110
|
+
ApiField.STATUS,
|
|
111
|
+
ApiField.JOBS,
|
|
112
|
+
ApiField.ENTITIES_COUNT,
|
|
113
|
+
ApiField.ACCEPTED_COUNT,
|
|
114
|
+
ApiField.ANNOTATED_COUNT,
|
|
115
|
+
ApiField.IN_PROGRESS_COUNT,
|
|
116
|
+
ApiField.PENDING_COUNT,
|
|
117
|
+
ApiField.META,
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def info_tuple_name():
|
|
122
|
+
"""
|
|
123
|
+
NamedTuple name - **LabelingQueueInfo**.
|
|
124
|
+
"""
|
|
125
|
+
return "LabelingQueueInfo"
|
|
126
|
+
|
|
127
|
+
def _convert_json_info(self, info: Dict, skip_missing: Optional[bool] = True):
|
|
128
|
+
""" """
|
|
129
|
+
|
|
130
|
+
def _get_value(dict, field_name, skip_missing):
|
|
131
|
+
if skip_missing is True:
|
|
132
|
+
return dict.get(field_name, None)
|
|
133
|
+
else:
|
|
134
|
+
return dict[field_name]
|
|
135
|
+
|
|
136
|
+
def _get_ids(data, skip_missing):
|
|
137
|
+
if skip_missing is True:
|
|
138
|
+
return [job.get(ApiField.ID, None) for job in data]
|
|
139
|
+
else:
|
|
140
|
+
return [job[ApiField.ID] for job in data]
|
|
141
|
+
|
|
142
|
+
if info is None:
|
|
143
|
+
return None
|
|
144
|
+
else:
|
|
145
|
+
field_values = []
|
|
146
|
+
for field_name in self.info_sequence():
|
|
147
|
+
if type(field_name) is str:
|
|
148
|
+
if field_name in [ApiField.JOBS, ApiField.LABELERS, ApiField.REVIEWERS]:
|
|
149
|
+
field_values.append(_get_ids(info[field_name], skip_missing))
|
|
150
|
+
continue
|
|
151
|
+
else:
|
|
152
|
+
field_values.append(_get_value(info, field_name, skip_missing))
|
|
153
|
+
elif type(field_name) is tuple:
|
|
154
|
+
value = None
|
|
155
|
+
for sub_name in field_name[0]:
|
|
156
|
+
if value is None:
|
|
157
|
+
value = _get_value(info, sub_name, skip_missing)
|
|
158
|
+
else:
|
|
159
|
+
value = _get_value(value, sub_name, skip_missing)
|
|
160
|
+
field_values.append(value)
|
|
161
|
+
else:
|
|
162
|
+
raise RuntimeError("Can not parse field {!r}".format(field_name))
|
|
163
|
+
res = self.InfoType(*field_values)
|
|
164
|
+
return LabelingQueueInfo(**res._asdict())
|
|
165
|
+
|
|
166
|
+
def _check_membership(self, ids: List[int], team_id: int) -> None:
|
|
167
|
+
"""Check if user is a member of the team in which the Labeling Queue is created."""
|
|
168
|
+
for user_id in ids:
|
|
169
|
+
memberships = self._api.user.get_teams(user_id)
|
|
170
|
+
team_ids = [team.id for team in memberships]
|
|
171
|
+
if team_id not in team_ids:
|
|
172
|
+
raise RuntimeError(
|
|
173
|
+
f"User with id {user_id} is not a member of the team with id {team_id}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def create(
|
|
177
|
+
self,
|
|
178
|
+
name: str,
|
|
179
|
+
user_ids: List[int],
|
|
180
|
+
reviewer_ids: List[int],
|
|
181
|
+
dataset_id: Optional[int] = None,
|
|
182
|
+
collection_id: Optional[int] = None,
|
|
183
|
+
readme: Optional[str] = None,
|
|
184
|
+
classes_to_label: Optional[List[str]] = None,
|
|
185
|
+
objects_limit_per_image: Optional[int] = None,
|
|
186
|
+
tags_to_label: Optional[List[str]] = None,
|
|
187
|
+
tags_limit_per_image: Optional[int] = None,
|
|
188
|
+
include_images_with_tags: Optional[List[str]] = None,
|
|
189
|
+
exclude_images_with_tags: Optional[List[str]] = None,
|
|
190
|
+
images_range: Optional[List[int, int]] = None,
|
|
191
|
+
reviewer_id: Optional[int] = None,
|
|
192
|
+
images_ids: Optional[List[int]] = None,
|
|
193
|
+
dynamic_classes: Optional[bool] = False,
|
|
194
|
+
dynamic_tags: Optional[bool] = False,
|
|
195
|
+
disable_confirm: Optional[bool] = None,
|
|
196
|
+
disable_submit: Optional[bool] = None,
|
|
197
|
+
toolbox_settings: Optional[Dict] = None,
|
|
198
|
+
hide_figure_author: Optional[bool] = False,
|
|
199
|
+
allow_review_own_annotations: Optional[bool] = False,
|
|
200
|
+
skip_complete_job_on_empty: Optional[bool] = False,
|
|
201
|
+
) -> int:
|
|
202
|
+
"""
|
|
203
|
+
Creates Labeling Queue and assigns given Users to it.
|
|
204
|
+
|
|
205
|
+
:param name: Labeling Queue name in Supervisely.
|
|
206
|
+
:type name: str
|
|
207
|
+
:param dataset_id: Dataset ID in Supervisely.
|
|
208
|
+
:type dataset_id: int
|
|
209
|
+
:param collection_id: Entities Collection ID in Supervisely.
|
|
210
|
+
:type collection_id: int, optional
|
|
211
|
+
:param user_ids: User IDs in Supervisely to assign Users as labelers to Labeling Queue.
|
|
212
|
+
:type user_ids: List[int]
|
|
213
|
+
:param readme: Additional information about Labeling Queue.
|
|
214
|
+
:type readme: str, optional
|
|
215
|
+
:param description: Description of Labeling Queue.
|
|
216
|
+
:type description: str, optional
|
|
217
|
+
:param classes_to_label: List of classes to label in Dataset.
|
|
218
|
+
:type classes_to_label: List[str], optional
|
|
219
|
+
:param objects_limit_per_image: Limit the number of objects that the labeler can create on each image.
|
|
220
|
+
:type objects_limit_per_image: int, optional
|
|
221
|
+
:param tags_to_label: List of tags to label in Dataset.
|
|
222
|
+
:type tags_to_label: List[str], optional
|
|
223
|
+
:param tags_limit_per_image: Limit the number of tags that the labeler can create on each image.
|
|
224
|
+
:type tags_limit_per_image: int, optional
|
|
225
|
+
:param include_images_with_tags: Include images with given tags for processing by labeler.
|
|
226
|
+
:type include_images_with_tags: List[str], optional
|
|
227
|
+
:param exclude_images_with_tags: Exclude images with given tags for processing by labeler.
|
|
228
|
+
:type exclude_images_with_tags: List[str], optional
|
|
229
|
+
:param images_range: Limit number of images to be labeled for each labeler.
|
|
230
|
+
:type images_range: List[int, int], optional
|
|
231
|
+
:param reviewer_id: User ID in Supervisely to assign User as Reviewer to Labeling Queue.
|
|
232
|
+
:type reviewer_id: int, optional
|
|
233
|
+
:param images_ids: List of images ids to label in dataset
|
|
234
|
+
:type images_ids: List[int], optional
|
|
235
|
+
:param dynamic_classes: If True, classes created after creating the queue will be available for annotators
|
|
236
|
+
:type dynamic_classes: bool, optional
|
|
237
|
+
:param dynamic_tags: If True, tags created after creating the queue will be available for annotators
|
|
238
|
+
:type dynamic_tags: bool, optional
|
|
239
|
+
:param disable_confirm: If True, the Confirm button will be disabled in the labeling tool. It will remain disabled until the next API call sets the parameter to False, re-enabling the button.
|
|
240
|
+
:type disable_confirm: bool, optional
|
|
241
|
+
:param disable_submit: If True, the Submit button will be disabled in the labeling tool. It will remain disabled until the next API call sets the parameter to False, re-enabling the button.
|
|
242
|
+
:type disable_submit: bool, optional
|
|
243
|
+
:param toolbox_settings: Settings for the labeling tool. Only video projects are supported.
|
|
244
|
+
:type toolbox_settings: Dict, optional
|
|
245
|
+
:return: Labeling Queue ID in Supervisely.
|
|
246
|
+
:rtype: int
|
|
247
|
+
:Usage example:
|
|
248
|
+
|
|
249
|
+
.. code-block:: python
|
|
250
|
+
|
|
251
|
+
import supervisely as sly
|
|
252
|
+
|
|
253
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
254
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
255
|
+
api = sly.Api.from_env()
|
|
256
|
+
|
|
257
|
+
user_name = 'alex'
|
|
258
|
+
dataset_id = 602
|
|
259
|
+
new_labeling_queue_id = api.labeling_queue.create(
|
|
260
|
+
user_name,
|
|
261
|
+
dataset_id,
|
|
262
|
+
user_ids=[111, 222],
|
|
263
|
+
readme='Readmy text',
|
|
264
|
+
description='Work for labelers',
|
|
265
|
+
objects_limit_per_image=5,
|
|
266
|
+
tags_limit_per_image=3
|
|
267
|
+
)
|
|
268
|
+
print(new_labeling_queue_id)
|
|
269
|
+
|
|
270
|
+
# >>> 2
|
|
271
|
+
|
|
272
|
+
# Create video labeling job with toolbox settings
|
|
273
|
+
|
|
274
|
+
user_id = 4
|
|
275
|
+
dataset_id = 277
|
|
276
|
+
video_id = 24897
|
|
277
|
+
toolbox_settings = {"playbackRate": 32, "skipFramesSize": 15, "showVideoTime": True}
|
|
278
|
+
|
|
279
|
+
new_labeling_queue_id = api.labeling_queue.create(
|
|
280
|
+
name="Labeling Queue name",
|
|
281
|
+
dataset_id=dataset_id,
|
|
282
|
+
user_ids=[user_id],
|
|
283
|
+
readme="Labeling Queue readme",
|
|
284
|
+
description="Some description",
|
|
285
|
+
classes_to_label=["car", "animal"],
|
|
286
|
+
tags_to_label=["animal_age_group"],
|
|
287
|
+
images_ids=[video_id],
|
|
288
|
+
toolbox_settings=toolbox_settings,
|
|
289
|
+
)
|
|
290
|
+
print(new_labeling_queue_id)
|
|
291
|
+
|
|
292
|
+
# >>> 3
|
|
293
|
+
"""
|
|
294
|
+
if dataset_id is None and collection_id is None:
|
|
295
|
+
raise RuntimeError("Either dataset_id or collection_id must be provided")
|
|
296
|
+
if classes_to_label is None:
|
|
297
|
+
classes_to_label = []
|
|
298
|
+
if tags_to_label is None:
|
|
299
|
+
tags_to_label = []
|
|
300
|
+
|
|
301
|
+
filter_images_by_tags = []
|
|
302
|
+
if include_images_with_tags is not None:
|
|
303
|
+
for tag_name in include_images_with_tags:
|
|
304
|
+
filter_images_by_tags.append({"name": tag_name, "positive": True})
|
|
305
|
+
|
|
306
|
+
if exclude_images_with_tags is not None:
|
|
307
|
+
for tag_name in exclude_images_with_tags:
|
|
308
|
+
filter_images_by_tags.append({"name": tag_name, "positive": False})
|
|
309
|
+
|
|
310
|
+
if objects_limit_per_image is None:
|
|
311
|
+
objects_limit_per_image = 0
|
|
312
|
+
|
|
313
|
+
if tags_limit_per_image is None:
|
|
314
|
+
tags_limit_per_image = 0
|
|
315
|
+
|
|
316
|
+
meta = {
|
|
317
|
+
"classes": classes_to_label,
|
|
318
|
+
"projectTags": tags_to_label,
|
|
319
|
+
"imageTags": filter_images_by_tags,
|
|
320
|
+
"imageFiguresLimit": objects_limit_per_image,
|
|
321
|
+
"imageTagsLimit": tags_limit_per_image,
|
|
322
|
+
"dynamicClasses": dynamic_classes,
|
|
323
|
+
"dynamicTags": dynamic_tags,
|
|
324
|
+
"hideFigureAuthor": hide_figure_author,
|
|
325
|
+
}
|
|
326
|
+
if images_ids is not None:
|
|
327
|
+
meta["entityIds"] = images_ids
|
|
328
|
+
|
|
329
|
+
if toolbox_settings is not None:
|
|
330
|
+
if dataset_id is not None:
|
|
331
|
+
dataset_info = self._api.dataset.get_info_by_id(dataset_id)
|
|
332
|
+
project_id = dataset_info.project_id
|
|
333
|
+
else:
|
|
334
|
+
collection_info = self._api.entities_collection.get_info_by_id(collection_id)
|
|
335
|
+
project_id = collection_info.project_id
|
|
336
|
+
project_info = self._api.project.get_info_by_id(project_id)
|
|
337
|
+
project_type = project_info.type
|
|
338
|
+
if project_type == ProjectType.VIDEOS.value:
|
|
339
|
+
playback_rate = toolbox_settings.get("playbackRate", None)
|
|
340
|
+
if playback_rate is not None:
|
|
341
|
+
if playback_rate not in PLAYBACK_RATE_POSSIBLE_VALUES:
|
|
342
|
+
raise ValueError(
|
|
343
|
+
f"'playbackRate' must be one of: '{','.join(PLAYBACK_RATE_POSSIBLE_VALUES)}'"
|
|
344
|
+
)
|
|
345
|
+
meta["toolboxSettings"] = toolbox_settings
|
|
346
|
+
|
|
347
|
+
if disable_confirm is not None:
|
|
348
|
+
meta.update({"disableConfirm": disable_confirm})
|
|
349
|
+
if disable_submit is not None:
|
|
350
|
+
meta.update({"disableSubmit": disable_submit})
|
|
351
|
+
|
|
352
|
+
queue_meta = {}
|
|
353
|
+
if allow_review_own_annotations is True:
|
|
354
|
+
queue_meta["reviewOwnAnnotationsAvailable"] = True
|
|
355
|
+
|
|
356
|
+
if skip_complete_job_on_empty is True:
|
|
357
|
+
queue_meta["skipCompleteAnnotationJobOnEmpty"] = True
|
|
358
|
+
|
|
359
|
+
data = {
|
|
360
|
+
ApiField.NAME: name,
|
|
361
|
+
ApiField.USER_IDS: user_ids,
|
|
362
|
+
ApiField.REVIEWER_IDS: reviewer_ids,
|
|
363
|
+
ApiField.META: meta,
|
|
364
|
+
}
|
|
365
|
+
if dataset_id is not None:
|
|
366
|
+
data[ApiField.DATASET_ID] = dataset_id
|
|
367
|
+
if collection_id is not None:
|
|
368
|
+
data[ApiField.COLLECTION_ID] = collection_id
|
|
369
|
+
|
|
370
|
+
if len(queue_meta) > 0:
|
|
371
|
+
data[ApiField.QUEUE_META] = queue_meta
|
|
372
|
+
|
|
373
|
+
if readme is not None:
|
|
374
|
+
data[ApiField.README] = str(readme)
|
|
375
|
+
|
|
376
|
+
if images_range is not None and images_range != (None, None):
|
|
377
|
+
if len(images_range) != 2:
|
|
378
|
+
raise RuntimeError("images_range has to contain 2 elements (start, end)")
|
|
379
|
+
images_range = {"start": images_range[0], "end": images_range[1]}
|
|
380
|
+
data[ApiField.META]["range"] = images_range
|
|
381
|
+
|
|
382
|
+
if reviewer_id is not None:
|
|
383
|
+
data[ApiField.REVIEWER_ID] = reviewer_id
|
|
384
|
+
|
|
385
|
+
response = self._api.post("labeling-queues.add", data)
|
|
386
|
+
return response.json()["id"] # {"success": true}
|
|
387
|
+
|
|
388
|
+
def get_list(
|
|
389
|
+
self,
|
|
390
|
+
team_id: int,
|
|
391
|
+
dataset_id: Optional[int] = None,
|
|
392
|
+
project_id: Optional[int] = None,
|
|
393
|
+
ids: Optional[List[int]] = None,
|
|
394
|
+
names: Optional[List[str]] = None,
|
|
395
|
+
show_disabled: Optional[bool] = False,
|
|
396
|
+
) -> List[LabelingQueueInfo]:
|
|
397
|
+
"""
|
|
398
|
+
Get list of information about Labeling Queues in the given Team.
|
|
399
|
+
|
|
400
|
+
:param team_id: Team ID in Supervisely.
|
|
401
|
+
:type team_id: int
|
|
402
|
+
:param dataset_id: Dataset ID in Supervisely.
|
|
403
|
+
:type dataset_id: int, optional
|
|
404
|
+
:param project_id: Project ID in Supervisely.
|
|
405
|
+
:type project_id: int, optional
|
|
406
|
+
:param ids: List of Labeling Queue IDs in Supervisely.
|
|
407
|
+
:type ids: List[int], optional
|
|
408
|
+
:param names: List of Labeling Queue names in Supervisely.
|
|
409
|
+
:type names: List[str], optional
|
|
410
|
+
:param show_disabled: Show disabled Labeling Queues.
|
|
411
|
+
:type show_disabled: bool, optional
|
|
412
|
+
:return: List of information about Labeling Queues. See :class:`info_sequence<info_sequence>`
|
|
413
|
+
:rtype: :class:`List[LabelingQueueInfo]`
|
|
414
|
+
:Usage example:
|
|
415
|
+
|
|
416
|
+
.. code-block:: python
|
|
417
|
+
|
|
418
|
+
import supervisely as sly
|
|
419
|
+
|
|
420
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
421
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
422
|
+
api = sly.Api.from_env()
|
|
423
|
+
|
|
424
|
+
label_jobs = api.labeling_queue.get_list(4)
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
filters = []
|
|
428
|
+
if project_id is not None:
|
|
429
|
+
filters.append({"field": ApiField.PROJECT_ID, "operator": "=", "value": project_id})
|
|
430
|
+
if dataset_id is not None:
|
|
431
|
+
filters.append({"field": ApiField.DATASET_ID, "operator": "=", "value": dataset_id})
|
|
432
|
+
if names is not None:
|
|
433
|
+
filters.append({"field": ApiField.NAME, "operator": "in", "value": names})
|
|
434
|
+
if ids is not None:
|
|
435
|
+
filters.append({"field": ApiField.ID, "operator": "in", "value": ids})
|
|
436
|
+
return self.get_list_all_pages(
|
|
437
|
+
"labeling-queues.list",
|
|
438
|
+
{ApiField.TEAM_ID: team_id, "showDisabled": show_disabled, ApiField.FILTER: filters},
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
def get_info_by_id(self, id: int) -> LabelingQueueInfo:
|
|
442
|
+
"""
|
|
443
|
+
Get Labeling Queue information by ID.
|
|
444
|
+
|
|
445
|
+
:param id: Labeling Queue ID in Supervisely.
|
|
446
|
+
:type id: int
|
|
447
|
+
:return: Information about Labeling Queue. See :class:`info_sequence<info_sequence>`
|
|
448
|
+
:rtype: :class:`LabelingJobInfo`
|
|
449
|
+
:Usage example:
|
|
450
|
+
|
|
451
|
+
.. code-block:: python
|
|
452
|
+
|
|
453
|
+
import supervisely as sly
|
|
454
|
+
|
|
455
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
456
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
457
|
+
api = sly.Api.from_env()
|
|
458
|
+
|
|
459
|
+
label_job_info = api.labeling_queue.get_info_by_id(2)
|
|
460
|
+
print(label_job_info)
|
|
461
|
+
# Output: [
|
|
462
|
+
# 2,
|
|
463
|
+
# "Annotation Queue (#1) (#1) (dataset_01)",
|
|
464
|
+
# "",
|
|
465
|
+
# "",
|
|
466
|
+
# 4,
|
|
467
|
+
# 8,
|
|
468
|
+
# "First Workspace",
|
|
469
|
+
# 58,
|
|
470
|
+
# "tutorial_project",
|
|
471
|
+
# 54,
|
|
472
|
+
# "dataset_01",
|
|
473
|
+
# 4,
|
|
474
|
+
# "anna",
|
|
475
|
+
# 4,
|
|
476
|
+
# "anna",
|
|
477
|
+
# 4,
|
|
478
|
+
# "anna",
|
|
479
|
+
# "2020-04-08T15:10:12.618Z",
|
|
480
|
+
# "2020-04-08T15:10:19.833Z",
|
|
481
|
+
# "2020-04-08T15:13:39.788Z",
|
|
482
|
+
# "completed",
|
|
483
|
+
# false,
|
|
484
|
+
# 3,
|
|
485
|
+
# 0,
|
|
486
|
+
# 1,
|
|
487
|
+
# 2,
|
|
488
|
+
# 2,
|
|
489
|
+
# [],
|
|
490
|
+
# [],
|
|
491
|
+
# [
|
|
492
|
+
# 1,
|
|
493
|
+
# 5
|
|
494
|
+
# ],
|
|
495
|
+
# null,
|
|
496
|
+
# null,
|
|
497
|
+
# [],
|
|
498
|
+
# [],
|
|
499
|
+
# [],
|
|
500
|
+
# [
|
|
501
|
+
# {
|
|
502
|
+
# "reviewStatus": "rejected",
|
|
503
|
+
# "id": 283,
|
|
504
|
+
# "name": "image_03"
|
|
505
|
+
# },
|
|
506
|
+
# {
|
|
507
|
+
# "reviewStatus": "accepted",
|
|
508
|
+
# "id": 282,
|
|
509
|
+
# "name": "image_02"
|
|
510
|
+
# },
|
|
511
|
+
# {
|
|
512
|
+
# "reviewStatus": "accepted",
|
|
513
|
+
# "id": 281,
|
|
514
|
+
# "name": "image_01"
|
|
515
|
+
# }
|
|
516
|
+
# ]
|
|
517
|
+
# ]
|
|
518
|
+
"""
|
|
519
|
+
return self._get_info_by_id(id, "labeling-queues.info")
|
|
520
|
+
|
|
521
|
+
def get_status(self, id: int) -> LabelingJobApi.Status:
|
|
522
|
+
"""
|
|
523
|
+
Get status of Labeling Job with given ID.
|
|
524
|
+
|
|
525
|
+
:param id: Labeling job ID in Supervisely.
|
|
526
|
+
:type id: int
|
|
527
|
+
:return: Labeling Job Status
|
|
528
|
+
:rtype: :class:`Status<supervisely.api.labeling_job_api.LabelingJobApi.Status>`
|
|
529
|
+
:Usage example:
|
|
530
|
+
|
|
531
|
+
.. code-block:: python
|
|
532
|
+
|
|
533
|
+
import supervisely as sly
|
|
534
|
+
|
|
535
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
536
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
537
|
+
api = sly.Api.from_env()
|
|
538
|
+
|
|
539
|
+
job_status = api.labeling_queue.get_status(4)
|
|
540
|
+
print(job_status) # pending
|
|
541
|
+
"""
|
|
542
|
+
status_str = self.get_info_by_id(id).status
|
|
543
|
+
return LabelingJobApi.Status(status_str)
|
|
544
|
+
|
|
545
|
+
def set_status(self, id: int, status: str) -> None:
|
|
546
|
+
"""
|
|
547
|
+
Sets Labeling Queue status.
|
|
548
|
+
|
|
549
|
+
:param id: Labeling Queue ID in Supervisely.
|
|
550
|
+
:type id: int
|
|
551
|
+
:param status: New Labeling Queue status
|
|
552
|
+
:type status: str
|
|
553
|
+
:return: None
|
|
554
|
+
:rtype: :class:`NoneType`
|
|
555
|
+
:Usage example:
|
|
556
|
+
|
|
557
|
+
.. code-block:: python
|
|
558
|
+
|
|
559
|
+
import supervisely as sly
|
|
560
|
+
from supervisely.api.labeling_job_api.LabelingJobApi.Status import COMPLETED
|
|
561
|
+
|
|
562
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
563
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
564
|
+
api = sly.Api.from_env()
|
|
565
|
+
|
|
566
|
+
api.labeling_queue.set_status(id=9, status="completed")
|
|
567
|
+
"""
|
|
568
|
+
self._api.post("labeling-queues.set-status", {ApiField.ID: id, ApiField.STATUS: status})
|
|
569
|
+
|
|
570
|
+
def get_project_meta(self, id: int) -> ProjectMeta:
|
|
571
|
+
"""
|
|
572
|
+
Returns project meta with classes and tags used in the labeling queue with given id.
|
|
573
|
+
|
|
574
|
+
:param id: Labeling Queue ID in Supervisely.
|
|
575
|
+
:type id: int
|
|
576
|
+
:return: Project meta of the labeling queue with given id.
|
|
577
|
+
:rtype: :class:`ProjectMeta`
|
|
578
|
+
"""
|
|
579
|
+
queue_info = self.get_info_by_id(id)
|
|
580
|
+
project_meta_json = self._api.project.get_meta(queue_info.project_id, with_settings=True)
|
|
581
|
+
project_meta = ProjectMeta.from_json(project_meta_json)
|
|
582
|
+
|
|
583
|
+
jobs = [self._api.labeling_job.get_info_by_id(job_id) for job_id in queue_info.jobs]
|
|
584
|
+
job_classes = set()
|
|
585
|
+
job_tags = set()
|
|
586
|
+
for job in jobs:
|
|
587
|
+
job_classes.update(job.classes_to_label)
|
|
588
|
+
job_tags.update(job.tags_to_label)
|
|
589
|
+
|
|
590
|
+
filtered_classes = [
|
|
591
|
+
obj_cls for obj_cls in project_meta.obj_classes if obj_cls.name in job_classes
|
|
592
|
+
]
|
|
593
|
+
filtered_tags = [
|
|
594
|
+
tag_meta for tag_meta in project_meta.tag_metas if tag_meta.name in job_tags
|
|
595
|
+
]
|
|
596
|
+
queue_meta = ProjectMeta(obj_classes=filtered_classes, tag_metas=filtered_tags)
|
|
597
|
+
return queue_meta
|
|
598
|
+
|
|
599
|
+
def get_entities_all_pages(
|
|
600
|
+
self,
|
|
601
|
+
id: int,
|
|
602
|
+
collection_id: Optional[int] = None,
|
|
603
|
+
per_page: int = 25,
|
|
604
|
+
sort: str = "name",
|
|
605
|
+
sort_order: str = "asc",
|
|
606
|
+
status: Optional[Union[List, Literal["none", "done", "accepted", "null"]]] = None,
|
|
607
|
+
limit: int = None,
|
|
608
|
+
filter_by: List[Dict] = None,
|
|
609
|
+
) -> Dict[str, Union[List[Dict], int]]:
|
|
610
|
+
"""
|
|
611
|
+
Get list of all or limited quantity entities from the Supervisely server.
|
|
612
|
+
|
|
613
|
+
:param id: Labeling Queue ID in Supervisely.
|
|
614
|
+
:type id: int
|
|
615
|
+
:param collection_id: Collection ID in Supervisely.
|
|
616
|
+
:type collection_id: int, optional
|
|
617
|
+
:param per_page: Number of entities per page.
|
|
618
|
+
:type per_page: int, optional
|
|
619
|
+
:param sort: Sorting field.
|
|
620
|
+
:type sort: str, optional
|
|
621
|
+
:param sort_order: Sorting order.
|
|
622
|
+
:type sort_order: str, optional
|
|
623
|
+
:param status: Status of entities to filter.
|
|
624
|
+
"null" - pending (in queue).
|
|
625
|
+
"none" - annotating (not in queue),
|
|
626
|
+
"done" - on review,
|
|
627
|
+
"accepted" - accepted,
|
|
628
|
+
:type status: str or List[str], optional
|
|
629
|
+
:param limit: Limit the number of entities to return. If limit is None, all entities will be returned.
|
|
630
|
+
:type limit: int, optional
|
|
631
|
+
:param filter_by: Filter for entities.
|
|
632
|
+
e.g. [{"field": "name", "operator": "in", "value": ["image_01", "image_02"]}]
|
|
633
|
+
- field - field name to filter by ("id", "name", "reviewedAt")
|
|
634
|
+
- operator - operator to use for filtering ("=", ">", "<", ">=", "<=")
|
|
635
|
+
- value - value to filter by
|
|
636
|
+
:type filter_by: List[Dict], optional
|
|
637
|
+
:param return_first_response: Specify if return first response
|
|
638
|
+
:type return_first_response: bool, optional
|
|
639
|
+
"""
|
|
640
|
+
|
|
641
|
+
method = "labeling-queues.stats.entities"
|
|
642
|
+
data = {
|
|
643
|
+
ApiField.ID: id,
|
|
644
|
+
ApiField.PAGE: 1,
|
|
645
|
+
ApiField.PER_PAGE: per_page,
|
|
646
|
+
ApiField.SORT: sort,
|
|
647
|
+
ApiField.SORT_ORDER: sort_order,
|
|
648
|
+
}
|
|
649
|
+
if collection_id is not None:
|
|
650
|
+
data[ApiField.FILTERS] = [
|
|
651
|
+
{
|
|
652
|
+
"type": "entities_collection",
|
|
653
|
+
"data": {ApiField.COLLECTION_ID: collection_id, ApiField.INCLUDE: True},
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
if filter_by is not None:
|
|
657
|
+
data[ApiField.FILTER] = filter_by
|
|
658
|
+
if status is not None:
|
|
659
|
+
if type(status) is str:
|
|
660
|
+
status = [status]
|
|
661
|
+
status = [None if s == "null" else s.lower() for s in status]
|
|
662
|
+
data["entityStatus"] = status
|
|
663
|
+
|
|
664
|
+
first_response = self._api.post(method, data).json()
|
|
665
|
+
total = first_response["total"]
|
|
666
|
+
pages_count = int(total / per_page) + 1 if total % per_page != 0 else int(total / per_page)
|
|
667
|
+
if pages_count in [0, 1]:
|
|
668
|
+
return first_response
|
|
669
|
+
|
|
670
|
+
limit_exceeded = False
|
|
671
|
+
results = first_response["images"]
|
|
672
|
+
if limit is not None and len(results) > limit:
|
|
673
|
+
limit_exceeded = True
|
|
674
|
+
|
|
675
|
+
if (pages_count == 1 and len(results) == total) or limit_exceeded is True:
|
|
676
|
+
pass
|
|
677
|
+
else:
|
|
678
|
+
for page_idx in range(2, pages_count + 1):
|
|
679
|
+
temp_resp = self._api.post(method, {**data, "page": page_idx, "per_page": per_page})
|
|
680
|
+
temp_items = temp_resp.json()["images"]
|
|
681
|
+
results.extend(temp_items)
|
|
682
|
+
if limit is not None and len(results) > limit:
|
|
683
|
+
limit_exceeded = True
|
|
684
|
+
break
|
|
685
|
+
|
|
686
|
+
if len(results) != total and limit is None:
|
|
687
|
+
raise RuntimeError(
|
|
688
|
+
"Method {!r}: error during pagination, some items are missed".format(method)
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
if limit is not None:
|
|
692
|
+
results = results[:limit]
|
|
693
|
+
return {"images": results, "total": total}
|
|
694
|
+
|
|
695
|
+
def get_entities_count_by_status(
|
|
696
|
+
self,
|
|
697
|
+
id: int,
|
|
698
|
+
status: Optional[Union[List, Literal["none", "done", "accepted", "null"]]] = None,
|
|
699
|
+
filter_by: List[Dict] = None,
|
|
700
|
+
) -> int:
|
|
701
|
+
"""
|
|
702
|
+
Get count of entities in the given Labeling Queue with given status.
|
|
703
|
+
:param id: Labeling Queue ID in Supervisely.
|
|
704
|
+
:type id: int
|
|
705
|
+
:param status: Status of entities to filter.
|
|
706
|
+
"null" - pending (in queue).
|
|
707
|
+
"none" - annotating (not in queue),
|
|
708
|
+
"done" - on review,
|
|
709
|
+
"accepted" - accepted,
|
|
710
|
+
:type status: str or List[str], optional
|
|
711
|
+
:param filter_by: Filter for entities.
|
|
712
|
+
e.g. [{"field": "name", "operator": "in", "value": ["image_01", "image_02"]}]
|
|
713
|
+
- field - field name to filter by ("id", "name", "reviewedAt")
|
|
714
|
+
- operator - operator to use for filtering ("=", ">", "<", ">=", "<=")
|
|
715
|
+
- value - value to filter by
|
|
716
|
+
:type filter_by: List[Dict], optional
|
|
717
|
+
:return: Count of entities in the Labeling Queue with given status.
|
|
718
|
+
:rtype: int
|
|
719
|
+
:Usage example:
|
|
720
|
+
|
|
721
|
+
.. code-block:: python
|
|
722
|
+
|
|
723
|
+
import supervisely as sly
|
|
724
|
+
|
|
725
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
726
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
727
|
+
api = sly.Api.from_env()
|
|
728
|
+
|
|
729
|
+
entities_count = api.labeling_queue.get_entities_count_by_status(4, status="none")
|
|
730
|
+
print(entities_count)
|
|
731
|
+
# Output: 3
|
|
732
|
+
"""
|
|
733
|
+
return self.get_entities_all_pages(id, status=status, limit=1, filter_by=filter_by).get(
|
|
734
|
+
"total", 0
|
|
735
|
+
)
|
supervisely/api/module_api.py
CHANGED
|
@@ -637,6 +637,30 @@ class ApiField:
|
|
|
637
637
|
""""""
|
|
638
638
|
SOURCE_BLOB = "sourceBlob"
|
|
639
639
|
""""""
|
|
640
|
+
JOBS = "jobs"
|
|
641
|
+
""""""
|
|
642
|
+
LABELERS = "labelers"
|
|
643
|
+
""""""
|
|
644
|
+
REVIEWERS = "reviewers"
|
|
645
|
+
""""""
|
|
646
|
+
REVIEWER_IDS = "reviewerIds"
|
|
647
|
+
""""""
|
|
648
|
+
ENTITIES_COUNT = "entitiesCount"
|
|
649
|
+
""""""
|
|
650
|
+
ACCEPTED_COUNT = "acceptedCount"
|
|
651
|
+
""""""
|
|
652
|
+
ANNOTATED_COUNT = "annotatedCount"
|
|
653
|
+
""""""
|
|
654
|
+
IN_PROGRESS_COUNT = "inProgressCount"
|
|
655
|
+
""""""
|
|
656
|
+
PENDING_COUNT = "pendingCount"
|
|
657
|
+
""""""
|
|
658
|
+
QUEUE_META = "queueMeta"
|
|
659
|
+
""""""
|
|
660
|
+
ENTITY_IDS = "entityIds"
|
|
661
|
+
""""""
|
|
662
|
+
COLLECTION_ID = "collectionId"
|
|
663
|
+
""""""
|
|
640
664
|
|
|
641
665
|
|
|
642
666
|
def _get_single_item(items):
|
|
@@ -529,6 +529,7 @@ class VolumeFigure(VideoFigure):
|
|
|
529
529
|
else:
|
|
530
530
|
geometry_json = data[ApiField.GEOMETRY]
|
|
531
531
|
geometry = shape.from_json(geometry_json)
|
|
532
|
+
geometry.sly_id = data.get(ID, None)
|
|
532
533
|
|
|
533
534
|
key = uuid.UUID(data[KEY]) if KEY in data else uuid.uuid4()
|
|
534
535
|
|
|
@@ -636,10 +637,15 @@ class VolumeFigure(VideoFigure):
|
|
|
636
637
|
"""
|
|
637
638
|
if isinstance(geometry_data, str):
|
|
638
639
|
mask_3d = Mask3D.create_from_file(geometry_data)
|
|
639
|
-
|
|
640
|
+
elif isinstance(geometry_data, ndarray):
|
|
640
641
|
mask_3d = Mask3D(geometry_data)
|
|
641
|
-
|
|
642
|
+
elif isinstance(geometry_data, bytes):
|
|
642
643
|
mask_3d = Mask3D.from_bytes(geometry_data)
|
|
644
|
+
else:
|
|
645
|
+
raise TypeError(
|
|
646
|
+
f"geometry_data must be str, ndarray, or bytes, but got {type(geometry_data)}"
|
|
647
|
+
)
|
|
648
|
+
|
|
643
649
|
return cls(
|
|
644
650
|
volume_object,
|
|
645
651
|
mask_3d,
|
|
@@ -22,18 +22,20 @@ supervisely/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
22
22
|
supervisely/api/advanced_api.py,sha256=Nd5cCnHFWc3PSUrCtENxTGtDjS37_lCHXsgXvUI3Ti8,2054
|
|
23
23
|
supervisely/api/agent_api.py,sha256=ShWAIlXcWXcyI9fqVuP5GZVCigCMJmjnvdGUfLspD6Y,8890
|
|
24
24
|
supervisely/api/annotation_api.py,sha256=XE3Sr_oQOMraZ9NlWs9ZqUsff1j47XRDh9Fy8rW4ubw,82763
|
|
25
|
-
supervisely/api/api.py,sha256=
|
|
26
|
-
supervisely/api/app_api.py,sha256=
|
|
27
|
-
supervisely/api/constants.py,sha256=
|
|
25
|
+
supervisely/api/api.py,sha256=zAYool7iuceIaCVl_CGflp9KVGwnKf5z6BUagxEZA_w,67695
|
|
26
|
+
supervisely/api/app_api.py,sha256=5lU3EU2NIwcatB6Uj3JUg_y-Qohsm6tfWK6M7MqEYKU,72442
|
|
27
|
+
supervisely/api/constants.py,sha256=WfqIcEpRnU4Mcfb6q0njeRs2VVSoTAJaIyrqBkBjP8I,253
|
|
28
28
|
supervisely/api/dataset_api.py,sha256=c6rEVySSxbyDB5fUCilsc5AKk0pWN_5vVN6OceDbEvc,47918
|
|
29
|
+
supervisely/api/entities_collection_api.py,sha256=lIF10PVgdUOEjcG7dJuxgpN30cnmxw9-UNc6tEmukKo,9957
|
|
29
30
|
supervisely/api/file_api.py,sha256=EX_Cam93QkR5SOOIkIznkzERIr0u7N7GHVGK27iOm20,92952
|
|
30
31
|
supervisely/api/github_api.py,sha256=NIexNjEer9H5rf5sw2LEZd7C1WR-tK4t6IZzsgeAAwQ,623
|
|
31
32
|
supervisely/api/image_annotation_tool_api.py,sha256=YcUo78jRDBJYvIjrd-Y6FJAasLta54nnxhyaGyanovA,5237
|
|
32
|
-
supervisely/api/image_api.py,sha256=
|
|
33
|
+
supervisely/api/image_api.py,sha256=8qs1AD8h1gyoibp9nZQ3b_A1Vid44Q103tmwRC-2hwQ,224419
|
|
33
34
|
supervisely/api/import_storage_api.py,sha256=BDCgmR0Hv6OoiRHLCVPKt3iDxSVlQp1WrnKhAK_Zl84,460
|
|
34
35
|
supervisely/api/issues_api.py,sha256=BqDJXmNoTzwc3xe6_-mA7FDFC5QQ-ahGbXk_HmpkSeQ,17925
|
|
35
|
-
supervisely/api/labeling_job_api.py,sha256=
|
|
36
|
-
supervisely/api/
|
|
36
|
+
supervisely/api/labeling_job_api.py,sha256=5xorizIYJlyBi-XtPiMyFz-COKR0V_KR5zVhd1kutSM,55187
|
|
37
|
+
supervisely/api/labeling_queue_api.py,sha256=svQKbHtt8304FCFw_ZUoAHrCj3Yetj2RTdgTP_xTGks,28424
|
|
38
|
+
supervisely/api/module_api.py,sha256=8lhsLIvZluHCB23MerHGdENWlfsj5gHIVwUVzMAkwgU,45283
|
|
37
39
|
supervisely/api/neural_network_api.py,sha256=ktPVRO4Jeulougio8F0mioJJHwRJcX250Djp1wBoQ9c,7620
|
|
38
40
|
supervisely/api/object_class_api.py,sha256=hBcQuKi9ouoiR7f99eot6vXHvqbzmal3IrHjJxuoFXg,10396
|
|
39
41
|
supervisely/api/plugin_api.py,sha256=TlfrosdRuYG4NUxk92QiQoVaOdztFspPpygyVa3M3zk,5283
|
|
@@ -1067,7 +1069,7 @@ supervisely/volume_annotation/constants.py,sha256=BdFIh56fy7vzLIjt0gH8xP01EIU-qg
|
|
|
1067
1069
|
supervisely/volume_annotation/plane.py,sha256=wyezAcc8tLp38O44CwWY0wjdQxf3VjRdFLWooCrk-Nw,16301
|
|
1068
1070
|
supervisely/volume_annotation/slice.py,sha256=9m3jtUYz4PYKV3rgbeh2ofDebkyg4TomNbkC6BwZ0lA,4635
|
|
1069
1071
|
supervisely/volume_annotation/volume_annotation.py,sha256=T-50ZmfhQDUtoXkSB9ur3LySCp9xZ1AmUT9KqjPy1DA,30179
|
|
1070
|
-
supervisely/volume_annotation/volume_figure.py,sha256=
|
|
1072
|
+
supervisely/volume_annotation/volume_figure.py,sha256=3_X2drnt_zCGBLWPanW8_O-jM-tI9-34rSacUWqTflk,25329
|
|
1071
1073
|
supervisely/volume_annotation/volume_object.py,sha256=rWzOnycoSJ4-CvFgDOP_rPortU4CdcYR26txe5wJHNo,3577
|
|
1072
1074
|
supervisely/volume_annotation/volume_object_collection.py,sha256=Tc4AovntgoFj5hpTLBv7pCQ3eL0BjorOVpOh2nAE_tA,5706
|
|
1073
1075
|
supervisely/volume_annotation/volume_tag.py,sha256=N2eOhAlbRDVVdSVQ83dzg7URDGtb1xHjxL2g9BW6ljU,9488
|
|
@@ -1083,9 +1085,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
1083
1085
|
supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
|
|
1084
1086
|
supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
|
|
1085
1087
|
supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
|
|
1086
|
-
supervisely-6.73.
|
|
1087
|
-
supervisely-6.73.
|
|
1088
|
-
supervisely-6.73.
|
|
1089
|
-
supervisely-6.73.
|
|
1090
|
-
supervisely-6.73.
|
|
1091
|
-
supervisely-6.73.
|
|
1088
|
+
supervisely-6.73.357.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
1089
|
+
supervisely-6.73.357.dist-info/METADATA,sha256=DdNugcuaOhnUNQMeJOHhj7x80PAPBkxnyKQencTMt-Q,33598
|
|
1090
|
+
supervisely-6.73.357.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
1091
|
+
supervisely-6.73.357.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
|
|
1092
|
+
supervisely-6.73.357.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
|
|
1093
|
+
supervisely-6.73.357.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|