supervisely 6.73.419__py3-none-any.whl → 6.73.421__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 +10 -5
- supervisely/api/app_api.py +71 -4
- supervisely/api/module_api.py +4 -0
- supervisely/api/nn/deploy_api.py +15 -9
- supervisely/api/nn/ecosystem_models_api.py +201 -0
- supervisely/api/nn/neural_network_api.py +12 -3
- supervisely/api/project_api.py +35 -6
- supervisely/api/task_api.py +5 -1
- supervisely/app/widgets/__init__.py +8 -1
- supervisely/app/widgets/agent_selector/template.html +1 -0
- supervisely/app/widgets/deploy_model/__init__.py +0 -0
- supervisely/app/widgets/deploy_model/deploy_model.py +729 -0
- supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
- supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
- supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
- supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
- supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +190 -0
- supervisely/app/widgets/experiment_selector/experiment_selector.py +447 -264
- supervisely/app/widgets/fast_table/fast_table.py +402 -74
- supervisely/app/widgets/fast_table/script.js +364 -96
- supervisely/app/widgets/fast_table/style.css +24 -0
- supervisely/app/widgets/fast_table/template.html +43 -3
- supervisely/app/widgets/radio_table/radio_table.py +10 -2
- supervisely/app/widgets/select/select.py +6 -4
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +18 -0
- supervisely/app/widgets/tabs/tabs.py +22 -6
- supervisely/app/widgets/tabs/template.html +5 -1
- supervisely/nn/artifacts/__init__.py +1 -1
- supervisely/nn/artifacts/artifacts.py +10 -2
- supervisely/nn/artifacts/detectron2.py +1 -0
- supervisely/nn/artifacts/hrda.py +1 -0
- supervisely/nn/artifacts/mmclassification.py +20 -0
- supervisely/nn/artifacts/mmdetection.py +5 -3
- supervisely/nn/artifacts/mmsegmentation.py +1 -0
- supervisely/nn/artifacts/ritm.py +1 -0
- supervisely/nn/artifacts/rtdetr.py +1 -0
- supervisely/nn/artifacts/unet.py +1 -0
- supervisely/nn/artifacts/utils.py +3 -0
- supervisely/nn/artifacts/yolov5.py +2 -0
- supervisely/nn/artifacts/yolov8.py +1 -0
- supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
- supervisely/nn/experiments.py +9 -0
- supervisely/nn/inference/gui/serving_gui_template.py +39 -13
- supervisely/nn/inference/inference.py +160 -94
- supervisely/nn/inference/predict_app/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/classes_selector.py +91 -0
- supervisely/nn/inference/predict_app/gui/gui.py +710 -0
- supervisely/nn/inference/predict_app/gui/input_selector.py +165 -0
- supervisely/nn/inference/predict_app/gui/model_selector.py +79 -0
- supervisely/nn/inference/predict_app/gui/output_selector.py +139 -0
- supervisely/nn/inference/predict_app/gui/preview.py +93 -0
- supervisely/nn/inference/predict_app/gui/settings_selector.py +184 -0
- supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
- supervisely/nn/inference/predict_app/gui/utils.py +282 -0
- supervisely/nn/inference/predict_app/predict_app.py +184 -0
- supervisely/nn/inference/uploader.py +9 -5
- supervisely/nn/model/prediction.py +2 -0
- supervisely/nn/model/prediction_session.py +20 -3
- supervisely/nn/training/gui/gui.py +131 -44
- supervisely/nn/training/gui/model_selector.py +8 -6
- supervisely/nn/training/gui/train_val_splits_selector.py +122 -70
- supervisely/nn/training/gui/training_artifacts.py +0 -5
- supervisely/nn/training/train_app.py +161 -44
- supervisely/project/project.py +211 -73
- supervisely/template/experiment/experiment.html.jinja +74 -17
- supervisely/template/experiment/experiment_generator.py +258 -112
- supervisely/template/experiment/header.html.jinja +31 -13
- supervisely/template/experiment/sly-style.css +7 -2
- {supervisely-6.73.419.dist-info → supervisely-6.73.421.dist-info}/METADATA +3 -1
- {supervisely-6.73.419.dist-info → supervisely-6.73.421.dist-info}/RECORD +75 -57
- supervisely/app/widgets/experiment_selector/style.css +0 -27
- supervisely/app/widgets/experiment_selector/template.html +0 -61
- {supervisely-6.73.419.dist-info → supervisely-6.73.421.dist-info}/LICENSE +0 -0
- {supervisely-6.73.419.dist-info → supervisely-6.73.421.dist-info}/WHEEL +0 -0
- {supervisely-6.73.419.dist-info → supervisely-6.73.421.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.419.dist-info → supervisely-6.73.421.dist-info}/top_level.txt +0 -0
|
File without changes
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from supervisely.app.content import DataJson, StateJson
|
|
4
|
+
from supervisely.app.widgets.widget import Widget
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DropdownCheckboxSelector(Widget):
|
|
8
|
+
class Routes:
|
|
9
|
+
VALUE_CHANGED = "value_changed"
|
|
10
|
+
|
|
11
|
+
class Item:
|
|
12
|
+
def __init__(self, id: str, name: str = None, description: str = None):
|
|
13
|
+
self.id = id
|
|
14
|
+
if name is None:
|
|
15
|
+
name = id
|
|
16
|
+
self.name = name
|
|
17
|
+
self.description = description
|
|
18
|
+
|
|
19
|
+
def to_json(self):
|
|
20
|
+
return {
|
|
21
|
+
"id": self.id,
|
|
22
|
+
"name": self.name,
|
|
23
|
+
"description": self.description,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_json(cls, data):
|
|
28
|
+
return cls(
|
|
29
|
+
id=data["id"],
|
|
30
|
+
name=data["name"],
|
|
31
|
+
description=data.get("description", None),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self, items: List[Item], label: str = None, widget_id: str = None, multiple: bool = True
|
|
36
|
+
):
|
|
37
|
+
self._items = items
|
|
38
|
+
self._label = label
|
|
39
|
+
self._multiple = multiple
|
|
40
|
+
self._changes_handled = False
|
|
41
|
+
|
|
42
|
+
super().__init__(widget_id=widget_id, file_path=__file__)
|
|
43
|
+
|
|
44
|
+
def get_json_data(self):
|
|
45
|
+
return {
|
|
46
|
+
"items": [item.to_json() for item in self._items],
|
|
47
|
+
"label": self._label,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def get_json_state(self):
|
|
51
|
+
return {
|
|
52
|
+
"selected": [],
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def set(self, items: List[Item]):
|
|
56
|
+
self._items = items
|
|
57
|
+
DataJson()[self.widget_id]["items"] = [
|
|
58
|
+
item.to_json() for item in self._items
|
|
59
|
+
]
|
|
60
|
+
DataJson().send_changes()
|
|
61
|
+
|
|
62
|
+
def get_selected(self) -> List[Item]:
|
|
63
|
+
selected = StateJson()[self.widget_id].get("selected", [])
|
|
64
|
+
return [item for item in self._items if item.id in selected]
|
|
65
|
+
|
|
66
|
+
def select(self, ids: List[str]):
|
|
67
|
+
selected = [item for item in self._items if item.id in ids]
|
|
68
|
+
StateJson()[self.widget_id]["selected"] = [item.to_json() for item in selected]
|
|
69
|
+
StateJson().send_changes()
|
|
70
|
+
|
|
71
|
+
def value_changed(self, func):
|
|
72
|
+
"""
|
|
73
|
+
Decorator to handle value changes.
|
|
74
|
+
:param func: function to call when value changes
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
route_path = self.get_route_path(self.Routes.VALUE_CHANGED)
|
|
78
|
+
server = self._sly_app.get_server()
|
|
79
|
+
|
|
80
|
+
@server.post(route_path)
|
|
81
|
+
def on_value_changed():
|
|
82
|
+
selected_items = self.get_selected()
|
|
83
|
+
return func(selected_items)
|
|
84
|
+
|
|
85
|
+
self._changes_handled = True
|
|
86
|
+
|
|
87
|
+
return on_value_changed
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<sly-dropdown-checkbox-selection
|
|
2
|
+
:label="data.{{{widget.widget_id}}}.label"
|
|
3
|
+
class="mr5"
|
|
4
|
+
:items="data.{{{widget.widget_id}}}.items"
|
|
5
|
+
:selected="state.{{{widget.widget_id}}}.selected"
|
|
6
|
+
options="{multipleSelection: {{{widget._multiple}}}}"
|
|
7
|
+
{% if widget._changes_handled == true %}
|
|
8
|
+
@update:selected="state.{{{widget.widget_id}}}.selected = $event; post('/{{{widget.widget_id}}}/value_changed')"
|
|
9
|
+
{% else %}
|
|
10
|
+
@update:selected="state.{{{widget.widget_id}}}.selected = $event"
|
|
11
|
+
{% endif %}
|
|
12
|
+
/>
|
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from typing import Dict, List, Tuple
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from supervisely._utils import logger
|
|
6
|
+
from supervisely.api.api import Api
|
|
7
|
+
from supervisely.app.exceptions import show_dialog
|
|
8
|
+
from supervisely.app.widgets import Widget
|
|
9
|
+
from supervisely.app.widgets.container.container import Container
|
|
10
|
+
from supervisely.app.widgets.dropdown_checkbox_selector.dropdown_checkbox_selector import (
|
|
11
|
+
DropdownCheckboxSelector,
|
|
12
|
+
)
|
|
13
|
+
from supervisely.app.widgets.fast_table.fast_table import FastTable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EcosystemModelSelector(Widget):
|
|
17
|
+
class COLUMN:
|
|
18
|
+
MODEL_NAME = "name"
|
|
19
|
+
FRAMEWORK = "framework"
|
|
20
|
+
TASK_TYPE = "task"
|
|
21
|
+
PARAMETERS = "params (M)"
|
|
22
|
+
# TODO: support metrics for different tasks
|
|
23
|
+
MAP = "mAP"
|
|
24
|
+
|
|
25
|
+
COLUMNS = [
|
|
26
|
+
str(COLUMN.MODEL_NAME),
|
|
27
|
+
str(COLUMN.FRAMEWORK),
|
|
28
|
+
str(COLUMN.TASK_TYPE),
|
|
29
|
+
str(COLUMN.PARAMETERS),
|
|
30
|
+
str(COLUMN.MAP),
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
def __init__(self, frameworks: List[str] = None, task_types: List[str] = None, models: List[Dict] = None, api: Api = None, widget_id: str = None):
|
|
34
|
+
if api is None:
|
|
35
|
+
api = Api()
|
|
36
|
+
self.api = api
|
|
37
|
+
self.frameworks = None
|
|
38
|
+
self.task_types = None
|
|
39
|
+
self.models = None
|
|
40
|
+
|
|
41
|
+
self.set(frameworks, task_types, models)
|
|
42
|
+
|
|
43
|
+
self.framework_filter = DropdownCheckboxSelector(items=[], label="Frameworks")
|
|
44
|
+
self.task_filter = DropdownCheckboxSelector(items=[], label="Task Types")
|
|
45
|
+
self.table = FastTable(columns=self.COLUMNS, page_size=10, is_radio=True, header_right_content=Container(widgets=[self.framework_filter, self.task_filter], direction="horizontal"))
|
|
46
|
+
self.refresh_table()
|
|
47
|
+
self.table.set_filter(self._filter_function)
|
|
48
|
+
|
|
49
|
+
@self.framework_filter.value_changed
|
|
50
|
+
def _framework_filter_changed(frameworks: List[DropdownCheckboxSelector.Item]):
|
|
51
|
+
task_types = self.task_filter.get_selected()
|
|
52
|
+
self.table.filter(([f.id for f in frameworks], [t.id for t in task_types]))
|
|
53
|
+
|
|
54
|
+
@self.task_filter.value_changed
|
|
55
|
+
def _task_filter_changed(task_types: List[DropdownCheckboxSelector.Item]):
|
|
56
|
+
frameworks = self.framework_filter.get_selected()
|
|
57
|
+
self.table.filter(([f.id for f in frameworks], [t.id for t in task_types]))
|
|
58
|
+
|
|
59
|
+
super().__init__(widget_id=widget_id, file_path=__file__)
|
|
60
|
+
|
|
61
|
+
def _filter_function(
|
|
62
|
+
self, data: pd.DataFrame, filter_value: Tuple[List[str], List[str]]
|
|
63
|
+
) -> pd.DataFrame:
|
|
64
|
+
try:
|
|
65
|
+
frameworks, task_types = filter_value
|
|
66
|
+
if not frameworks and not task_types:
|
|
67
|
+
return data
|
|
68
|
+
filtered_models = set()
|
|
69
|
+
for idx, model in enumerate(self.models):
|
|
70
|
+
should_add = True
|
|
71
|
+
if frameworks and model["framework"] not in frameworks:
|
|
72
|
+
should_add = False
|
|
73
|
+
if task_types and model["task"] not in task_types:
|
|
74
|
+
should_add = False
|
|
75
|
+
if should_add:
|
|
76
|
+
filtered_models.add(idx)
|
|
77
|
+
|
|
78
|
+
filtered_data = data.iloc[sorted(filtered_models)]
|
|
79
|
+
filtered_data.reset_index(inplace=True, drop=True)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"Error during filtering: {e}", exc_info=True)
|
|
82
|
+
show_dialog(title="Filtering Error", description=str(e), status="error")
|
|
83
|
+
return data
|
|
84
|
+
return filtered_data
|
|
85
|
+
|
|
86
|
+
def _filter_models(self, models: List[Dict], frameworks: List[str], task_types: List[str]) -> List[Dict]:
|
|
87
|
+
if frameworks is None and task_types is None:
|
|
88
|
+
return models
|
|
89
|
+
|
|
90
|
+
filtered_models = []
|
|
91
|
+
for model in models:
|
|
92
|
+
if frameworks and model["framework"] not in frameworks:
|
|
93
|
+
continue
|
|
94
|
+
if task_types and model["model"]["task"] not in task_types:
|
|
95
|
+
continue
|
|
96
|
+
filtered_models.append(model)
|
|
97
|
+
return filtered_models
|
|
98
|
+
|
|
99
|
+
def set(self, frameworks: List[str] = None, task_types: List[str] = None, models: List[Dict] = None):
|
|
100
|
+
self.frameworks = frameworks
|
|
101
|
+
self.task_types = task_types
|
|
102
|
+
if models is None:
|
|
103
|
+
models = self.api.nn.ecosystem_models_api.list_models()
|
|
104
|
+
self.models = models
|
|
105
|
+
self.models = self._filter_models(self.models, self.frameworks, self.task_types)
|
|
106
|
+
|
|
107
|
+
def _map_from_model(self, model: Dict):
|
|
108
|
+
try:
|
|
109
|
+
map = model.get("evaluation", {}).get("metrics", {}).get("mAP", None)
|
|
110
|
+
if map is None:
|
|
111
|
+
return None
|
|
112
|
+
return float(map)
|
|
113
|
+
except:
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
def _params_from_model(self, model: Dict):
|
|
117
|
+
try:
|
|
118
|
+
params = model.get("paramsM", None)
|
|
119
|
+
if params is None:
|
|
120
|
+
return None
|
|
121
|
+
return float(params)
|
|
122
|
+
except:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
def _get_table_data(self, models: List[Dict] = None) -> List[Dict]:
|
|
126
|
+
if models is None:
|
|
127
|
+
models = self.models
|
|
128
|
+
data = [
|
|
129
|
+
{
|
|
130
|
+
self.COLUMN.FRAMEWORK: model_data["framework"],
|
|
131
|
+
self.COLUMN.MODEL_NAME: model_data["name"],
|
|
132
|
+
self.COLUMN.TASK_TYPE: model_data["task"],
|
|
133
|
+
self.COLUMN.PARAMETERS: self._params_from_model(model_data),
|
|
134
|
+
self.COLUMN.MAP: self._map_from_model(model_data),
|
|
135
|
+
}
|
|
136
|
+
for model_data in models
|
|
137
|
+
]
|
|
138
|
+
return data
|
|
139
|
+
|
|
140
|
+
def refresh_table(self):
|
|
141
|
+
data = self._get_table_data()
|
|
142
|
+
df = pd.DataFrame.from_records(data=data, columns=self.COLUMNS)
|
|
143
|
+
self.table.read_pandas(df)
|
|
144
|
+
unique_frameworks = df[self.COLUMN.FRAMEWORK].unique().tolist()
|
|
145
|
+
self.framework_filter.set(items=[DropdownCheckboxSelector.Item(id=f, name=f) for f in unique_frameworks])
|
|
146
|
+
unique_task_types = df[self.COLUMN.TASK_TYPE].unique().tolist()
|
|
147
|
+
self.task_filter.set(items=[DropdownCheckboxSelector.Item(id=t, name=t) for t in unique_task_types])
|
|
148
|
+
|
|
149
|
+
def get_selected(self):
|
|
150
|
+
idx = self.table.get_selected_row().row_index
|
|
151
|
+
return self.models[idx]
|
|
152
|
+
|
|
153
|
+
def select(self, index: int):
|
|
154
|
+
self.table.select_row(index)
|
|
155
|
+
|
|
156
|
+
def select_framework_and_model_name(self, framework: str, model_name: str):
|
|
157
|
+
for idx, model in enumerate(self.models):
|
|
158
|
+
if model["name"] == model_name and model["framework"] == framework:
|
|
159
|
+
self.table.select_row(idx)
|
|
160
|
+
return
|
|
161
|
+
raise ValueError(f"Model with framework `{framework}` and name '{model_name}' not found.")
|
|
162
|
+
|
|
163
|
+
def get_json_data(self):
|
|
164
|
+
return {}
|
|
165
|
+
|
|
166
|
+
def get_json_state(self):
|
|
167
|
+
return {}
|
|
168
|
+
|
|
169
|
+
def to_html(self):
|
|
170
|
+
return self.table.to_html()
|
|
171
|
+
|
|
172
|
+
def disable(self):
|
|
173
|
+
return self.table.disable()
|
|
174
|
+
|
|
175
|
+
def enable(self):
|
|
176
|
+
return self.table.enable()
|
|
177
|
+
|
|
178
|
+
def hide(self):
|
|
179
|
+
return self.table.hide()
|
|
180
|
+
|
|
181
|
+
def show(self):
|
|
182
|
+
return self.table.show()
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def loading(self):
|
|
186
|
+
return self.table.loading
|
|
187
|
+
|
|
188
|
+
@loading.setter
|
|
189
|
+
def loading(self, value: bool):
|
|
190
|
+
self.table.loading = value
|