supervisely 6.73.284__py3-none-any.whl → 6.73.286__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/__init__.py +1 -1
- supervisely/_utils.py +9 -0
- supervisely/api/entity_annotation/figure_api.py +3 -0
- supervisely/api/module_api.py +35 -1
- supervisely/api/video/video_api.py +1 -1
- supervisely/api/video_annotation_tool_api.py +58 -7
- supervisely/app/fastapi/subapp.py +12 -11
- supervisely/nn/inference/cache.py +19 -1
- supervisely/nn/inference/inference.py +23 -0
- supervisely/nn/inference/tracking/base_tracking.py +362 -0
- supervisely/nn/inference/tracking/bbox_tracking.py +179 -129
- supervisely/nn/inference/tracking/mask_tracking.py +420 -329
- supervisely/nn/inference/tracking/point_tracking.py +325 -288
- supervisely/nn/inference/tracking/tracker_interface.py +346 -13
- {supervisely-6.73.284.dist-info → supervisely-6.73.286.dist-info}/METADATA +1 -1
- {supervisely-6.73.284.dist-info → supervisely-6.73.286.dist-info}/RECORD +20 -19
- {supervisely-6.73.284.dist-info → supervisely-6.73.286.dist-info}/LICENSE +0 -0
- {supervisely-6.73.284.dist-info → supervisely-6.73.286.dist-info}/WHEEL +0 -0
- {supervisely-6.73.284.dist-info → supervisely-6.73.286.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.284.dist-info → supervisely-6.73.286.dist-info}/top_level.txt +0 -0
supervisely/__init__.py
CHANGED
|
@@ -311,4 +311,4 @@ except Exception as e:
|
|
|
311
311
|
# If new changes in Supervisely Python SDK require upgrade of the Supervisely instance
|
|
312
312
|
# set a new value for the environment variable MINIMUM_INSTANCE_VERSION_FOR_SDK, otherwise
|
|
313
313
|
# users can face compatibility issues, if the instance version is lower than the SDK version.
|
|
314
|
-
os.environ["MINIMUM_INSTANCE_VERSION_FOR_SDK"] = "6.12.
|
|
314
|
+
os.environ["MINIMUM_INSTANCE_VERSION_FOR_SDK"] = "6.12.23"
|
supervisely/_utils.py
CHANGED
|
@@ -83,6 +83,15 @@ def take_with_default(v, default):
|
|
|
83
83
|
return v if v is not None else default
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def find_value_by_keys(d: Dict, keys: List[str], default=object()):
|
|
87
|
+
for key in keys:
|
|
88
|
+
if key in d:
|
|
89
|
+
return d[key]
|
|
90
|
+
if default is object():
|
|
91
|
+
raise KeyError(f"None of the keys {keys} are in the dictionary.")
|
|
92
|
+
return default
|
|
93
|
+
|
|
94
|
+
|
|
86
95
|
def batched(seq, batch_size=50):
|
|
87
96
|
for i in range(0, len(seq), batch_size):
|
|
88
97
|
yield seq[i : i + batch_size]
|
|
@@ -81,6 +81,9 @@ class FigureInfo(NamedTuple):
|
|
|
81
81
|
if self.geometry_meta is not None:
|
|
82
82
|
return Rectangle(*self.geometry_meta["bbox"], sly_id=self.id)
|
|
83
83
|
|
|
84
|
+
def to_json(self):
|
|
85
|
+
return FigureApi.convert_info_to_json(self)
|
|
86
|
+
|
|
84
87
|
|
|
85
88
|
class FigureApi(RemoveableBulkModuleApi):
|
|
86
89
|
"""
|
supervisely/api/module_api.py
CHANGED
|
@@ -3,7 +3,16 @@ import asyncio
|
|
|
3
3
|
from collections import namedtuple
|
|
4
4
|
from copy import deepcopy
|
|
5
5
|
from math import ceil
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Any,
|
|
9
|
+
AsyncGenerator,
|
|
10
|
+
Dict,
|
|
11
|
+
List,
|
|
12
|
+
NamedTuple,
|
|
13
|
+
Optional,
|
|
14
|
+
Tuple,
|
|
15
|
+
)
|
|
7
16
|
|
|
8
17
|
import requests
|
|
9
18
|
|
|
@@ -602,6 +611,8 @@ class ApiField:
|
|
|
602
611
|
""""""
|
|
603
612
|
WITH_SHARED = "withShared"
|
|
604
613
|
""""""
|
|
614
|
+
USE_DIRECT_PROGRESS_MESSAGES = "useDirectProgressMessages"
|
|
615
|
+
""""""
|
|
605
616
|
EXTRA_FIELDS = "extraFields"
|
|
606
617
|
""""""
|
|
607
618
|
CUSTOM_SORT = "customSort"
|
|
@@ -861,6 +872,29 @@ class ModuleApiBase(_JsonConvertibleModule):
|
|
|
861
872
|
raise RuntimeError("Can not parse field {!r}".format(field_name))
|
|
862
873
|
return self.InfoType(*field_values)
|
|
863
874
|
|
|
875
|
+
@classmethod
|
|
876
|
+
def convert_info_to_json(cls, info: NamedTuple) -> Dict:
|
|
877
|
+
"""_convert_info_to_json"""
|
|
878
|
+
|
|
879
|
+
def _create_nested_dict(keys, value):
|
|
880
|
+
if len(keys) == 1:
|
|
881
|
+
return {keys[0]: value}
|
|
882
|
+
else:
|
|
883
|
+
return {keys[0]: _create_nested_dict(keys[1:], value)}
|
|
884
|
+
|
|
885
|
+
json_info = {}
|
|
886
|
+
for field_name, value in zip(cls.info_sequence(), info):
|
|
887
|
+
if type(field_name) is str:
|
|
888
|
+
json_info[field_name] = value
|
|
889
|
+
elif isinstance(field_name, tuple):
|
|
890
|
+
if len(field_name[0]) == 0:
|
|
891
|
+
json_info[field_name[1]] = value
|
|
892
|
+
else:
|
|
893
|
+
json_info.update(_create_nested_dict(field_name[0], value))
|
|
894
|
+
else:
|
|
895
|
+
raise RuntimeError("Can not parse field {!r}".format(field_name))
|
|
896
|
+
return json_info
|
|
897
|
+
|
|
864
898
|
def _get_response_by_id(self, id, method, id_field, fields=None):
|
|
865
899
|
"""_get_response_by_id"""
|
|
866
900
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict, Optional
|
|
3
|
+
from typing import Any, Dict, Optional, Tuple
|
|
4
4
|
|
|
5
5
|
from supervisely.api.module_api import ApiField, ModuleApiBase
|
|
6
6
|
from supervisely.collection.str_enum import StrEnum
|
|
@@ -21,6 +21,7 @@ class VideoAnnotationToolAction(StrEnum):
|
|
|
21
21
|
""""""
|
|
22
22
|
ENTITIES_SET_INTITY = "entities/setEntity"
|
|
23
23
|
""""""
|
|
24
|
+
DIRECT_TRACKING_PROGRESS = "figures/setDirectTrackingProgress"
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class VideoAnnotationToolApi(ModuleApiBase):
|
|
@@ -51,7 +52,7 @@ class VideoAnnotationToolApi(ModuleApiBase):
|
|
|
51
52
|
VideoAnnotationToolAction.JOBS_ENABLE_CONTROLS,
|
|
52
53
|
{},
|
|
53
54
|
)
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
def disable_submit_button(self, session_id: str) -> Dict[str, Any]:
|
|
56
57
|
"""Disables submit button of the labeling jobs.
|
|
57
58
|
|
|
@@ -65,7 +66,7 @@ class VideoAnnotationToolApi(ModuleApiBase):
|
|
|
65
66
|
VideoAnnotationToolAction.JOBS_DISABLE_SUBMIT,
|
|
66
67
|
{},
|
|
67
68
|
)
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
def enable_submit_button(self, session_id: str) -> Dict[str, Any]:
|
|
70
71
|
"""Enables submit button of the labeling jobs.
|
|
71
72
|
|
|
@@ -79,7 +80,7 @@ class VideoAnnotationToolApi(ModuleApiBase):
|
|
|
79
80
|
VideoAnnotationToolAction.JOBS_ENABLE_SUBMIT,
|
|
80
81
|
{},
|
|
81
82
|
)
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
def disable_confirm_button(self, session_id: str) -> Dict[str, Any]:
|
|
84
85
|
"""Disables confirm button of the labeling jobs.
|
|
85
86
|
|
|
@@ -93,10 +94,10 @@ class VideoAnnotationToolApi(ModuleApiBase):
|
|
|
93
94
|
VideoAnnotationToolAction.JOBS_DISABLE_CONFIRM,
|
|
94
95
|
{},
|
|
95
96
|
)
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
def enable_confirm_button(self, session_id: str) -> Dict[str, Any]:
|
|
98
99
|
"""Enables confirm button of the labeling jobs.
|
|
99
|
-
|
|
100
|
+
|
|
100
101
|
:param session_id: ID of the session in the Video Labeling Tool which confirm button should be enabled.
|
|
101
102
|
:type session_id: str
|
|
102
103
|
:return: Response from API server in JSON format.
|
|
@@ -132,12 +133,62 @@ class VideoAnnotationToolApi(ModuleApiBase):
|
|
|
132
133
|
},
|
|
133
134
|
)
|
|
134
135
|
|
|
136
|
+
def set_direct_tracking_progress(
|
|
137
|
+
self,
|
|
138
|
+
session_id: str,
|
|
139
|
+
video_id: int,
|
|
140
|
+
track_id: str,
|
|
141
|
+
frame_range: Tuple,
|
|
142
|
+
progress_current: int,
|
|
143
|
+
progress_total: int,
|
|
144
|
+
):
|
|
145
|
+
payload = {
|
|
146
|
+
ApiField.TRACK_ID: track_id,
|
|
147
|
+
ApiField.VIDEO_ID: video_id,
|
|
148
|
+
ApiField.FRAME_RANGE: frame_range,
|
|
149
|
+
ApiField.PROGRESS: {
|
|
150
|
+
ApiField.CURRENT: progress_current,
|
|
151
|
+
ApiField.TOTAL: progress_total,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
return self._act(session_id, VideoAnnotationToolAction.DIRECT_TRACKING_PROGRESS, payload)
|
|
155
|
+
|
|
156
|
+
def set_direct_tracking_error(
|
|
157
|
+
self,
|
|
158
|
+
session_id: str,
|
|
159
|
+
video_id: int,
|
|
160
|
+
track_id: str,
|
|
161
|
+
message: str,
|
|
162
|
+
):
|
|
163
|
+
payload = {
|
|
164
|
+
ApiField.TRACK_ID: track_id,
|
|
165
|
+
ApiField.VIDEO_ID: video_id,
|
|
166
|
+
ApiField.TYPE: "error",
|
|
167
|
+
ApiField.ERROR: {ApiField.MESSAGE: message},
|
|
168
|
+
}
|
|
169
|
+
return self._act(session_id, VideoAnnotationToolAction.DIRECT_TRACKING_PROGRESS, payload)
|
|
170
|
+
|
|
171
|
+
def set_direct_tracking_warning(
|
|
172
|
+
self,
|
|
173
|
+
session_id: str,
|
|
174
|
+
video_id: int,
|
|
175
|
+
track_id: str,
|
|
176
|
+
message: str,
|
|
177
|
+
):
|
|
178
|
+
payload = {
|
|
179
|
+
ApiField.TRACK_ID: track_id,
|
|
180
|
+
ApiField.VIDEO_ID: video_id,
|
|
181
|
+
ApiField.TYPE: "warning",
|
|
182
|
+
ApiField.MESSAGE: message,
|
|
183
|
+
}
|
|
184
|
+
return self._act(session_id, VideoAnnotationToolAction.DIRECT_TRACKING_PROGRESS, payload)
|
|
185
|
+
|
|
135
186
|
def _act(self, session_id: int, action: VideoAnnotationToolAction, payload: dict):
|
|
136
187
|
data = {
|
|
137
188
|
ApiField.SESSION_ID: session_id,
|
|
138
189
|
ApiField.ACTION: str(action),
|
|
139
190
|
ApiField.PAYLOAD: payload,
|
|
140
191
|
}
|
|
141
|
-
resp = self._api.post("
|
|
192
|
+
resp = self._api.post("annotation-tool.run-action", data)
|
|
142
193
|
|
|
143
194
|
return resp.json()
|
|
@@ -926,19 +926,20 @@ class Application(metaclass=Singleton):
|
|
|
926
926
|
self.test_client = TestClient(self._fastapi)
|
|
927
927
|
|
|
928
928
|
if not headless:
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
929
|
+
if is_development() and hot_reload:
|
|
930
|
+
templates = Jinja2Templates()
|
|
931
|
+
self.hot_reload = arel.HotReload([])
|
|
932
|
+
self._fastapi.add_websocket_route(
|
|
933
|
+
"/hot-reload", route=self.hot_reload, name="hot-reload"
|
|
934
|
+
)
|
|
935
|
+
self._fastapi.add_event_handler("startup", self.hot_reload.startup)
|
|
936
|
+
self._fastapi.add_event_handler("shutdown", self.hot_reload.shutdown)
|
|
936
937
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
938
|
+
# Setting HOTRELOAD=1 in template context, otherwise the HTML would not have the hot reload script.
|
|
939
|
+
templates.env.globals["HOTRELOAD"] = "1"
|
|
940
|
+
templates.env.globals["hot_reload"] = self.hot_reload
|
|
940
941
|
|
|
941
|
-
|
|
942
|
+
logger.debug("Hot reload is enabled, use app.reload_page() to reload page.")
|
|
942
943
|
|
|
943
944
|
if is_production():
|
|
944
945
|
# to save offline session
|
|
@@ -303,6 +303,22 @@ class InferenceImageCache:
|
|
|
303
303
|
self.get_frame_from_cache(video_id, frame_index) for frame_index in frame_indexes
|
|
304
304
|
]
|
|
305
305
|
|
|
306
|
+
def frames_loader(
|
|
307
|
+
self, api: sly.Api, video_id: int, frame_indexes: List[int]
|
|
308
|
+
) -> Generator[np.ndarray, None, None]:
|
|
309
|
+
if not isinstance(self._cache, PersistentImageTTLCache):
|
|
310
|
+
for frame_index in frame_indexes:
|
|
311
|
+
yield self.download_frame(api, video_id, frame_index)
|
|
312
|
+
return
|
|
313
|
+
self.run_cache_task_manually(api, None, video_id=video_id)
|
|
314
|
+
for i, frame_index in enumerate(frame_indexes):
|
|
315
|
+
if video_id in self._cache:
|
|
316
|
+
break
|
|
317
|
+
yield self.download_frame(api, video_id, frame_index)
|
|
318
|
+
if i < len(frame_indexes):
|
|
319
|
+
for frame in self._read_frames_from_cached_video_iter(video_id, frame_indexes[i:]):
|
|
320
|
+
yield frame
|
|
321
|
+
|
|
306
322
|
def download_frame(self, api: sly.Api, video_id: int, frame_index: int) -> np.ndarray:
|
|
307
323
|
name = self._frame_name(video_id, frame_index)
|
|
308
324
|
self._wait_if_in_queue(name, api.logger)
|
|
@@ -401,7 +417,9 @@ class InferenceImageCache:
|
|
|
401
417
|
"""
|
|
402
418
|
return_images = kwargs.get("return_images", True)
|
|
403
419
|
progress_cb = kwargs.get("progress_cb", None)
|
|
404
|
-
video_info = kwargs.get("video_info",
|
|
420
|
+
video_info = kwargs.get("video_info", None)
|
|
421
|
+
if video_info is None:
|
|
422
|
+
video_info = api.video.get_info_by_id(video_id)
|
|
405
423
|
|
|
406
424
|
self._wait_if_in_queue(video_id, api.logger)
|
|
407
425
|
if not video_id in self._cache:
|
|
@@ -2252,6 +2252,25 @@ class Inference:
|
|
|
2252
2252
|
def is_model_deployed(self):
|
|
2253
2253
|
return self._model_served
|
|
2254
2254
|
|
|
2255
|
+
def schedule_task(self, func, *args, **kwargs):
|
|
2256
|
+
inference_request_uuid = kwargs.get("inference_request_uuid", None)
|
|
2257
|
+
if inference_request_uuid is None:
|
|
2258
|
+
self._executor.submit(func, *args, **kwargs)
|
|
2259
|
+
else:
|
|
2260
|
+
self._on_inference_start(inference_request_uuid)
|
|
2261
|
+
future = self._executor.submit(
|
|
2262
|
+
self._handle_error_in_async,
|
|
2263
|
+
inference_request_uuid,
|
|
2264
|
+
func,
|
|
2265
|
+
*args,
|
|
2266
|
+
**kwargs,
|
|
2267
|
+
)
|
|
2268
|
+
end_callback = partial(
|
|
2269
|
+
self._on_inference_end, inference_request_uuid=inference_request_uuid
|
|
2270
|
+
)
|
|
2271
|
+
future.add_done_callback(end_callback)
|
|
2272
|
+
logger.debug("Scheduled task.", extra={"inference_request_uuid": inference_request_uuid})
|
|
2273
|
+
|
|
2255
2274
|
def serve(self):
|
|
2256
2275
|
if not self._use_gui:
|
|
2257
2276
|
Progress("Deploying model ...", 1)
|
|
@@ -2320,6 +2339,9 @@ class Inference:
|
|
|
2320
2339
|
else:
|
|
2321
2340
|
autostart_func()
|
|
2322
2341
|
|
|
2342
|
+
self.cache.add_cache_endpoint(server)
|
|
2343
|
+
self.cache.add_cache_files_endpoint(server)
|
|
2344
|
+
|
|
2323
2345
|
@server.post(f"/get_session_info")
|
|
2324
2346
|
@self._check_serve_before_call
|
|
2325
2347
|
def get_session_info(response: Response):
|
|
@@ -2632,6 +2654,7 @@ class Inference:
|
|
|
2632
2654
|
|
|
2633
2655
|
# Ger rid of `pending_results` to less response size
|
|
2634
2656
|
inference_request["pending_results"] = []
|
|
2657
|
+
inference_request.pop("lock", None)
|
|
2635
2658
|
return inference_request
|
|
2636
2659
|
|
|
2637
2660
|
@server.post(f"/pop_inference_results")
|